From 38fc89125df66856e2ed2d272e1829d477670047 Mon Sep 17 00:00:00 2001 From: Eric Torrence <eric.torrence@cern.ch> Date: Fri, 11 Nov 2022 05:40:37 +0100 Subject: [PATCH] First commit of LHC information --- .../Reconstruction/scripts/faser_reco.py | 5 + .../FaserAuthentication/CMakeLists.txt | 1 + .../FaserAuthentication/data/dblookup.xml | 5 + LHCData/LHCDataAlgs/CMakeLists.txt | 19 + .../LHCDataAlgs/python/LHCDataAlgConfig.py | 20 + LHCData/LHCDataAlgs/src/LHCDataAlg.cxx | 136 ++++ LHCData/LHCDataAlgs/src/LHCDataAlg.h | 41 + .../src/components/LHCDataAlgs_entries.cxx | 3 + LHCData/LHCDataTools/CMakeLists.txt | 27 + .../LHCDataTools/LHCDataTools/ILHCDataTool.h | 81 ++ .../LHCDataTools/python/LHCDataToolsConfig.py | 19 + LHCData/LHCDataTools/src/LHCDataTool.cxx | 261 +++++++ LHCData/LHCDataTools/src/LHCDataTool.h | 107 +++ .../src/components/LHCDataTools_entries.cxx | 3 + LHCData/LHCDataUtils/CMakeLists.txt | 10 + LHCData/LHCDataUtils/README.md | 1 + LHCData/LHCDataUtils/python/CoolDataReader.py | 181 +++++ .../LHCDataUtils/python/LumiBlobConversion.py | 388 +++++++++ LHCData/LHCDataUtils/python/LumiDBHandler.py | 121 +++ .../LHCDataUtils/scripts/makeLHCFillData.py | 734 ++++++++++++++++++ xAOD/xAODFaserLHC/CMakeLists.txt | 27 + xAOD/xAODFaserLHC/Root/xAODFaserLHCCLIDs.cxx | 8 + .../Root/xAODFaserLHCDataAux_v1.cxx | 45 ++ .../xAODFaserLHC/Root/xAODFaserLHCData_v1.cxx | 78 ++ xAOD/xAODFaserLHC/xAODFaserLHC/FaserLHCData.h | 22 + .../xAODFaserLHC/FaserLHCDataAux.h | 22 + xAOD/xAODFaserLHC/xAODFaserLHC/selection.xml | 13 + .../versions/FaserLHCDataAux_v1.h | 68 ++ .../xAODFaserLHC/versions/FaserLHCData_v1.h | 103 +++ .../xAODFaserLHC/xAODFaserLHCDict.h | 27 + xAOD/xAODFaserLHCAthenaPool/CMakeLists.txt | 14 + .../src/xAODFaserLHCDataAuxCnv.cxx | 5 + .../src/xAODFaserLHCDataAuxCnv.h | 16 + .../src/xAODFaserLHCDataCnv.cxx | 5 + .../src/xAODFaserLHCDataCnv.h | 17 + 35 files changed, 2633 insertions(+) create mode 100644 LHCData/LHCDataAlgs/CMakeLists.txt create mode 100644 LHCData/LHCDataAlgs/python/LHCDataAlgConfig.py create mode 100644 LHCData/LHCDataAlgs/src/LHCDataAlg.cxx create mode 100644 LHCData/LHCDataAlgs/src/LHCDataAlg.h create mode 100644 LHCData/LHCDataAlgs/src/components/LHCDataAlgs_entries.cxx create mode 100644 LHCData/LHCDataTools/CMakeLists.txt create mode 100644 LHCData/LHCDataTools/LHCDataTools/ILHCDataTool.h create mode 100644 LHCData/LHCDataTools/python/LHCDataToolsConfig.py create mode 100644 LHCData/LHCDataTools/src/LHCDataTool.cxx create mode 100644 LHCData/LHCDataTools/src/LHCDataTool.h create mode 100644 LHCData/LHCDataTools/src/components/LHCDataTools_entries.cxx create mode 100644 LHCData/LHCDataUtils/CMakeLists.txt create mode 100644 LHCData/LHCDataUtils/README.md create mode 100644 LHCData/LHCDataUtils/python/CoolDataReader.py create mode 100644 LHCData/LHCDataUtils/python/LumiBlobConversion.py create mode 100644 LHCData/LHCDataUtils/python/LumiDBHandler.py create mode 100755 LHCData/LHCDataUtils/scripts/makeLHCFillData.py create mode 100644 xAOD/xAODFaserLHC/CMakeLists.txt create mode 100644 xAOD/xAODFaserLHC/Root/xAODFaserLHCCLIDs.cxx create mode 100644 xAOD/xAODFaserLHC/Root/xAODFaserLHCDataAux_v1.cxx create mode 100644 xAOD/xAODFaserLHC/Root/xAODFaserLHCData_v1.cxx create mode 100644 xAOD/xAODFaserLHC/xAODFaserLHC/FaserLHCData.h create mode 100644 xAOD/xAODFaserLHC/xAODFaserLHC/FaserLHCDataAux.h create mode 100644 xAOD/xAODFaserLHC/xAODFaserLHC/selection.xml create mode 100644 xAOD/xAODFaserLHC/xAODFaserLHC/versions/FaserLHCDataAux_v1.h create mode 100644 xAOD/xAODFaserLHC/xAODFaserLHC/versions/FaserLHCData_v1.h create mode 100644 xAOD/xAODFaserLHC/xAODFaserLHC/xAODFaserLHCDict.h create mode 100644 xAOD/xAODFaserLHCAthenaPool/CMakeLists.txt create mode 100644 xAOD/xAODFaserLHCAthenaPool/src/xAODFaserLHCDataAuxCnv.cxx create mode 100644 xAOD/xAODFaserLHCAthenaPool/src/xAODFaserLHCDataAuxCnv.h create mode 100644 xAOD/xAODFaserLHCAthenaPool/src/xAODFaserLHCDataCnv.cxx create mode 100644 xAOD/xAODFaserLHCAthenaPool/src/xAODFaserLHCDataCnv.h diff --git a/Control/CalypsoExample/Reconstruction/scripts/faser_reco.py b/Control/CalypsoExample/Reconstruction/scripts/faser_reco.py index 4e233bf4..96752694 100755 --- a/Control/CalypsoExample/Reconstruction/scripts/faser_reco.py +++ b/Control/CalypsoExample/Reconstruction/scripts/faser_reco.py @@ -176,6 +176,9 @@ else: from FaserGeoModel.FaserGeoModelConfig import FaserGeometryCfg acc.merge(FaserGeometryCfg(ConfigFlags)) +from LHCDataAlgs.LHCDataAlgConfig import LHCDataAlgCfg +acc.merge(LHCDataAlgCfg(ConfigFlags)) + # Set up algorithms from WaveRecAlgs.WaveRecAlgsConfig import WaveformReconstructionCfg acc.merge(WaveformReconstructionCfg(ConfigFlags)) @@ -223,6 +226,8 @@ itemList = [ "xAOD::EventInfo#*" , "xAOD::EventAuxInfo#*" , "xAOD::FaserTriggerData#*" , "xAOD::FaserTriggerDataAux#*" + , "xAOD::FaserLHCData#*" + , "xAOD::FaserLHCDataAux#*" , "FaserSiHitCollection#*" # Strip hits, do we want this? , "FaserSCT_RDO_Container#*" , "FaserSCT_SpacePointContainer#*" diff --git a/Database/ConnectionManagement/FaserAuthentication/CMakeLists.txt b/Database/ConnectionManagement/FaserAuthentication/CMakeLists.txt index ecf7b9bb..eb1c72ae 100644 --- a/Database/ConnectionManagement/FaserAuthentication/CMakeLists.txt +++ b/Database/ConnectionManagement/FaserAuthentication/CMakeLists.txt @@ -39,6 +39,7 @@ set( FaserAuthentication_home # Apparently not needed, because we don't have it... #atlas_install_xmls( ${FaserAuthentication_home}/authentication.xml ) + # Configure the environment setup module: configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/FaserAuthenticationEnvironmentConfig.cmake.in diff --git a/Database/ConnectionManagement/FaserAuthentication/data/dblookup.xml b/Database/ConnectionManagement/FaserAuthentication/data/dblookup.xml index 958fb2ba..9913f4be 100644 --- a/Database/ConnectionManagement/FaserAuthentication/data/dblookup.xml +++ b/Database/ConnectionManagement/FaserAuthentication/data/dblookup.xml @@ -36,4 +36,9 @@ <service name="sqlite_file:///cvmfs/faser.cern.ch/repo/sw/database/DBRelease/current/sqlite200/ALLP200.db" accessMode="read" /> </logicalservice> +<logicalservice name="COOLOFL_LHC"> + <service name="sqlite_file:data/sqlite200/ALLP200.db" accessMode="read" /> + <service name="sqlite_file:///cvmfs/faser.cern.ch/repo/sw/database/DBRelease/current/sqlite200/ALLP200.db" accessMode="read" /> +</logicalservice> + </servicelist> diff --git a/LHCData/LHCDataAlgs/CMakeLists.txt b/LHCData/LHCDataAlgs/CMakeLists.txt new file mode 100644 index 00000000..17abef2b --- /dev/null +++ b/LHCData/LHCDataAlgs/CMakeLists.txt @@ -0,0 +1,19 @@ +############################################################################### +# Package: LHCDataAlgs +################################################################################ + +# Declare the package name: +atlas_subdir( LHCDataAlgs ) + +# External dependencies: +find_package( Boost COMPONENTS filesystem thread system ) + +# Component(s) in the package: +atlas_add_component( LHCDataAlgs + src/*.h + src/*.cxx + src/components/*.cxx + LINK_LIBRARIES AthenaBaseComps StoreGateLib xAODFaserLHC LHCDataToolsLib ) + +# Install files from the package: +atlas_install_python_modules( python/*.py ) diff --git a/LHCData/LHCDataAlgs/python/LHCDataAlgConfig.py b/LHCData/LHCDataAlgs/python/LHCDataAlgConfig.py new file mode 100644 index 00000000..195fbbd1 --- /dev/null +++ b/LHCData/LHCDataAlgs/python/LHCDataAlgConfig.py @@ -0,0 +1,20 @@ +# Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration +from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator +from AthenaConfiguration.ComponentFactory import CompFactory + +from LHCDataTools.LHCDataToolsConfig import LHCDataCfg + +#LHCDataTool = CompFactory.LHCDataTool + +def LHCDataAlgCfg(flags, name="LHCDataAlg", **kwargs): + + acc = ComponentAccumulator() + + tool = CompFactory.LHCDataTool(name="LHCDataTool") + alg = CompFactory.LHCDataAlg(name, **kwargs) + alg.LHCDataTool = tool + + acc.addEventAlgo(alg) + acc.merge(LHCDataCfg(flags)) + + return acc diff --git a/LHCData/LHCDataAlgs/src/LHCDataAlg.cxx b/LHCData/LHCDataAlgs/src/LHCDataAlg.cxx new file mode 100644 index 00000000..9463ab87 --- /dev/null +++ b/LHCData/LHCDataAlgs/src/LHCDataAlg.cxx @@ -0,0 +1,136 @@ +#include "LHCDataAlg.h" + +#include "xAODFaserLHC/FaserLHCDataAux.h" + +LHCDataAlg::LHCDataAlg(const std::string& name, ISvcLocator* pSvcLocator) + : AthReentrantAlgorithm(name, pSvcLocator) { +} + +StatusCode +LHCDataAlg::initialize() { + ATH_MSG_INFO(name() << "::initalize()" ); + + // Initalize tools + ATH_CHECK( m_lhcTool.retrieve() ); + + // Read handle + ATH_CHECK( m_eventInfo.initialize() ); + + // Write handle + ATH_CHECK( m_lhcDataKey.initialize() ); + + return StatusCode::SUCCESS; +} + +StatusCode +LHCDataAlg::finalize() { + return StatusCode::SUCCESS; +} + +StatusCode +LHCDataAlg::execute(const EventContext& ctx) const { + + ATH_MSG_DEBUG("Executing"); + + SG::WriteHandle<xAOD::FaserLHCData> lhcDataHandle(m_lhcDataKey, ctx); + ATH_CHECK( lhcDataHandle.record( std::make_unique<xAOD::FaserLHCData>(), + std::make_unique<xAOD::FaserLHCDataAux>() ) ); + + ATH_MSG_DEBUG("FaserLHCData " << lhcDataHandle.name() << " initialized"); + + lhcDataHandle->set_fillNumber(m_lhcTool->getFillNumber(ctx)); + lhcDataHandle->set_machineMode(m_lhcTool->getMachineMode(ctx)); + lhcDataHandle->set_injectionScheme(m_lhcTool->getInjectionScheme(ctx)); + lhcDataHandle->set_beamType1(m_lhcTool->getBeamType1(ctx)); + lhcDataHandle->set_beamType2(m_lhcTool->getBeamType2(ctx)); + + lhcDataHandle->set_numBunchBeam1(m_lhcTool->getNumBunchBeam1(ctx)); + lhcDataHandle->set_numBunchBeam2(m_lhcTool->getNumBunchBeam2(ctx)); + lhcDataHandle->set_numBunchColliding(m_lhcTool->getNumBunchColl(ctx)); + + lhcDataHandle->set_beamMode(m_lhcTool->getBeamMode(ctx)); + lhcDataHandle->set_betaStar(m_lhcTool->getBetaStar(ctx)); + lhcDataHandle->set_crossingAngle(m_lhcTool->getCrossingAngle(ctx)); + + //lhcDataHandle->set_beam1Bunches(m_lhcTool->getBeam1Bunches(ctx)); + //lhcDataHandle->set_beam2Bunches(m_lhcTool->getBeam2Bunches(ctx)); + //lhcDataHandle->set_luminousBunches(m_lhcTool->getLuminousBunches(ctx)); + + // Fill BCID information + + // Get the BCID mask + std::vector<unsigned char> bcid_mask = m_lhcTool->getBCIDMasks(ctx); + + // Get the event bcid value + SG::ReadHandle<xAOD::EventInfo> xevt(m_eventInfo, ctx); + unsigned int bcid = xevt->bcid(); + + int nearest = findDistance(bcid, bcid_mask, 3); // Colliding beams + lhcDataHandle->set_distanceToCollidingBCID(nearest); + ATH_MSG_DEBUG("Found distance of " << nearest << " from BCID " << bcid + << " to the nearest colliding BCID "); + + nearest = findDistance(bcid, bcid_mask, 1); // Beam1 unpaired + lhcDataHandle->set_distanceToUnpairedB1(nearest); + ATH_MSG_DEBUG("Found distance of " << nearest << " from BCID " << bcid + << " to the nearest unpaired B1 "); + + nearest = findDistance(bcid, bcid_mask, 2); // Beam2 unpaired + lhcDataHandle->set_distanceToUnpairedB2(nearest); + ATH_MSG_DEBUG("Found distance of " << nearest << " from BCID " << bcid + << " to the nearest unpaired B2 "); + + return StatusCode::SUCCESS; +} + +// Function to find distance to nearest BCID +// mask indicates the condition: 1 - unpaired B1, 2 - unpaired B2, 3 - colliding +int +LHCDataAlg::findDistance(unsigned int bcid, const std::vector<unsigned char>& bcid_mask, unsigned char mask) const { + + // Does the BCID make sense? + if ((bcid > 3564) || (bcid <= 0)) { + ATH_MSG_WARNING("Requested distance from invalid BCID " << bcid << "!"); + return -3565; + } + + unsigned int test_bcid; + + // Move outwards from the bcid (starting at offset of 0) + for (unsigned int i=0; i < 3564/2; i++) { + + // Try positive offsets + test_bcid = bcid + i; + + // Wrap around + if (test_bcid >= 3564) test_bcid -= 3564; + + // BCID 0 doesn't exist in the real machine + // BCID 3564 doesn't exist in our array (but will always be empty in real machine) + // So avoid these pathologies + if (test_bcid != 0) + if (mask == bcid_mask[test_bcid]) return i; + + if(i == 0) continue; // No need to check -0 + + // Try negative offsets + test_bcid = bcid - i; + + // Wrap around + if (test_bcid < 1) test_bcid += 3564; + + // BCID 0 doesn't exist in the real machine + // BCID 3564 doesn't exist in our array (but will always be empty) + // So avoid these pathologies + if (test_bcid != 3564) + if (mask == bcid_mask[test_bcid]) return -i; + + } + + // If we got to here, there was no match + // Does the BCID make sense? + ATH_MSG_WARNING("Couldn't find distance from BCID " << bcid << " and pattern " << mask << "!"); + ATH_MSG_WARNING(bcid_mask); + return -3565; + +} diff --git a/LHCData/LHCDataAlgs/src/LHCDataAlg.h b/LHCData/LHCDataAlgs/src/LHCDataAlg.h new file mode 100644 index 00000000..f53257bc --- /dev/null +++ b/LHCData/LHCDataAlgs/src/LHCDataAlg.h @@ -0,0 +1,41 @@ +#ifndef LHCDATAALG_H +#define LHCDATAALG_H + +#include "AthenaBaseComps/AthReentrantAlgorithm.h" + +#include "StoreGate/ReadCondHandleKey.h" +#include "StoreGate/WriteCondHandleKey.h" + +#include "GaudiKernel/ToolHandle.h" +#include "GaudiKernel/ICondSvc.h" +#include "GaudiKernel/ServiceHandle.h" + +#include "xAODFaserLHC/FaserLHCData.h" +#include "xAODEventInfo/EventInfo.h" + +#include "LHCDataTools/ILHCDataTool.h" + +class LHCDataAlg : public AthReentrantAlgorithm { + public: + LHCDataAlg(const std::string& name, ISvcLocator* pSvcLocator); + virtual ~LHCDataAlg() = default; + + virtual StatusCode initialize() override; + virtual StatusCode execute(const EventContext& ctx) const override; + virtual StatusCode finalize() override; + virtual bool isClonable() const override { return true; }; + + private: + ToolHandle<ILHCDataTool> m_lhcTool{this, "LHCDataTool", "LHCDataTool"}; + SG::ReadHandleKey<xAOD::EventInfo> m_eventInfo{ this, "EventInfoKey", "EventInfo", "ReadHandleKey for xAOD::EventInfo"}; + SG::WriteHandleKey<xAOD::FaserLHCData> m_lhcDataKey + {this, "FaserLHCDataKey", "FaserLHCData"}; + + // Utility function to find nearest BCID + int findDistance(unsigned int bcid, const std::vector<unsigned char>& bcid_mask, + unsigned char mask) const; + + //ServiceHandle<ICondSvc> m_condSvc{this, "CondSvc", "CondSvc"}; +}; + +#endif // LHCDATAALG_H diff --git a/LHCData/LHCDataAlgs/src/components/LHCDataAlgs_entries.cxx b/LHCData/LHCDataAlgs/src/components/LHCDataAlgs_entries.cxx new file mode 100644 index 00000000..1620e8dc --- /dev/null +++ b/LHCData/LHCDataAlgs/src/components/LHCDataAlgs_entries.cxx @@ -0,0 +1,3 @@ +#include "../LHCDataAlg.h" + +DECLARE_COMPONENT( LHCDataAlg ) diff --git a/LHCData/LHCDataTools/CMakeLists.txt b/LHCData/LHCDataTools/CMakeLists.txt new file mode 100644 index 00000000..248abc91 --- /dev/null +++ b/LHCData/LHCDataTools/CMakeLists.txt @@ -0,0 +1,27 @@ +############################################################################### +# Package: LHCDataTools +################################################################################ + +# Declare the package name: +atlas_subdir( LHCDataTools ) + +# External dependencies: +find_package( CLHEP ) +find_package( ROOT COMPONENTS Core Tree MathCore Hist RIO pthread ) + +# Component(s) in the package: +atlas_add_component ( LHCDataTools + src/components/*.cxx + INCLUDE_DIRS ${ROOT_INCLUDE_DIRS} ${CLHEP_INCLUDE_DIRS} + LINK_LIBRARIES ${ROOT_LIBRARIES} ${CLHEP_LIBRARIES} AthenaKernel LHCDataToolsLib GaudiKernel AthenaBaseComps AthenaPoolUtilities StoreGateLib xAODEventInfo ) + + +atlas_add_library( LHCDataToolsLib + src/*.cxx + PUBLIC_HEADERS LHCDataTools + INCLUDE_DIRS ${ROOT_INCLUDE_DIRS} ${CLHEP_INCLUDE_DIRS} + LINK_LIBRARIES ${ROOT_LIBRARIES} ${CLHEP_LIBRARIES} AthenaKernel GaudiKernel AthenaBaseComps AthenaPoolUtilities StoreGateLib xAODEventInfo ) + +# Install files from the package: +atlas_install_python_modules( python/*.py ) + diff --git a/LHCData/LHCDataTools/LHCDataTools/ILHCDataTool.h b/LHCData/LHCDataTools/LHCDataTools/ILHCDataTool.h new file mode 100644 index 00000000..cf6abd17 --- /dev/null +++ b/LHCData/LHCDataTools/LHCDataTools/ILHCDataTool.h @@ -0,0 +1,81 @@ +/* + Copyright (C) 2002-2019 CERN for the benefit of the ATLAS and FAsER collaborations +*/ + +/** @file ILHCDataTool.h Interface file for LHCDataTool. + */ + +// Multiple inclusion protection +#ifndef ILHCDATATOOL +#define ILHCDATATOOL + +//STL includes +#include <map> + +//Gaudi Includes +#include "GaudiKernel/IAlgTool.h" +#include "GaudiKernel/EventContext.h" + +class ILHCDataTool: virtual public IAlgTool { + + public: + + //----------Public Member Functions----------// + // Structors + virtual ~ILHCDataTool() = default; //!< Destructor + + /// Creates the InterfaceID and interfaceID() method + DeclareInterfaceID(ILHCDataTool, 1, 0); + + // Methods to return fill data + virtual int getFillNumber(const EventContext& ctx) const = 0; + virtual int getFillNumber(void) const = 0; + + virtual std::string getMachineMode(const EventContext& ctx) const = 0; + virtual std::string getMachineMode(void) const = 0; + + virtual std::string getInjectionScheme(const EventContext& ctx) const = 0; + virtual std::string getInjectionScheme(void) const = 0; + + virtual int getBeamType1(const EventContext& ctx) const = 0; + virtual int getBeamType1(void) const = 0; + + virtual int getBeamType2(const EventContext& ctx) const = 0; + virtual int getBeamType2(void) const = 0; + + virtual unsigned int getNumBunchBeam1(const EventContext& ctx) const = 0; + virtual unsigned int getNumBunchBeam1(void) const = 0; + + virtual unsigned int getNumBunchBeam2(const EventContext& ctx) const = 0; + virtual unsigned int getNumBunchBeam2(void) const = 0; + + virtual unsigned int getNumBunchColl(const EventContext& ctx) const = 0; + virtual unsigned int getNumBunchColl(void) const = 0; + + // Methods to return beam data + virtual std::string getBeamMode(const EventContext& ctx) const = 0; + virtual std::string getBeamMode(void) const = 0; + + virtual float getBetaStar(const EventContext& ctx) const = 0; + virtual float getBetaStar(void) const = 0; + + virtual float getCrossingAngle(const EventContext& ctx) const = 0; + virtual float getCrossingAngle(void) const = 0; + + // Methods to return BCID data + virtual unsigned int getBeam1Bunches(const EventContext& ctx) const = 0; + virtual unsigned int getBeam1Bunches(void) const = 0; + + virtual unsigned int getBeam2Bunches(const EventContext& ctx) const = 0; + virtual unsigned int getBeam2Bunches(void) const = 0; + + virtual unsigned int getLuminousBunches(const EventContext& ctx) const = 0; + virtual unsigned int getLuminousBunches(void) const = 0; + + virtual std::vector<unsigned char> getBCIDMasks(const EventContext& ctx) const = 0; + virtual std::vector<unsigned char> getBCIDMasks(void) const = 0; + +}; + +//---------------------------------------------------------------------- +#endif // LHCDATATOOL diff --git a/LHCData/LHCDataTools/python/LHCDataToolsConfig.py b/LHCData/LHCDataTools/python/LHCDataToolsConfig.py new file mode 100644 index 00000000..cdd2d267 --- /dev/null +++ b/LHCData/LHCDataTools/python/LHCDataToolsConfig.py @@ -0,0 +1,19 @@ +# Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration +from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator +from AthenaConfiguration.ComponentFactory import CompFactory +from IOVDbSvc.IOVDbSvcConfig import addFolders + +def LHCDataCfg(flags, **kwargs): + acc = ComponentAccumulator() + dbInstance = kwargs.get("dbInstance", "COOLOFL_LHC") + dbName = flags.IOVDb.DatabaseInstance # e.g. CONDBR3 + + #acc.merge(addFolders(flags, folder_list, dbInstance, className="AthenaAttributeList")) + # COOLOFL_LHC is not known to ATLAS IOVDBSvc + # Must use non-shorthand folder specification here + folder_list = ["/LHC/FillData", "/LHC/BeamData", "/LHC/BCIDData"] + for folder_name in folder_list: + folder_string = f"<db>{dbInstance}/{dbName}</db> {folder_name}" + acc.merge(addFolders(flags, folder_string, className="AthenaAttributeList")) + return acc + diff --git a/LHCData/LHCDataTools/src/LHCDataTool.cxx b/LHCData/LHCDataTools/src/LHCDataTool.cxx new file mode 100644 index 00000000..64a2d536 --- /dev/null +++ b/LHCData/LHCDataTools/src/LHCDataTool.cxx @@ -0,0 +1,261 @@ +/* + Copyright (C) 2002-2019 CERN for the benefit of the ATLAS and FASER collaborations +*/ + +/** @file LHCDataTool.cxx Implementation file for LHCDataTool. + @author Eric Torrence (05/02/22) +*/ + +#include "LHCDataTool.h" +#include "CoralBase/Blob.h" + +//---------------------------------------------------------------------- +LHCDataTool::LHCDataTool (const std::string& type, const std::string& name, const IInterface* parent) : + base_class(type, name, parent) +{ +} + +//---------------------------------------------------------------------- +StatusCode +LHCDataTool::initialize() { + + ATH_MSG_DEBUG("LHCDataTool::initialize()"); + + // Read Handles + ATH_CHECK(m_fillDataKey.initialize()); + ATH_CHECK(m_beamDataKey.initialize()); + ATH_CHECK(m_bcidDataKey.initialize()); + + return StatusCode::SUCCESS; +} + +//---------------------------------------------------------------------- +StatusCode +LHCDataTool::finalize() { + // Print where you are + return StatusCode::SUCCESS; +} + +//---------------------------------------------------------------------- +int +LHCDataTool::getFillNumber(const EventContext& ctx) const { + // Read Cond Handle + SG::ReadCondHandle<AthenaAttributeList> readHandle{m_fillDataKey, ctx}; + return (**readHandle)["FillNumber"].data<int>(); +} + +int +LHCDataTool::getFillNumber(void) const { + const EventContext& ctx{Gaudi::Hive::currentContext()}; + return getFillNumber(ctx); +} + +//---------------------------------------------------------------------- +std::string +LHCDataTool::getMachineMode(const EventContext& ctx) const { + // Read Cond Handle + SG::ReadCondHandle<AthenaAttributeList> readHandle{m_fillDataKey, ctx}; + return (**readHandle)["MachineMode"].data<std::string>(); +} + +std::string +LHCDataTool::getMachineMode(void) const { + const EventContext& ctx{Gaudi::Hive::currentContext()}; + return getMachineMode(ctx); +} + +//---------------------------------------------------------------------- +std::string +LHCDataTool::getInjectionScheme(const EventContext& ctx) const { + // Read Cond Handle + SG::ReadCondHandle<AthenaAttributeList> readHandle{m_fillDataKey, ctx}; + return (**readHandle)["InjectionScheme"].data<std::string>(); +} + +std::string +LHCDataTool::getInjectionScheme(void) const { + const EventContext& ctx{Gaudi::Hive::currentContext()}; + return getInjectionScheme(ctx); +} + +//---------------------------------------------------------------------- +int +LHCDataTool::getBeamType1(const EventContext& ctx) const { + // Read Cond Handle + SG::ReadCondHandle<AthenaAttributeList> readHandle{m_fillDataKey, ctx}; + return (**readHandle)["BeamType1"].data<int>(); +} + +int +LHCDataTool::getBeamType1(void) const { + const EventContext& ctx{Gaudi::Hive::currentContext()}; + return getBeamType1(ctx); +} + +//---------------------------------------------------------------------- +int +LHCDataTool::getBeamType2(const EventContext& ctx) const { + // Read Cond Handle + SG::ReadCondHandle<AthenaAttributeList> readHandle{m_fillDataKey, ctx}; + return (**readHandle)["BeamType2"].data<int>(); +} + +int +LHCDataTool::getBeamType2(void) const { + const EventContext& ctx{Gaudi::Hive::currentContext()}; + return getBeamType2(ctx); +} + +//---------------------------------------------------------------------- +unsigned int +LHCDataTool::getNumBunchBeam1(const EventContext& ctx) const { + SG::ReadCondHandle<AthenaAttributeList> readHandle{m_fillDataKey, ctx}; + return(**readHandle)["NumBunchBeam1"].data<unsigned int>(); +} + +unsigned int +LHCDataTool::getNumBunchBeam1(void) const { + const EventContext& ctx{Gaudi::Hive::currentContext()}; + return getNumBunchBeam1(ctx); +} + +unsigned int +LHCDataTool::getNumBunchBeam2(const EventContext& ctx) const { + SG::ReadCondHandle<AthenaAttributeList> readHandle{m_fillDataKey, ctx}; + return(**readHandle)["NumBunchBeam2"].data<unsigned int>(); +} + +unsigned int +LHCDataTool::getNumBunchBeam2(void) const { + const EventContext& ctx{Gaudi::Hive::currentContext()}; + return getNumBunchBeam2(ctx); +} + +unsigned int +LHCDataTool::getNumBunchColl(const EventContext& ctx) const { + SG::ReadCondHandle<AthenaAttributeList> readHandle{m_fillDataKey, ctx}; + return(**readHandle)["NumBunchColl"].data<unsigned int>(); +} + +unsigned int +LHCDataTool::getNumBunchColl(void) const { + const EventContext& ctx{Gaudi::Hive::currentContext()}; + return getNumBunchColl(ctx); +} + +//---------------------------------------------------------------------- +std::string +LHCDataTool::getBeamMode(const EventContext& ctx) const { + SG::ReadCondHandle<AthenaAttributeList> readHandle{m_beamDataKey, ctx}; + return(**readHandle)["BeamMode"].data<std::string>(); +} + +std::string +LHCDataTool::getBeamMode(void) const { + const EventContext& ctx{Gaudi::Hive::currentContext()}; + return getBeamMode(ctx); +} + +//---------------------------------------------------------------------- +float +LHCDataTool::getBetaStar(const EventContext& ctx) const { + SG::ReadCondHandle<AthenaAttributeList> readHandle{m_beamDataKey, ctx}; + return(**readHandle)["BetaStar"].data<float>(); +} + +float +LHCDataTool::getBetaStar(void) const { + const EventContext& ctx{Gaudi::Hive::currentContext()}; + return getBetaStar(ctx); +} + +//---------------------------------------------------------------------- +float +LHCDataTool::getCrossingAngle(const EventContext& ctx) const { + SG::ReadCondHandle<AthenaAttributeList> readHandle{m_beamDataKey, ctx}; + return(**readHandle)["CrossingAngle"].data<float>(); +} + +float +LHCDataTool::getCrossingAngle(void) const { + const EventContext& ctx{Gaudi::Hive::currentContext()}; + return getCrossingAngle(ctx); +} + +//---------------------------------------------------------------------- +unsigned int +LHCDataTool::getBeam1Bunches(const EventContext& ctx) const { + SG::ReadCondHandle<AthenaAttributeList> readHandle{m_bcidDataKey, ctx}; + return(**readHandle)["Beam1Bunches"].data<unsigned int>(); +} + +unsigned int +LHCDataTool::getBeam1Bunches(void) const { + const EventContext& ctx{Gaudi::Hive::currentContext()}; + return getBeam1Bunches(ctx); +} + +unsigned int +LHCDataTool::getBeam2Bunches(const EventContext& ctx) const { + SG::ReadCondHandle<AthenaAttributeList> readHandle{m_bcidDataKey, ctx}; + return(**readHandle)["Beam2Bunches"].data<unsigned int>(); +} + +unsigned int +LHCDataTool::getBeam2Bunches(void) const { + const EventContext& ctx{Gaudi::Hive::currentContext()}; + return getBeam2Bunches(ctx); +} + +unsigned int +LHCDataTool::getLuminousBunches(const EventContext& ctx) const { + SG::ReadCondHandle<AthenaAttributeList> readHandle{m_bcidDataKey, ctx}; + return(**readHandle)["LuminousBunches"].data<unsigned int>(); +} + +unsigned int +LHCDataTool::getLuminousBunches(void) const { + const EventContext& ctx{Gaudi::Hive::currentContext()}; + return getLuminousBunches(ctx); +} + + +//---------------------------------------------------------------------- +// This function unpacks the blob every time this is accesed +// Should probably cache this using a callback +std::vector<unsigned char> +LHCDataTool::getBCIDMasks(const EventContext& ctx) const { + + SG::ReadCondHandle<AthenaAttributeList> bcidHandle{m_bcidDataKey, ctx}; + const coral::Blob& blob = (**bcidHandle)["BCIDmasks"].data<coral::Blob>(); + const unsigned char* p = static_cast<const unsigned char*>(blob.startingAddress()); + + // Should always be 3564 BCIDs + if (blob.size() != 3564) { + ATH_MSG_WARNING("Found BCID blob with size " << blob.size() << "!"); + } + + std::vector<unsigned char> bcid_vector(3564); + + bool first = true; + for (int i=0; i<blob.size(); i++) { + // First BCID is 1, but this is stored at location i=1 + // So you can index this vector as bcid_vector[bcid_number] + bcid_vector[i] = *p++; + if (first && (bcid_vector[i] == 3)) { + first = false; + ATH_MSG_DEBUG("Found first colliding BCID at " << i); + } + } + + return bcid_vector; +} + +std::vector<unsigned char> +LHCDataTool::getBCIDMasks(void) const { + const EventContext& ctx{Gaudi::Hive::currentContext()}; + return getBCIDMasks(ctx); +} + + + diff --git a/LHCData/LHCDataTools/src/LHCDataTool.h b/LHCData/LHCDataTools/src/LHCDataTool.h new file mode 100644 index 00000000..6a969cc0 --- /dev/null +++ b/LHCData/LHCDataTools/src/LHCDataTool.h @@ -0,0 +1,107 @@ +// -*- C++ -*- + +/* + Copyright (C) 2002-2019 CERN for the benefit of the ATLAS and CERN collaborations +*/ + +/** @file LHCDataTool.h Header file for LHCDataTool. + @author Eric Torrence, 20/04/22 +*/ + +// Multiple inclusion protection +#ifndef LHCDATA_TOOL +#define LHCDATA_TOOL + +// Include interface class +#include "AthenaBaseComps/AthAlgTool.h" +#include "LHCDataTools/ILHCDataTool.h" + +// Include Athena stuff +#include "AthenaPoolUtilities/CondAttrListCollection.h" +#include "StoreGate/ReadCondHandleKey.h" + +#include "GaudiKernel/ICondSvc.h" +#include "Gaudi/Property.h" + +// Include Gaudi classes +#include "GaudiKernel/EventContext.h" + +/** This class contains a Tool that reads Waveform range data and makes it available to + other algorithms. The current implementation reads the data from a COOL database. +*/ + +class LHCDataTool: public extends<AthAlgTool, ILHCDataTool> { + + public: + //----------Public Member Functions----------// + // Structors + LHCDataTool(const std::string& type, const std::string& name, const IInterface* parent); //!< Constructor + virtual ~LHCDataTool() = default; //!< Destructor + + // Standard Gaudi functions + virtual StatusCode initialize() override; //!< Gaudi initialiser + virtual StatusCode finalize() override; //!< Gaudi finaliser + + // Methods to return fill data + virtual int getFillNumber(const EventContext& ctx) const override; + virtual int getFillNumber(void) const override; + + virtual std::string getMachineMode(const EventContext& ctx) const override; + virtual std::string getMachineMode(void) const override; + + virtual std::string getInjectionScheme(const EventContext& ctx) const override; + virtual std::string getInjectionScheme(void) const override; + + virtual int getBeamType1(const EventContext& ctx) const override; + virtual int getBeamType1(void) const override; + + virtual int getBeamType2(const EventContext& ctx) const override; + virtual int getBeamType2(void) const override; + + virtual unsigned int getNumBunchBeam1(const EventContext& ctx) const override; + virtual unsigned int getNumBunchBeam1(void) const override; + + virtual unsigned int getNumBunchBeam2(const EventContext& ctx) const override; + virtual unsigned int getNumBunchBeam2(void) const override; + + virtual unsigned int getNumBunchColl(const EventContext& ctx) const override; + virtual unsigned int getNumBunchColl(void) const override; + + // Methods to return beam data + virtual std::string getBeamMode(const EventContext& ctx) const override; + virtual std::string getBeamMode(void) const override; + + virtual float getBetaStar(const EventContext& ctx) const override; + virtual float getBetaStar(void) const override; + + virtual float getCrossingAngle(const EventContext& ctx) const override; + virtual float getCrossingAngle(void) const override; + + // Methods to return BCID data + virtual unsigned int getBeam1Bunches(const EventContext& ctx) const override; + virtual unsigned int getBeam1Bunches(void) const override; + + virtual unsigned int getBeam2Bunches(const EventContext& ctx) const override; + virtual unsigned int getBeam2Bunches(void) const override; + + virtual unsigned int getLuminousBunches(const EventContext& ctx) const override; + virtual unsigned int getLuminousBunches(void) const override; + + // This returns a char for each BCID encoding beam1/beam2 + // A colliding BCID will have value 3 + // BCIDs always count starting at 1 + virtual std::vector<unsigned char> getBCIDMasks(const EventContext& ctx) const override; + virtual std::vector<unsigned char> getBCIDMasks(void) const override; + + private: + // Read Cond Handles + SG::ReadCondHandleKey<AthenaAttributeList> m_fillDataKey{this, "FillDataKey", "/LHC/FillData", "Key of fill data folder"}; + SG::ReadCondHandleKey<AthenaAttributeList> m_beamDataKey{this, "BeamDataKey", "/LHC/BeamData", "Key of fill data folder"}; + SG::ReadCondHandleKey<AthenaAttributeList> m_bcidDataKey{this, "BcidDataKey", "/LHC/BCIDData", "Key of fill data folder"}; + + ServiceHandle<ICondSvc> m_condSvc{this, "CondSvc", "CondSvc"}; + +}; + +//---------------------------------------------------------------------- +#endif // LHCDATA_TOOL diff --git a/LHCData/LHCDataTools/src/components/LHCDataTools_entries.cxx b/LHCData/LHCDataTools/src/components/LHCDataTools_entries.cxx new file mode 100644 index 00000000..1f44bf09 --- /dev/null +++ b/LHCData/LHCDataTools/src/components/LHCDataTools_entries.cxx @@ -0,0 +1,3 @@ +#include "../LHCDataTool.h" + +DECLARE_COMPONENT( LHCDataTool ) diff --git a/LHCData/LHCDataUtils/CMakeLists.txt b/LHCData/LHCDataUtils/CMakeLists.txt new file mode 100644 index 00000000..2c449495 --- /dev/null +++ b/LHCData/LHCDataUtils/CMakeLists.txt @@ -0,0 +1,10 @@ +################################################################################ +# Package: LHCDataUtils +################################################################################ + +# Declare the package name: +atlas_subdir( LHCDataUtils ) + +atlas_install_python_modules( python/*.py ) + +atlas_install_scripts( scripts/*.sh scripts/*.py ) diff --git a/LHCData/LHCDataUtils/README.md b/LHCData/LHCDataUtils/README.md new file mode 100644 index 00000000..0ca447fe --- /dev/null +++ b/LHCData/LHCDataUtils/README.md @@ -0,0 +1 @@ +Utilities to produce and update COOL databases for LHC information diff --git a/LHCData/LHCDataUtils/python/CoolDataReader.py b/LHCData/LHCDataUtils/python/CoolDataReader.py new file mode 100644 index 00000000..238f8987 --- /dev/null +++ b/LHCData/LHCDataUtils/python/CoolDataReader.py @@ -0,0 +1,181 @@ +# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration + +# +# CoolDataReader +# +# Eric Torrence - October 2010 +# +# Contents: +# CoolDataReader - utility object to handle reading of COOL DB folders. +# The benefit over just using AtlCoolLib directly is that each DB connection is +# cached, so multiple connections to the same DB will not be made. +# +# CoolDataReader.readData() returns a list the full IObject for maximal flexibility +# +# General usage example +# myReader = CoolDataReader('COOLONL_TRIGGER/COMP200', '/TRIGGER/LUMI/LBLESTONL') +# myReader.setIOVRange(startIOV, endIOV) +# myReader.readData() +# for obj in myReader.data: +# ... +# +# One can specify specific channels or IOV ranges if desired, but by default all data will be loaded +# +# The CoolDataReader uses the LumiDBHandler internally to cache multiple CoolConnections +# + +from __future__ import print_function +from PyCool import cool + +# Get our global DB handler object +from LHCDataUtils.LumiDBHandler import LumiDBHandler + + +class CoolDataReader: + + def __init__(self, dbstr=None, folderstr=None): + + self.verbose = False + + # Defined variables + self.dbstr = None + self.folderstr = None + self.channelIdList = [] + self.tag = '' + self.iovstart = None + self.iovend = None + + self.folder = None + self.data = [] + + # Initialize to default values + self.setChannel() + self.setTag() + self.setFolder(dbstr, folderstr) + self.setIOVRange() + + def setFolder(self, dbstr, folderstr): + # Force re-opening connection if these are different + if (dbstr != self.dbstr) or (folderstr != self.folderstr): + self.folder = None + + self.dbstr = dbstr + self.folderstr = folderstr + + def setTag(self, tagstr=''): + self.tag = tagstr + + def setChannel(self, channelIdList=[]): + self.channelIdList = channelIdList + + def setChannelAll(self): + self.setChannel() + + def setChannelId(self, channelId): + self.setChannel([channelId]) + + def setIOVRange(self, iovstart=cool.ValidityKeyMin, iovend=cool.ValidityKeyMax): + self.iovstart = iovstart + self.iovend = iovend + + def setIOVRangeFromRun(self, runnum, startOfNextRun=False): + self.iovstart = runnum << 32 + if startOfNextRun: + self.iovend = ((runnum+1) << 32) + else: + self.iovend = ((runnum+1) << 32) - 1 + + # Call to get data after all other parameters are properly set + # Data is returned as a list of IObject values, one per DB entry. + # This gives maximal flexibility to manipulate the items + def readData(self): + + self.data = [] + + # Open the DB connection here if needed + if self.folder is None: + dbHandler = LumiDBHandler() + self.folder = dbHandler.getFolder(self.dbstr, self.folderstr) + + if self.folder is None: + print("Can't access DB", self.dbstr, 'folder', self.folderstr, '!') + return self.data + + # Create the channel list + if len(self.channelIdList) == 0: + channels = cool.ChannelSelection.all() + self.readChannelList(channels) + + else: + # Build the channel list here + self.channelIdList.sort() # Must be sorted! + + # Must read channels 50 at a time due to COOL limit... + ichan = 0 + while (ichan < len(self.channelIdList)) : + + jchan = 0 + channels = None + firstChan = True + + for channelId in self.channelIdList[ichan:]: + jchan += 1 + if firstChan: + firstChan = False + channels = cool.ChannelSelection(channelId) + else: + channels.addChannel(channelId) + if jchan == 50: break + + # Remeber how many we have read for next time + if self.verbose: + print('CoolDataReader.readData() - loaded %d channels from %d' % (jchan, ichan)) + ichan += jchan + + if self.verbose: + print('CoolDataReader.readData() - browsing', self.iovstart, self.iovend, 'with channel', channels, 'and tag', self.tag) + + self.readChannelList(channels) + + # End of loop building channel list and reading + + # End of if statement reading data + return self.data + + def readChannelList(self, channels): + + # Open iterator over our defined IOV range + try: + itr = self.folder.browseObjects(self.iovstart, self.iovend, channels, self.tag) + except Exception as e: + print('CoolDataReader.readData() - exception reading folder:', self.folderstr) + print(e) + print('CoolDataReader.readData() - will try to reconnect (once)') + + # Force re-opening connection + dbHandler = LumiDBHandler() + dbHandler.verbose = True + self.folder = dbHandler.getFolder(self.dbstr, self.folderstr, force=True) + + if self.folder is None: + print('CoolDataReader.readData() - forced re-opening failed!') + return self.data + + # OK, lets try reading this again + print('CoolDataReader.readData() - trying to re-read re-opened folder!') + try: + itr = self.folder.browseObjects(self.iovstart, self.iovend, channels, self.tag) + except Exception as e: + print('CoolDataReader.readData() - exception reading folder:', self.folderstr) + print(e) + return self.data + + while itr.goToNext(): + obj = itr.currentRef() + # print obj.payload() + self.data.append(obj.clone()) + + itr.close() + + + diff --git a/LHCData/LHCDataUtils/python/LumiBlobConversion.py b/LHCData/LHCDataUtils/python/LumiBlobConversion.py new file mode 100644 index 00000000..bf57d1d2 --- /dev/null +++ b/LHCData/LHCDataUtils/python/LumiBlobConversion.py @@ -0,0 +1,388 @@ +# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration + +from __future__ import print_function +from builtins import range +import sys +import array +import struct + +# import cppyy +# cppyy.gbl.cool.IDatabase # force the load of the dictionary (to stay on the safe side) +# from cppyy import gbl +# def blob_read(self, size = -1): +# if size < 0: +# endpos = self.size() +# else: +# endpos = self.pos + size +# beginpos = self.pos +# self.pos = endpos +# buf = self.startingAddress() +# buf.SetSize(self.size()) +# return buf[beginpos:endpos] + +# add the new functions +# getattr(gbl,"coral::Blob").read = blob_read + +def bConvert(b, nbyte=1): + # routine to store an unsigned int (1, 2, 4 or 8 byte) in a blob + packopt=dict([[1,'B'],[2,'H'],[4,'f'],[8,'d']]) + if nbyte in packopt: + # print 'bConvert - b:[', b[0:nbyte], '] nbyte:', nbyte, ' fmt:', packopt[nbyte], type(b) + ival=struct.unpack(packopt[nbyte], b[0:nbyte]) + else: + print(f'bConvert: Unrecognized pack option {nbyte}') + sys.exit() + + return ival[0] + +# Optional arguemnt to nval to specify number of values to read +def bConvertList(b, nbyte=1, nval=1): + # routine to store an unsigned int (1, 2, 4 or 8 byte) in a blob + packopt=dict([[1,'B'],[2,'H'],[4,'f'],[8,'d']]) + if nbyte in packopt: + # print 'bConvert - b:[', b[0:nbyte], '] nbyte:', nbyte, ' fmt:', packopt[nbyte], type(b) + fmt = '%d%s' % (nval, packopt[nbyte]) + ival=struct.unpack(fmt, b[0:nval*nbyte]) + else: + print(f'bConvertList: Unrecognized pack option {nbyte}') + sys.exit() + + return list(ival) + +# Unpack bunch group bgrp. By default, bgrp=1 is the physics bunch group. +def unpackBunchGroup(blob, bgrp=1): + physBG = [] + if blob is None: return + if blob.size() == 0: return + + blobCopy = blob.read() + mask = (1 << int(bgrp)) + + ivallist = bConvertList(blobCopy, 1, 3564) + for i in range(3564): + if ivallist[i] & mask: + physBG.append(i) + +# blobCounter = 0 +# for i in range(3564): +# try: +# b = blobCopy[blobCounter:blobCounter+1] +# blobCounter += 1 +# ival = struct.unpack('B', b) +# #s = struct.unpack('B', b) +# #ival = bConvert(s) +# except Exception, e: +# print e +# ival = 0 +# if (ival>>1) & 1 == 1: +# physBG.append(i) + + return physBG + +# Unpack bunch group bgrp. By default, bgrp=1 is the physics bunch group. +def unpackBunchGroupList(blob, bgrp=[1]): + physBG = dict() + mask = dict() + + if blob is None: return + if blob.size() == 0: return + + blobCopy = blob.read() + + for id in bgrp: + mask[id] = (1 << int(id)) + physBG[id] = [] + + ivallist = bConvertList(blobCopy, 1, 3564) + for i in range(3564): + for id in bgrp: + if ivallist[i] & mask[id]: + physBG[id].append(i) + + return physBG + +# Generic routine to unpack BCID mask +# The nb1, nb2, nlumi are for backwards compatibility to Run1 +# These are not needed to unpack the Run2 BCID mask +# Return values are a list of beam1, beam2, and colliding BCIDs +def unpackBCIDMask(blob,nb1=0,nb2=0,nlumi=0): + + if blob is None: + return [],[],[] + + bloblength = blob.size() + + if bloblength == 0: + return [],[],[] + + if bloblength == 3564: + return unpackRun2BCIDMask(blob) + else: + return unpackRun1BCIDMask(blob,nb1,nb2,nlumi) + +# routine to unpack the BCID mask stored in COOL +# This is the run2 version +def unpackRun2BCIDMask(blob): + beam1=[] + beam2=[] + coll=[] + blobCopy = blob.read() + rawData = bConvertList(blobCopy, 1, 3564) + + for i in range(3564): + val = rawData[i] + if val & 0x01: + beam1.append(i) + if val & 0x02: + beam2.append(i) + if (val & 0x03) == 0x03: + coll.append(i) + + # print('unpackRun2BCIDMask found:') + # print(' Beam1:', beam1) + # print(' Beam2:', beam2) + # print(' Coll: ', coll) + + return beam1,beam2,coll + +# routine to unpack the BCID mask stored in COOL +# This is the run1 version +def unpackRun1BCIDMask(blob,nb1,nb2,nlumi): + beam1=[] + beam2=[] + coll=[] + blobCopy = blob.read() + beam1 = bConvertList(blobCopy, 2, nb1) + beam2 = bConvertList(blobCopy[2*nb1:], 2, nb2) + coll = bConvertList(blobCopy[2*(nb1+nb2):], 2, nlumi) + #unpackfmt = '%dH' % nb1 + #list(struct.unpack(unpackfmt, blobCopy[0:(2*nb1)])) + #unpackfmt = '%dH' % nb2 + #beam2 = list(struct.unpack(unpackfmt, blobCopy[(2*nb1):2*(nb1+nb2)])) + #unpackfmt = '%dH' % nlumi + #coll = list(struct.unpack(unpackfmt, blobCopy[2*(nb1+nb2):2*(nb1+nb2+nlumi)])) + +# blobCounter = 0 +# for i in range(nb1): +# b = blobCopy[blobCounter:blobCounter+2] +# blobCounter += 2 +# val=struct.unpack('H', b) +# beam1.append(val) + +# for i in range(nb2): +# b = blobCopy[blobCounter:blobCounter+2] +# blobCounter += 2 +# val=struct.unpack('H', b) +# beam2.append(val) + +# for i in range(nlumi): +# b = blobCopy[blobCounter:blobCounter+2] +# blobCounter += 2 +# val=struct.unpack('H', b) +# coll.append(val) + + return beam1,beam2,coll + +# routine to unpack values (raw lumi or currents) stored as blob in COOL +# blob - COOL blob with per-BCID values +# mask - BCID mask appropriate for quantity being unpacked (i.e.: beam1, collisions, ...) +# normValue - Normalization value from same COOL folder as BLOB (i.e.: B1BunchAverage) +# +# Note, the normValue is only used in certain storage modes. If you want to renormalize, do this yourself. +# Specifying a different value for the normValue will likely cause unpredictable results. + +def unpackBCIDValues(blob, mask=[], normValue=1): + + bss, bcidVec, lvec = unpackBunches(blob, mask) + + if bss>0: + if not (len(bcidVec)==len(lvec)): + print('unpackBCIDValues - length mismatch: len(bcidVec)=', len(bcidVec), 'len(lvec)=', len(lvec)) + sys.exit() + + bLumi=[] + for i in range(len(bcidVec)): + if bss<4: + bLumi.append(lvec[i]*normValue/pow(100,bss)) + else: + bLumi.append(lvec[i]) + + #for i in range(len(bcidVec)): + # print 'BCID:', bcidVec[i], 'Raw:', bLumi[i] + + return bcidVec,bLumi + + else: + return [],[] + +def unpackBunches(blob,mask=[]): + # routine to unpack Intensity/Luminosity info stored in COOL + # the mask given as input has to match the quantity to be + # unpacked (beam1, beam2, beamsand for B1, B2 intensities and + # luminosities, respectively) + + if blob is None or blob.size() == 0: + return 0,[],[] + + blobCopy = blob.read() + blobCounter = 0 + try: + b = blobCopy[blobCounter:blobCounter+1] + blobCounter += 1 + flag=bConvert(b) + bss=(flag%100)//10 + smod=flag%10 + # print 'Storage mode for',str, 'is', smod, 'with bss=', bss + + if smod==2: + b = blobCopy[blobCounter:blobCounter+2] + blobCounter += 2 + vlen=bConvert(b, 2) + #print 'Bunch vector has length ',vlen + bcidVec=[] + bcidVec = bConvertList(blobCopy[blobCounter:], 2, vlen) + blobCounter += 2*vlen + # for i in range(vlen): + # valb = blobCopy[blobCounter:blobCounter+2] + # blobCounter += 2 + # val=struct.unpack('H', valb) + # bcidVec.append(val) + + elif smod==0: + # Make sure this is a list, and sorted (can pass set for example) + bcidVec=list(mask) + bcidVec.sort() + vlen=len(mask) + elif smod==3: + print('storage mode 3 not implemented in unpackBunches') + sys.exit() + elif smod==1: + bcidVec=[i for i in range(3564)] + vlen=3564 + else: + print('Unknown storage mode ',smod) + sys.exit() + valueVec=[] + + valueVec = bConvertList(blobCopy[blobCounter:], bss, vlen) +# for i in range(vlen): +# valb = blobCopy[blobCounter:blobCounter+bss] +# blobCounter += bss +# val=bConvert(valb,bss) +# valueVec.append(val) + + return bss,bcidVec,valueVec + + except RuntimeError as e: + print(e) + return 0,[],[] + +# Unpack live fraction into vector keyed by bcid-1 +# Takes payload of /TRIGGER/LUMI/PerBcidDeadtime folder +def unpackLiveFraction(trigPayload, priority = 'high'): + + liveVec = array.array('f', 3564*[0.]) + + if priority == 'high': + blob = trigPayload['HighPriority'] + elif priority == 'low': + blob = trigPayload['LowPriority'] + else: + print('unpackLiveFraction - unknown priority requested %s', str(priority)) + return liveVec + + bloblength = blob.size() + + # Due to a bug, the blob was sometimes written at 3654 rather than desired 3564 + # More bugs, use anything long enough + if bloblength < 3*3564: #!= 3*3654 and bloblength != 3*3564: + # Corrupt, don't trust anything + print('unpackLiveFraction found blob length %d!' % bloblength) + return liveVec + + blobCopy = blob.read() + # blobCounter = 0 + + # No counts, no work to do + turnCounter = trigPayload['TurnCounter'] + if not turnCounter > 0: + return liveVec + + # Even if longer blob is present, only care about this range + + byte = bConvertList(blobCopy, 1, 3*3564) + + for i in range(3564): + + busyCounter = byte[3*i] | (byte[3*i+1] << 8) | (byte[3*i+2] << 16) + + # byte0 = struct.unpack('B', blobCopy[blobCounter:blobCounter+1]) + # blobCounter += 1 + # byte1 = struct.unpack('B', blobCopy[blobCounter:blobCounter+1]) + # blobCounter += 1 + # byte2 = struct.unpack('B', blobCopy[blobCounter:blobCounter+1]) + # blobCounter += 1 + # busyCounter = byte0 | (byte1 << 8) | (byte2 << 16) + + liveFrac = 1 - float(busyCounter) / turnCounter + + liveVec[i] = liveFrac + + # print 'BCID: %d Busy: %d Turn: %d Live: %f' % (i+1, busyCounter, turnCounter, liveFrac) + + return liveVec + +# Unpack live fraction into vector keyed by bcid-1 +# Takes payload of /TRIGGER/LUMI/PerBcidDeadtime folder +def unpackLiveFractionRun2(trigPayload, priority = 'high'): + + liveVec = array.array('f', 3564*[0.]) + + if priority == 'high': + blob = trigPayload['DT0'] + elif priority == 'low': + blob = trigPayload['DT1'] + else: + print('unpackLiveFraction - unknown priority requested %s', str(priority)) + return liveVec + + bloblength = blob.size() + + if bloblength < 3*(3564+2): #!= 3*3654 and bloblength != 3*3564: + # Corrupt, don't trust anything + print('unpackLiveFraction found blob length %d!' % bloblength) + return liveVec + + blobCopy = blob.read() + # blobCounter = 0 + + # Turn counter is now at the end, so we must unpack everything + byte = bConvertList(blobCopy, 1, 3*3566) + + i = 3565 + turnCounter = byte[3*i] | (byte[3*i+1] << 8) | (byte[3*i+2] << 16) + + if not turnCounter > 0: + return liveVec + + # Entry 0 is LB number, which we can skip + for i in range(1, 3564): + + busyCounter = byte[3*i] | (byte[3*i+1] << 8) | (byte[3*i+2] << 16) + + # byte0 = struct.unpack('B', blobCopy[blobCounter:blobCounter+1]) + # blobCounter += 1 + # byte1 = struct.unpack('B', blobCopy[blobCounter:blobCounter+1]) + # blobCounter += 1 + # byte2 = struct.unpack('B', blobCopy[blobCounter:blobCounter+1]) + # blobCounter += 1 + # busyCounter = byte0 | (byte1 << 8) | (byte2 << 16) + + liveFrac = float(turnCounter - busyCounter) / turnCounter + + liveVec[i] = liveFrac + + # print 'BCID: %d Busy: %d Turn: %d Live: %f' % (i+1, busyCounter, turnCounter, liveFrac) + + return liveVec + diff --git a/LHCData/LHCDataUtils/python/LumiDBHandler.py b/LHCData/LHCDataUtils/python/LumiDBHandler.py new file mode 100644 index 00000000..7e6d5619 --- /dev/null +++ b/LHCData/LHCDataUtils/python/LumiDBHandler.py @@ -0,0 +1,121 @@ +# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration + +# +# LumiDBHandler +# +# Eric Torrence - October 2010 +# +# Contents: +# LumiDBHandler - utility object to handle opening and closing COOL DB connections within +# a large python script. The benefit over just using AtlCoolLib directly +# is that each DB connection is cached, so multiple connections to the same +# DB will not be made. +# +# The parent script should call closeAllDB in its __del__ function to close +# the DB connections, even if the script crashes. +# +# General usage example +# dbH = LumiDBHandler() +# myFolder = dbH.getFolder('COOLONL_TRIGGER/COMP200', '/TRIGGER/LUMI/LBLESTONL') +# +# One can then browse the folder as usual using browseObjects +# +# The CoolDataReader uses this class internally to make for more easy access +# + +import CoolConvUtilities.AtlCoolLib as AtlCoolLib + +class LumiDBHandler: + + # Define dbDict here at class scope + # Then access with self.__class__.dbDict and it will be the same for all instances of the class + # This is a pythonish way to create static classes + + # Dict to store DB connection indexed by text DB connection name + dbDict = dict() + + + def __init__(self): + + # Debug output (can be changed for each instance, slick...) + self.verbose = False + + # Return a folder reference for the dbstring, folder specified + # DB will be opened if necessary + # Example: getFolder('COOLONL_TRIGGER/COMP200', '/TRIGGER/LUMI/LBLESTONL') + def getFolder(self, dbstring, folder, force=False): + + if self.verbose: + print('LumiDBHandler.getFolder(', dbstring, ',', folder, ') called') + + if not self.openDB(dbstring, force=force): + print("LumiDBHandler.getFolder - can't connect to DB!") + return None + + return self.__class__.dbDict[dbstring].getFolder(folder) + + # Open a COOL DB connection based on a name such as "COOLONL_INDET/OFLP200" + # Returns True if successful (or DB already open) + def openDB(self, dbstring, oracle=False, debug=False, force=False): + + if self.verbose: + print('LumiDBHandler.openDB(', dbstring, ') called') + + # Check if already open + if dbstring in self.__class__.dbDict: + + # No force, just return + if not force: + if self.verbose: + print('LumiDBHandler.openDB - Connection already exists') + return True # Yes it is + + # Force specified, close so we can re-open + if self.verbose: + print('LumiDBHandler.openDB - Connection already exists, closing first due to force=True') + self.closeDB(dbstring) + + # Try to open DB connection + if self.verbose: + print('LumiDBHandler.openDB - Connecting to', dbstring) + + try: + db = AtlCoolLib.indirectOpen(dbstring, readOnly=True, oracle=oracle, debug=debug) + except Exception as e: + print(e) + return False + + # OK, opened. Save this to our dict for later use + self.__class__.dbDict[dbstring] = db + + return True + + # Close specific DB + def closeDB(self, dbstring): + + if self.verbose: + print('LumiDBHandler.closeDB - Closing connection to', dbstring) + + if dbstring not in self.__class__.dbDict: + print("LumiDBHandler.closeDB - DB doesn't exist:", dbstring) + else: + try: + self.__class__.dbDict[dbstring].closeDatabase() + except Exception as e: + print(e) + self.__class__.dbDict.pop(dbstring) + + # Called by default in the destructor, but not guaranteed if there are problems + def closeAllDB(self): + self.closeAll() + + def closeAll(self): + + if self.verbose: + print('LumiDBHandler.closeAllDB called') + + # Can't use iterkeys here as we are deleting the elements + # In python3 must create explicit list + for dbstring in list(self.__class__.dbDict.keys()): + self.closeDB(dbstring) + diff --git a/LHCData/LHCDataUtils/scripts/makeLHCFillData.py b/LHCData/LHCDataUtils/scripts/makeLHCFillData.py new file mode 100755 index 00000000..f0187ab2 --- /dev/null +++ b/LHCData/LHCDataUtils/scripts/makeLHCFillData.py @@ -0,0 +1,734 @@ +#!/usr/bin/env python3 +import os +import sys +import argparse + +import time +import calendar +import datetime + +from pathlib import Path + +#from LHCDataUtils.LumiDBHandler import LumiDBHandler +#from LHCDataUtils.CoolDataReader import CoolDataReader + +from PyCool import cool + +# Useful utilities for manipulating COOL files +# See https://gitlab.cern.ch/atlas/athena/-/blob/master/Database/CoolConvUtilities/python/AtlCoolLib.py +from CoolConvUtilities.AtlCoolLib import ensureFolder,forceOpen,athenaDesc,timeVal,timeString + + +import CoolConvUtilities.AtlCoolLib as AtlCoolLib + +class LumiDBHandler: + + # Define dbDict here at class scope + # Then access with self.__class__.dbDict and it will be the same for all instances of the class + # This is a pythonish way to create static classes + + # Dict to store DB connection indexed by text DB connection name + dbDict = dict() + + + def __init__(self): + + # Debug output (can be changed for each instance, slick...) + self.verbose = False + + # Return a folder reference for the dbstring, folder specified + # DB will be opened if necessary + # Example: getFolder('COOLONL_TRIGGER/COMP200', '/TRIGGER/LUMI/LBLESTONL') + def getFolder(self, dbstring, folder, force=False): + + if self.verbose: + print('LumiDBHandler.getFolder(', dbstring, ',', folder, ') called') + + if not self.openDB(dbstring, force=force): + print("LumiDBHandler.getFolder - can't connect to DB!") + return None + + return self.__class__.dbDict[dbstring].getFolder(folder) + + # Open a COOL DB connection based on a name such as "COOLONL_INDET/OFLP200" + # Returns True if successful (or DB already open) + def openDB(self, dbstring, oracle=False, debug=False, force=False): + + if self.verbose: + print('LumiDBHandler.openDB(', dbstring, ') called') + + # Check if already open + if dbstring in self.__class__.dbDict: + + # No force, just return + if not force: + if self.verbose: + print('LumiDBHandler.openDB - Connection already exists') + return True # Yes it is + + # Force specified, close so we can re-open + if self.verbose: + print('LumiDBHandler.openDB - Connection already exists, closing first due to force=True') + self.closeDB(dbstring) + + # Try to open DB connection + if self.verbose: + print('LumiDBHandler.openDB - Connecting to', dbstring) + + try: + db = AtlCoolLib.indirectOpen(dbstring, readOnly=True, oracle=oracle, debug=debug) + except Exception as e: + print(e) + return False + + # OK, opened. Save this to our dict for later use + self.__class__.dbDict[dbstring] = db + + return True + + # Close specific DB + def closeDB(self, dbstring): + + if self.verbose: + print('LumiDBHandler.closeDB - Closing connection to', dbstring) + + if dbstring not in self.__class__.dbDict: + print("LumiDBHandler.closeDB - DB doesn't exist:", dbstring) + else: + try: + self.__class__.dbDict[dbstring].closeDatabase() + except Exception as e: + print(e) + self.__class__.dbDict.pop(dbstring) + + # Called by default in the destructor, but not guaranteed if there are problems + def closeAllDB(self): + self.closeAll() + + def closeAll(self): + + if self.verbose: + print('LumiDBHandler.closeAllDB called') + + # Can't use iterkeys here as we are deleting the elements + # In python3 must create explicit list + for dbstring in list(self.__class__.dbDict.keys()): + self.closeDB(dbstring) + +# End of class LumiDBHandler + + +class CoolDataReader: + + def __init__(self, dbstr=None, folderstr=None): + + self.verbose = False + + # Defined variables + self.dbstr = None + self.folderstr = None + self.channelIdList = [] + self.tag = '' + self.iovstart = None + self.iovend = None + + self.folder = None + self.data = [] + + # Initialize to default values + self.setChannel() + self.setTag() + self.setFolder(dbstr, folderstr) + self.setIOVRange() + + def setFolder(self, dbstr, folderstr): + # Force re-opening connection if these are different + if (dbstr != self.dbstr) or (folderstr != self.folderstr): + self.folder = None + + self.dbstr = dbstr + self.folderstr = folderstr + + def setTag(self, tagstr=''): + self.tag = tagstr + + def setChannel(self, channelIdList=[]): + self.channelIdList = channelIdList + + def setChannelAll(self): + self.setChannel() + + def setChannelId(self, channelId): + self.setChannel([channelId]) + + def setIOVRange(self, iovstart=cool.ValidityKeyMin, iovend=cool.ValidityKeyMax): + self.iovstart = iovstart + self.iovend = iovend + + def setIOVRangeFromRun(self, runnum, startOfNextRun=False): + self.iovstart = runnum << 32 + if startOfNextRun: + self.iovend = ((runnum+1) << 32) + else: + self.iovend = ((runnum+1) << 32) - 1 + + # Call to get data after all other parameters are properly set + # Data is returned as a list of IObject values, one per DB entry. + # This gives maximal flexibility to manipulate the items + def readData(self): + + self.data = [] + + # Open the DB connection here if needed + if self.folder is None: + dbHandler = LumiDBHandler() + self.folder = dbHandler.getFolder(self.dbstr, self.folderstr) + + if self.folder is None: + print("Can't access DB", self.dbstr, 'folder', self.folderstr, '!') + return self.data + + # Create the channel list + if len(self.channelIdList) == 0: + channels = cool.ChannelSelection.all() + self.readChannelList(channels) + + else: + # Build the channel list here + self.channelIdList.sort() # Must be sorted! + + # Must read channels 50 at a time due to COOL limit... + ichan = 0 + while (ichan < len(self.channelIdList)) : + + jchan = 0 + channels = None + firstChan = True + + for channelId in self.channelIdList[ichan:]: + jchan += 1 + if firstChan: + firstChan = False + channels = cool.ChannelSelection(channelId) + else: + channels.addChannel(channelId) + if jchan == 50: break + + # Remeber how many we have read for next time + if self.verbose: + print('CoolDataReader.readData() - loaded %d channels from %d' % (jchan, ichan)) + ichan += jchan + + if self.verbose: + print('CoolDataReader.readData() - browsing', self.iovstart, self.iovend, 'with channel', channels, 'and tag', self.tag) + + self.readChannelList(channels) + + # End of loop building channel list and reading + + # End of if statement reading data + return self.data + + def readChannelList(self, channels): + + # Open iterator over our defined IOV range + try: + itr = self.folder.browseObjects(self.iovstart, self.iovend, channels, self.tag) + except Exception as e: + print('CoolDataReader.readData() - exception reading folder:', self.folderstr) + print(e) + print('CoolDataReader.readData() - will try to reconnect (once)') + + # Force re-opening connection + dbHandler = LumiDBHandler() + dbHandler.verbose = True + self.folder = dbHandler.getFolder(self.dbstr, self.folderstr, force=True) + + if self.folder is None: + print('CoolDataReader.readData() - forced re-opening failed!') + return self.data + + # OK, lets try reading this again + print('CoolDataReader.readData() - trying to re-read re-opened folder!') + try: + itr = self.folder.browseObjects(self.iovstart, self.iovend, channels, self.tag) + except Exception as e: + print('CoolDataReader.readData() - exception reading folder:', self.folderstr) + print(e) + return self.data + + while itr.goToNext(): + obj = itr.currentRef() + # print obj.payload() + self.data.append(obj.clone()) + + itr.close() + +# End of class CoolDataReader + + +def parse_arguments(): + + description = "Script to create LHC data database" + parser = argparse.ArgumentParser(description) + + parser.add_argument("--verbose", "-v", action="store_true", + help="Print debugging information") + + parser.add_argument("--fills", "-f", nargs='+', + help="Fills to find information on") + + parser.add_argument("--recent", action="store_true", + help="Update new fills not already in DB") + + parser.add_argument("--output", "-o", default="fill_data.db", + help="Specify output DB") + + parser.add_argument("--create", "-c", action="store_true", + help="Overwrite existing DB") + + return parser.parse_args() + + +# Take a string and turn it into a list of integers +# Can specify single values, ranges, or comma separated lists of both +# Can also specify file name with list of numbers +def parseFillList(filllist): + + fill_list = [] + + # Check if this is a file with fill numbers + if len(filllist) == 1: + path = Path(filllist[0]) + if path.exists() and path.is_file(): + print(f"Reading fills from {path}") + # Try reading each line as a fill number + with path.open() as f: + for line in f.readlines(): + line = line.strip() + if len(line) == 0: continue + if line[0] in ['#', '!']: continue + if not line.isnumeric(): + print(f"Error parsing {line}") + continue + fill_list.append(int(line)) + # Done reading file + return(fill_list) + elif '-' in filllist[0]: + pass + elif ',' in filllist[0]: + pass + elif not filllist[0].isnumeric(): + print(f"File {path} doesn't exist!") + return fill_list + + for string in filllist: + tokens = string.split(',') + + for segment in tokens: + + if len(segment) == 0: continue + + if '-' in segment: # Range of fills + start, end = segment.split('-') + if not start.isnumeric(): + print(f"Found invalid fill {start}") + continue + if not end.isnumeric(): + print(f"Found invalid fill {end}") + continue + start = int(start) + end = int(end) + fill_list.extend(list(range(int(start), int(end)+1))) + + else: + if not segment.isnumeric(): + print(f"Found invalid fill {segment}") + continue + fill_list.append(int(segment)) + + return(fill_list) + +def getIOVDict(args, first_fill): + + if args.verbose: + print(f"Searching for IOV for fill {first_fill}") + + # Utility to read ATLAS DB + dbHandler = LumiDBHandler() + dbname = "COOLOFL_DCS/CONDBR2" + db = dbHandler.getFolder(dbname, "/LHC/DCS/FILLSTATE") + db.setPrefetchAll(False) + + # Use channel selector to give us a reverse iterator + channel = cool.ChannelSelection(order=cool.ChannelSelection.channelBeforeSinceDesc) + + # Limit how much of DB to read + iovstart = 1000000000 * timeVal("2022-06-01:00:00:00") + #iovstart = int(1E9) * int(calendar.timegm(time.strptime("2022-06-01", "%Y-%m-%d"))) + + if args.verbose: + print(f"Starting from {timeString(iovstart)}") + + # Now step backwards reading until we find our first fill + itr = db.browseObjects(iovstart, cool.ValidityKeyMax, channel) + + iov_dict = {} + last_fill = None + while itr.goToNext(): + obj = itr.currentRef() + fill = obj.payloadValue('FillNumber') + # Skip any invalid values + if not fill.isnumeric(): + print(f"Found {fill} for FillNumber, skipping!") + continue + + # Replace with integer + fill = int(obj.payloadValue('FillNumber')) + + if fill == 0: + print(f"Found FillNumber = {fill}, skipping!") + continue + + # Lots of output... + #if args.verbose: + # print(f"Fill {obj.payloadValue('FillNumber')} Since {timeString(obj.since())}") + + # Have we gone far enough? + if fill < first_fill: break + + # Check if we found a new fill + if not iov_dict.get(fill, None): + + # Update previous fill + if last_fill: + iov = iov_dict[last_fill] + iov_dict[last_fill] = (obj.until(), iov[1]) + + last_fill = fill + iov_dict[fill] = (obj.since(), obj.until()) + + # Update fill range + iov = iov_dict[fill] + iov_dict[fill] = (obj.since(), iov[1]) + + # Done, print out what we found if desired + if args.verbose: + for fill in sorted(iov_dict.keys()): + + #time_lo = iov_dict[fill][0] // int(1E9) + #time_hi = iov_dict[fill][1] // int(1E9) + + #print(f"Fill {fill} from {datetime.datetime.fromtimestamp(time_lo)} to {datetime.datetime.fromtimestamp(time_hi)}") + print(f"Fill {fill} from {timeString(iov_dict[fill][0])} to {timeString(iov_dict[fill][1])}") + + # Close our database here + dbHandler.closeDB(dbname) + + return iov_dict + + +class FillObject: + def __init__(self): + self.fillNumber = 0 + self.machineMode = '' + self.beamType1 = 0 + self.beamType2 = 0 + self.nBeam1 = 0 + self.nBeam2 = 0 + self.nColl = 0 + self.injScheme = '' + self.since = cool.ValidityKeyMin + self.until = cool.ValidityKeyMax + + # List with tuple of IOV range and payload dict (since, until, payload) + self.iov_list = [] + + # List with tuple of IOV range and payload dict (since, until, payload) + self.bcid_list = [] + + def updateFill(self, obj): + + payload = obj.payload() + + # Set the fill number + if self.fillNumber == 0: + self.updateFillParams(payload) + + # For bunches, use max + self.nBeam1 = max(self.nBeam1, payload["NumBunchBeam1"]) + self.nBeam2 = max(self.nBeam2, payload["NumBunchBeam2"]) + self.nColl = max(self.nColl, payload["NumBunchColl"]) + + self.updateIOVList(obj.since(), obj.until(), obj.payload()) + + def updateBCID(self, obj): + + # Need to make sure we don't duplicate IOVs from FILLPARAMS + # Truncate records to fit into fill IOV range + if obj.since() < self.since: + since = self.since + else: + since = obj.since() + + if obj.until() > self.until: + until = self.until + else: + until = obj.until() + + if since == until: # Could happen? + return + + # Need to copy here? + #valdict = {} + #for key in ["Beam1Bunches", "Beam2Bunches", "LuminousBunches", "BCIDmasks"]: + # valdict[key] = obj.payload()[key] + + self.bcid_list.append((since, until, obj.payload())) + + def updateIOVList(self, since, until, payload): + + # We want a subset of the payload + valdict = {} + for key in ["BeamMode", "BetaStar", "CrossingAngle"]: + valdict[key] = payload[key] + + # No list, append current value + if len(self.iov_list) == 0: + self.iov_list.append((since, until, valdict)) + + else: + # Extend existing IOV to start of this one + self.iov_list[-1] = (self.iov_list[-1][0], since, self.iov_list[-1][2]) + + # Add new IOV if value is different + # Check values separately, as we don't want to update betastar + # unless we are in stable beams + last_payload = self.iov_list[-1][2] + if valdict["BeamMode"] != last_payload["BeamMode"]: + self.iov_list.append((since, until, valdict)) + + elif valdict["CrossingAngle"] != last_payload["CrossingAngle"]: + self.iov_list.append((since, until, valdict)) + + elif valdict["BeamMode"] == "STABLE BEAMS" and valdict["BetaStar"] != last_payload["BetaStar"]: + self.iov_list.append((since, until, valdict)) + + def updateFillParams(self, payload): + # Update things that shouldn't change + try: + self.fillNumber = int(payload['FillNumber']) + except Exception as e: + print(f'Error setting fill number from {payload["FillNumber"]}') + print(e) + + self.machineMode = payload['MachineMode'] + self.beamType1 = payload['BeamType1'] + self.beamType2 = payload['BeamType2'] + self.injScheme = payload['InjectionScheme'] + + def setLast(self): + ''' Set the final entry in the self.iov_list to an open-ended IOV ''' + if len(self.iov_list) == 0: return + + self.iov_list[-1] = (self.iov_list[-1][0], cool.ValidityKeyMax, self.iov_list[-1][2]) + + def __str__(self): + return f'Fill: {self.fillNumber} Mode: {self.machineMode} B1/2: {self.beamType1}/{self.beamType2} Bunches B1/B2/Coll: {self.nBeam1}/{self.nBeam2}/{self.nColl} {self.injScheme}' + +def findRecentFills(args): + fill_list = [] + print("findRecentFills not implemented!") + return fill_list + +# +# Start execution here +# +args = parse_arguments() + +if args.verbose: + print(f"Updating fill {args.fills}") + print(f"Recent: {args.recent}") + print(f"Output: {args.output}") + print(f"Create: {args.create}") + +if args.recent: + + if args.fills: + print("Can't specify --fills and --recent!") + sys.exit(1) + + fill_list = findRecentFills(args) + + if len(fill_list) == 0: + print("No new fills found!") + sys.exit(0) + +else: + if not args.fills: + print("No fills specified! Use --fills to provide fill numbers") + sys.exit(1) + + fill_list = parseFillList(args.fills) + fill_list.sort() + + if len(fill_list) == 0: + print("No fills specified! Use --fills to provide fill numbers") + sys.exit(1) + +if args.verbose: + print(f"Fill list:\n{fill_list}") + +# To speed things up, lets find the IOV ranges for each fill in our fill list +# Do this by reverse lookup in FILLSTATE +iov_dict = getIOVDict(args, fill_list[0]) + +# Open (or create) the database +connectString = f'sqlite://;schema={args.output};dbname=CONDBR3' + +if args.verbose: + print(f"Opening DB using connection string {connectString}") + +if os.path.exists(args.output): + if args.create: + print(f"Deleting {args.output} due to --create") + os.remove(args.output) + + else: + print(f"Output DB file {args.output} already exists!") + print(f"Writing in place, use --create to delete") + +else: + print(f"Creating new DB {args.output}") + +# Opens or creates as needed +db = forceOpen(connectString) + +if not db: + print("Error with {connectString}") + sys.exit(1) + +# Create folders (use CoolConvUtilities function) +description = athenaDesc(runLumi=False, datatype="AthenaAttributeList") +if args.verbose: + print(f"Folder description: {description}") + +# Order matters here! +lhc_spec = cool.RecordSpecification() +lhc_spec.extend("FillNumber", cool.StorageType.Int32) +lhc_spec.extend("MachineMode", cool.StorageType.String4k) +lhc_spec.extend("InjectionScheme", cool.StorageType.String4k) +lhc_spec.extend("BeamType1", cool.StorageType.Int32) +lhc_spec.extend("BeamType2", cool.StorageType.Int32) +lhc_spec.extend("NumBunchBeam1", cool.StorageType.UInt32) +lhc_spec.extend("NumBunchBeam2", cool.StorageType.UInt32) +lhc_spec.extend("NumBunchColl", cool.StorageType.UInt32) + +beam_spec = cool.RecordSpecification() +beam_spec.extend("BeamMode", cool.StorageType.String4k) +beam_spec.extend("BetaStar", cool.StorageType.Float) +beam_spec.extend("CrossingAngle", cool.StorageType.Float) + +bcid_spec = cool.RecordSpecification() +bcid_spec.extend("Beam1Bunches", cool.StorageType.UInt32) +bcid_spec.extend("Beam2Bunches", cool.StorageType.UInt32) +bcid_spec.extend("LuminousBunches", cool.StorageType.UInt32) +bcid_spec.extend("BCIDmasks", cool.StorageType.Blob64k) + +# Ensure folder opens or creates as needed +# Create storage buffer for writing +try: + lhc_folder = ensureFolder(db, "/LHC/FillData", lhc_spec, description) + beam_folder = ensureFolder(db, "/LHC/BeamData", beam_spec, description) + bcid_folder = ensureFolder(db, "/LHC/BCIDData", bcid_spec, description) + lhc_folder.setupStorageBuffer() + beam_folder.setupStorageBuffer() + bcid_folder.setupStorageBuffer() + +except Exception as e: + print("Could not access or create folders!") + print(e) + sys.exit(1) + +db_lhc = CoolDataReader("COOLOFL_DCS/CONDBR2", "/LHC/DCS/FILLSTATE") +db_bcid = CoolDataReader("COOLONL_TDAQ/CONDBR2", "/TDAQ/OLC/LHC/FILLPARAMS") + +for fill in fill_list: + + if not iov_dict.get(fill, None): # Should have just found this + print(f"Can't find fill {fill} in IOV dictionary!") + sys.exit(1) + + time_lo = iov_dict[fill][0] + time_hi = iov_dict[fill][1] + if args.verbose: + print(f"Working on fill {fill} from {timeString(time_lo)} to {timeString(time_hi)}") + + fill_obj = FillObject() + fill_obj.since = time_lo + fill_obj.until = time_hi + + db_lhc.setIOVRange(time_lo, time_hi) + db_lhc.readData() + + for obj in db_lhc.data: + fill_obj.updateFill(obj) + + # Is this the last fill? + if fill == fill_list[-1]: + fill_obj.setLast() + + db_bcid.setIOVRange(time_lo, time_hi) + db_bcid.readData() + + for obj in db_bcid.data: + fill_obj.updateBCID(obj) + + # Now we want to fill our folders + lhc_record = cool.Record(lhc_spec) + lhc_record["FillNumber"] = fill_obj.fillNumber + lhc_record["MachineMode"] = fill_obj.machineMode + lhc_record["InjectionScheme"] = fill_obj.injScheme + lhc_record["BeamType1"] = fill_obj.beamType1 + lhc_record["BeamType2"] = fill_obj.beamType2 + lhc_record["NumBunchBeam1"] = fill_obj.nBeam1 + lhc_record["NumBunchBeam2"] = fill_obj.nBeam2 + lhc_record["NumBunchColl"] = fill_obj.nColl + + if args.verbose: + print(f"Writing fill {fill_obj.fillNumber}") + print(fill_obj) + + chan = 0 # No channels here, but need to pass dummy argument + lhc_folder.storeObject(fill_obj.since, fill_obj.until, lhc_record, chan) + + if args.verbose: print("Writing beam folder:") + + for since, until, payload in fill_obj.iov_list: + beam_record = cool.Record(beam_spec) + for key in payload: + beam_record[key] = payload[key] + + if args.verbose: + print(f"{timeString(since)} - {timeString(until)}: {beam_record}") + + beam_folder.storeObject(since, until, beam_record, chan) + + for since, until, payload in fill_obj.bcid_list: + bcid_record = cool.Record(bcid_spec) + for key in payload: + bcid_record[key] = payload[key] + + if args.verbose: + print(f"{timeString(since)} - {timeString(until)}: {bcid_record}") + + bcid_folder.storeObject(since, until, bcid_record, chan) + +# Make sure everything is writen +lhc_folder.flushStorageBuffer() +beam_folder.flushStorageBuffer() +bcid_folder.flushStorageBuffer() + +# End of loop over fills + +db.closeDatabase() diff --git a/xAOD/xAODFaserLHC/CMakeLists.txt b/xAOD/xAODFaserLHC/CMakeLists.txt new file mode 100644 index 00000000..577da2cc --- /dev/null +++ b/xAOD/xAODFaserLHC/CMakeLists.txt @@ -0,0 +1,27 @@ +# Copyright (C) 2020 CERN for the benefit of the FASER collaboration + +# Declare the package name. +atlas_subdir( xAODFaserLHC ) + +# External dependencies. +find_package( xAODUtilities ) + +# Component(s) in the package. +atlas_add_library( xAODFaserLHC + xAODFaserLHC/*.h xAODFaserLHC/versions/*.h Root/*.cxx + PUBLIC_HEADERS xAODFaserLHC + LINK_LIBRARIES xAODCore ) + +atlas_add_xaod_smart_pointer_dicts( + INPUT xAODFaserLHC/selection.xml + OUTPUT _selectionFile + OBJECTS "xAOD::FaserLHCData_v1" "xAOD::FaserLHCDataAux_v1") + +atlas_add_dictionary( xAODFaserLHCDict + xAODFaserLHC/xAODFaserLHCDict.h + ${_selectionFile} + LINK_LIBRARIES xAODCore xAODFaserLHC + EXTRA_FILES Root/dict/*.cxx ) + +# Understand what this does... +atlas_generate_cliddb( xAODFaserLHC ) diff --git a/xAOD/xAODFaserLHC/Root/xAODFaserLHCCLIDs.cxx b/xAOD/xAODFaserLHC/Root/xAODFaserLHCCLIDs.cxx new file mode 100644 index 00000000..86811f0c --- /dev/null +++ b/xAOD/xAODFaserLHC/Root/xAODFaserLHCCLIDs.cxx @@ -0,0 +1,8 @@ +/* + Copyright (C) 2020 CERN for the benefit of the FASER collaboration +*/ + +//simple includes to force the CLASS_DEF etc to be encountered during compile + +#include "xAODFaserLHC/FaserLHCData.h" +#include "xAODFaserLHC/FaserLHCDataAux.h" diff --git a/xAOD/xAODFaserLHC/Root/xAODFaserLHCDataAux_v1.cxx b/xAOD/xAODFaserLHC/Root/xAODFaserLHCDataAux_v1.cxx new file mode 100644 index 00000000..37e37fa7 --- /dev/null +++ b/xAOD/xAODFaserLHC/Root/xAODFaserLHCDataAux_v1.cxx @@ -0,0 +1,45 @@ +/* + Copyright (C) 2020 CERN for the benefit of the FASER collaboration +*/ + +// $Id: $ + +// Local include(s): +#include "xAODFaserLHC/versions/FaserLHCDataAux_v1.h" + +namespace xAOD { + + FaserLHCDataAux_v1::FaserLHCDataAux_v1() + : AuxInfoBase(), + fillNumber(0), + machineMode(""), + beamMode(""), + beamType1(0), + beamType2(0), + numBunchBeam1(0), + numBunchBeam2(0), + numBunchColliding(0), + betaStar(0), + crossingAngle(0), + injectionScheme(""), + distanceToCollidingBCID(0), + distanceToUnpairedB1(0), + distanceToUnpairedB2(0) + { + AUX_VARIABLE( fillNumber ); + AUX_VARIABLE( machineMode ); + AUX_VARIABLE( beamMode ); + AUX_VARIABLE( beamType1 ); + AUX_VARIABLE( beamType2 ); + AUX_VARIABLE( numBunchBeam1 ); + AUX_VARIABLE( numBunchBeam2 ); + AUX_VARIABLE( numBunchColliding ); + AUX_VARIABLE( betaStar ); + AUX_VARIABLE( crossingAngle ); + AUX_VARIABLE( injectionScheme ); + AUX_VARIABLE( distanceToCollidingBCID ); + AUX_VARIABLE( distanceToUnpairedB1 ); + AUX_VARIABLE( distanceToUnpairedB2 ); + } + +} // namespace xAOD diff --git a/xAOD/xAODFaserLHC/Root/xAODFaserLHCData_v1.cxx b/xAOD/xAODFaserLHC/Root/xAODFaserLHCData_v1.cxx new file mode 100644 index 00000000..05403bcf --- /dev/null +++ b/xAOD/xAODFaserLHC/Root/xAODFaserLHCData_v1.cxx @@ -0,0 +1,78 @@ +/* + Copyright (C) 2020 CERN for the benefit of the FASER collaboration +*/ + +// $Id: $ + +// xAOD include(s): +#include "xAODCore/AuxStoreAccessorMacros.h" + +// Local include(s): +#include "xAODFaserLHC/versions/FaserLHCData_v1.h" + +namespace xAOD { + + FaserLHCData_v1::FaserLHCData_v1() + : SG::AuxElement() { + } + + ///////////////////////////////////////////////////////////////////////////// + // + // Implementation for the start time functions + AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( FaserLHCData_v1, unsigned int, + fillNumber, set_fillNumber ) + + AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( FaserLHCData_v1, std::string, + machineMode, set_machineMode ) + + AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( FaserLHCData_v1, std::string, + beamMode, set_beamMode ) + + AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( FaserLHCData_v1, int, + beamType1, set_beamType1 ) + + AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( FaserLHCData_v1, int, + beamType2, set_beamType2 ) + + AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( FaserLHCData_v1, unsigned int, + numBunchBeam1, set_numBunchBeam1 ) + + AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( FaserLHCData_v1, unsigned int, + numBunchBeam2, set_numBunchBeam2 ) + + AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( FaserLHCData_v1, unsigned int, + numBunchColliding, set_numBunchColliding ) + + AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( FaserLHCData_v1, float, + betaStar, set_betaStar ) + + AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( FaserLHCData_v1, float, + crossingAngle, set_crossingAngle ) + + AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( FaserLHCData_v1, std::string, + injectionScheme, set_injectionScheme ) + + AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( FaserLHCData_v1, int, + distanceToCollidingBCID, set_distanceToCollidingBCID ) + + AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( FaserLHCData_v1, int, + distanceToUnpairedB1, set_distanceToUnpairedB1 ) + + AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( FaserLHCData_v1, int, + distanceToUnpairedB2, set_distanceToUnpairedB2 ) + +} // namespace xAOD + +namespace xAOD { + + std::ostream& operator<<(std::ostream& s, const xAOD::FaserLHCData_v1& td) { + s << "xAODFaserLHCData: fill=" << td.fillNumber() + << " beamMode=" << td.beamMode() + << " betaStar=" << td.betaStar() + << " distanceToCollidingBCID=" << td.distanceToCollidingBCID() + << std::endl; + + return s; + } + +} // namespace xAOD diff --git a/xAOD/xAODFaserLHC/xAODFaserLHC/FaserLHCData.h b/xAOD/xAODFaserLHC/xAODFaserLHC/FaserLHCData.h new file mode 100644 index 00000000..561c8782 --- /dev/null +++ b/xAOD/xAODFaserLHC/xAODFaserLHC/FaserLHCData.h @@ -0,0 +1,22 @@ +// Dear emacs, this is -*- c++ -*- + +/* + Copyright (C) 2020 CERN for the benefit of the FASER collaboration +*/ + +#ifndef XAODFASERLHC_FASERLHCDATA_H +#define XAODFASERLHC_FASERLHCDATA_H + +// Local include(s): +#include "xAODFaserLHC/versions/FaserLHCData_v1.h" + +namespace xAOD { + /// Declare the latest version of the class + typedef FaserLHCData_v1 FaserLHCData; +} + +// Set up a CLID for the container: +#include "xAODCore/CLASS_DEF.h" +CLASS_DEF( xAOD::FaserLHCData, 255278152, 1 ) + +#endif // XAODFASERLHC_FASERLHCDATA_H diff --git a/xAOD/xAODFaserLHC/xAODFaserLHC/FaserLHCDataAux.h b/xAOD/xAODFaserLHC/xAODFaserLHC/FaserLHCDataAux.h new file mode 100644 index 00000000..6eeac626 --- /dev/null +++ b/xAOD/xAODFaserLHC/xAODFaserLHC/FaserLHCDataAux.h @@ -0,0 +1,22 @@ +// Dear emacs, this is -*- c++ -*- + +/* + Copyright (C) 2020 CERN for the benefit of the FASER collaboration +*/ + +#ifndef XAODFASERLHC_FASERLHCDATAAUX_H +#define XAODFASERLHC_FASERLHCDATAAUX_H + +// Local include(s): +#include "xAODFaserLHC/versions/FaserLHCDataAux_v1.h" + +namespace xAOD { + /// Declare the latest version of the class + typedef FaserLHCDataAux_v1 FaserLHCDataAux; +} + +// Set up a CLID for the container: +#include "xAODCore/CLASS_DEF.h" +CLASS_DEF( xAOD::FaserLHCDataAux, 151779941, 1 ) + +#endif // XAODFASERLHC_FASERLHCDATAAUX_H diff --git a/xAOD/xAODFaserLHC/xAODFaserLHC/selection.xml b/xAOD/xAODFaserLHC/xAODFaserLHC/selection.xml new file mode 100644 index 00000000..c4ba39f9 --- /dev/null +++ b/xAOD/xAODFaserLHC/xAODFaserLHC/selection.xml @@ -0,0 +1,13 @@ +<!-- Copyright (C) 2020 CERN for the benefit of the FASER collaboration --> +<!-- Missing id values here, need to figure out where these come from... --> +<lcgdict> + + <class name="xAOD::FaserLHCData_v1" + id="6c373036-fad1-476c-9a8b-15591faccf00"/> + <typedef name="xAOD::FaserLHCData" /> + + <class name="xAOD::FaserLHCDataAux_v1" + id="484ef174-8efb-426a-888c-827f72ed972a"/> + <typedef name="xAOD::FaserLHCDataAux" /> + +</lcgdict> diff --git a/xAOD/xAODFaserLHC/xAODFaserLHC/versions/FaserLHCDataAux_v1.h b/xAOD/xAODFaserLHC/xAODFaserLHC/versions/FaserLHCDataAux_v1.h new file mode 100644 index 00000000..c2daf68d --- /dev/null +++ b/xAOD/xAODFaserLHC/xAODFaserLHC/versions/FaserLHCDataAux_v1.h @@ -0,0 +1,68 @@ +// Dear emacs, this is -*- c++ -*- + +/* + Copyright (C) 2020 CERN for the benefit of the FASER collaboration +*/ + +#ifndef XAODFASERLHC_VERSIONS_FASERLHCDATAAUX_V1_H +#define XAODFASERLHC_VERSIONS_FASERLHCDATAAUX_V1_H + +// System include(s): +extern "C" { +# include "stdint.h" +} + +// xAOD include(s): +#include "xAODCore/AuxInfoBase.h" + +namespace xAOD { + + /// Class holding the data handled by FaserLHCData_v1 + /// + /// This class holds the payload of the TLB raw data + /// + /// @author Eric Torrence <torrence@uoregon.edu> + /// + /// $Revision: $ + /// $Date: $ + + class FaserLHCDataAux_v1 : public AuxInfoBase { + + public: + /// Default constructor + FaserLHCDataAux_v1(); + + private: + /// @name LHCData payload + /// @{ + unsigned int fillNumber; + std::string machineMode; + std::string beamMode; + int beamType1; + int beamType2; + + unsigned int numBunchBeam1; + unsigned int numBunchBeam2; + unsigned int numBunchColliding; + + float betaStar; + float crossingAngle; + + std::string injectionScheme; + + int distanceToCollidingBCID; + int distanceToUnpairedB1; + int distanceToUnpairedB2; + /// @} + + }; // class FaserLHCDataAuxContainer_v1 + +} // namespace xAOD + +// Set up the StoreGate inheritance of the class: +#include "xAODCore/BaseInfo.h" +SG_BASE( xAOD::FaserLHCDataAux_v1, xAOD::AuxInfoBase ); + +#endif // XAODFASERLHC_VERSIONS_FASERLHCDATAAUX_V1_H + + diff --git a/xAOD/xAODFaserLHC/xAODFaserLHC/versions/FaserLHCData_v1.h b/xAOD/xAODFaserLHC/xAODFaserLHC/versions/FaserLHCData_v1.h new file mode 100644 index 00000000..982ce105 --- /dev/null +++ b/xAOD/xAODFaserLHC/xAODFaserLHC/versions/FaserLHCData_v1.h @@ -0,0 +1,103 @@ +// Dear emacs, this is -*- c++ -*- + +/* + Copyright (C) 2020 CERN for the benefit of the FASER collaboration +*/ + +#ifndef XAODFASERLHC_VERSIONS_FASERLHCDATA_V1_H +#define XAODFASERLHC_VERSIONS_FASERLHCDATA_V1_H + +// System include(s): +extern "C" { +# include "stdint.h" +} + +// Core EDM include(s): +#include "AthContainers/AuxElement.h" + +namespace xAOD { + + /// Class describing the Faser LHC information + /// + /// + class FaserLHCData_v1 : public SG::AuxElement { + + public: + /// Default constructor + FaserLHCData_v1(); + + /// @name Access LHC elements + /// @{ + + /// First batch comes from ATLAS /LHC/DCS/FILLSTATE folder + /// which is a copy of information sent to ATLAS by LHC over DIP + + /// LHC fill number + unsigned int fillNumber() const; + void set_fillNumber(unsigned int value); + + /// LHC machine mode (i.e. PROTON PHYSICS) + std::string machineMode() const; + void set_machineMode(std::string value); + + /// LHC Beam Mode (i.e. STABLE BEAMS) + std::string beamMode() const; + void set_beamMode(std::string value); + + /// LHC Beam type (charge of particle, 1-proton, 82-lead) + int beamType1() const; + void set_beamType1(int value); + + int beamType2() const; + void set_beamType2(int value); + + /// Nominal bunches (derived from DIP "Circulating Bunch Configuration") + unsigned int numBunchBeam1() const; + void set_numBunchBeam1(unsigned int value); + unsigned int numBunchBeam2() const; + void set_numBunchBeam2(unsigned int value); + unsigned int numBunchColliding() const; + void set_numBunchColliding(unsigned int value); + + // Beta* at IP1, in cm + float betaStar() const; + void set_betaStar(float value); + + // Crossing angle at IP1, in uRad + float crossingAngle() const; + void set_crossingAngle(float value); + + // Injection scheme + std::string injectionScheme() const; + void set_injectionScheme(std::string value); + + // Second batch is information is related to the BCID structure + + // Number of BCIDs difference between current event BCID (from EventInfo) + // and a colliding BCID in the nominal bunch pattern + int distanceToCollidingBCID() const; + void set_distanceToCollidingBCID(int value); + + int distanceToUnpairedB1() const; + void set_distanceToUnpairedB1(int value); + + int distanceToUnpairedB2() const; + void set_distanceToUnpairedB2(int value); + + /// Collimator settings? + + + /// @} + + + }; // class FaserLHCData_v1 + + std::ostream& operator<<(std::ostream& s, const xAOD::FaserLHCData_v1& td); + +} // namespace xAOD + +// Declare the inheritance of the type: +#include "xAODCore/BaseInfo.h" +SG_BASE( xAOD::FaserLHCData_v1, SG::AuxElement ); + +#endif // XAODFASERLHC_VERSIONS_FASERLHCDATA_V1_H diff --git a/xAOD/xAODFaserLHC/xAODFaserLHC/xAODFaserLHCDict.h b/xAOD/xAODFaserLHC/xAODFaserLHC/xAODFaserLHCDict.h new file mode 100644 index 00000000..4715ad86 --- /dev/null +++ b/xAOD/xAODFaserLHC/xAODFaserLHC/xAODFaserLHCDict.h @@ -0,0 +1,27 @@ +/* + Copyright (C) 2020 CERN for the benefit of the FASER collaboration +*/ + +#ifndef XAODFASERLHC_XAODFASERLHCDICT_H +#define XAODFASERLHC_XAODFASERLHCDICT_H + +// Local include(s): +#include "xAODFaserLHC/FaserLHCData.h" +#include "xAODFaserLHC/FaserLHCDataAux.h" +#include "xAODFaserLHC/versions/FaserLHCData_v1.h" +#include "xAODFaserLHC/versions/FaserLHCDataAux_v1.h" + +// EDM include(s). +#include "xAODCore/tools/DictHelpers.h" + +namespace { + struct GCCXML_DUMMY_INSTANTIATION_XAODFASERLHC { + // Local type(s). + XAOD_INSTANTIATE_NS_OBJECT_TYPES( xAOD, FaserLHCData_v1 ); + + }; +} + + +#endif // XAODFASERLHC_XAODFASERLHCDICT_H + diff --git a/xAOD/xAODFaserLHCAthenaPool/CMakeLists.txt b/xAOD/xAODFaserLHCAthenaPool/CMakeLists.txt new file mode 100644 index 00000000..d15a49cc --- /dev/null +++ b/xAOD/xAODFaserLHCAthenaPool/CMakeLists.txt @@ -0,0 +1,14 @@ +# Copyright (C) 2020 CERN for the benefit of the FASER collaboration + +# Declare the package name. +atlas_subdir( xAODFaserLHCAthenaPool ) + +# Component(s) in the package: +atlas_add_poolcnv_library( xAODFaserLHCAthenaPoolPoolCnv + src/*.h src/*.cxx + FILES xAODFaserLHC/FaserLHCData.h xAODFaserLHC/FaserLHCDataAux.h + TYPES_WITH_NAMESPACE xAOD::FaserLHCData xAOD::FaserLHCDataAux + CNV_PFX xAOD + LINK_LIBRARIES AthenaPoolCnvSvcLib AthenaPoolUtilities xAODFaserLHC ) + + diff --git a/xAOD/xAODFaserLHCAthenaPool/src/xAODFaserLHCDataAuxCnv.cxx b/xAOD/xAODFaserLHCAthenaPool/src/xAODFaserLHCDataAuxCnv.cxx new file mode 100644 index 00000000..2ae94670 --- /dev/null +++ b/xAOD/xAODFaserLHCAthenaPool/src/xAODFaserLHCDataAuxCnv.cxx @@ -0,0 +1,5 @@ +/* + Copyright (C) 2020 CERN for the benefit of the FASER collaboration +*/ + +// Dummy source file so that cmake will know this is a custom converter. diff --git a/xAOD/xAODFaserLHCAthenaPool/src/xAODFaserLHCDataAuxCnv.h b/xAOD/xAODFaserLHCAthenaPool/src/xAODFaserLHCDataAuxCnv.h new file mode 100644 index 00000000..dc3f3f9a --- /dev/null +++ b/xAOD/xAODFaserLHCAthenaPool/src/xAODFaserLHCDataAuxCnv.h @@ -0,0 +1,16 @@ +// Dear emacs, this is -*- c++ -*- + +/* + Copyright (C) 2020 CERN for the benefit of the FASER collaboration +*/ + +#ifndef XAODFASERLHCDATAATHENAPOOL_XAODFASERLHCDATAAUXCNV_H +#define XAODFASERLHCDATAATHENAPOOL_XAODFASERLHCDATAAUXCNV_H + +#include "xAODFaserLHC/FaserLHCDataAux.h" +#include "AthenaPoolCnvSvc/T_AthenaPoolAuxContainerCnv.h" + +typedef T_AthenaPoolAuxContainerCnv<xAOD::FaserLHCDataAux> xAODFaserLHCDataAuxCnv; + + +#endif // XAODFASERLHCDATAATHENAPOOL_XAODFASERLHCDATAAUXCNV_H diff --git a/xAOD/xAODFaserLHCAthenaPool/src/xAODFaserLHCDataCnv.cxx b/xAOD/xAODFaserLHCAthenaPool/src/xAODFaserLHCDataCnv.cxx new file mode 100644 index 00000000..2ae94670 --- /dev/null +++ b/xAOD/xAODFaserLHCAthenaPool/src/xAODFaserLHCDataCnv.cxx @@ -0,0 +1,5 @@ +/* + Copyright (C) 2020 CERN for the benefit of the FASER collaboration +*/ + +// Dummy source file so that cmake will know this is a custom converter. diff --git a/xAOD/xAODFaserLHCAthenaPool/src/xAODFaserLHCDataCnv.h b/xAOD/xAODFaserLHCAthenaPool/src/xAODFaserLHCDataCnv.h new file mode 100644 index 00000000..a73c439c --- /dev/null +++ b/xAOD/xAODFaserLHCAthenaPool/src/xAODFaserLHCDataCnv.h @@ -0,0 +1,17 @@ +// Dear emacs, this is -*- c++ -*- + +/* + Copyright (C) 2020 CERN for the benefit of the FASER collaboration +*/ + +#ifndef XAODFASERLHCDATAATHENAPOOL_XAODFASERLHCDATACNV_H +#define XAODFASERLHCDATAATHENAPOOL_XAODFASERLHCDATACNV_H + +#include "xAODFaserLHC/FaserLHCData.h" +#include "AthenaPoolCnvSvc/T_AthenaPoolxAODCnv.h" + + +typedef T_AthenaPoolxAODCnv<xAOD::FaserLHCData> xAODFaserLHCDataCnv; + + +#endif // XAODFASERLHCDATAATHENAPOOL_XAODFASERLHCDATACNV_H -- GitLab