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 1/4] 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 4e233bf4f..967526945 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 ecf7b9bb3..eb1c72ae4 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 958fb2ba6..9913f4be0 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 000000000..17abef2b2 --- /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 000000000..195fbbd12 --- /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 000000000..9463ab877 --- /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 000000000..f53257bc5 --- /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 000000000..1620e8dcf --- /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 000000000..248abc91d --- /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 000000000..cf6abd17d --- /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 000000000..cdd2d267c --- /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 000000000..64a2d5365 --- /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 000000000..6a969cc02 --- /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 000000000..1f44bf090 --- /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 000000000..2c4494950 --- /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 000000000..0ca447fe3 --- /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 000000000..238f8987c --- /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 000000000..bf57d1d28 --- /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 000000000..7e6d5619b --- /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 000000000..f0187ab20 --- /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 000000000..577da2cc7 --- /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 000000000..86811f0c3 --- /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 000000000..37e37fa78 --- /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 000000000..05403bcfc --- /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 000000000..561c8782c --- /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 000000000..6eeac6266 --- /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 000000000..c4ba39f9a --- /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 000000000..c2daf68db --- /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 000000000..982ce1053 --- /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 000000000..4715ad862 --- /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 000000000..d15a49ccd --- /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 000000000..2ae946703 --- /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 000000000..dc3f3f9af --- /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 000000000..2ae946703 --- /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 000000000..a73c439c6 --- /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 From 0755ad2832f36b5a35d5ea6ae86e86d26b12f64e Mon Sep 17 00:00:00 2001 From: Eric Torrence <eric.torrence@cern.ch> Date: Tue, 22 Nov 2022 08:51:18 +0100 Subject: [PATCH 2/4] Updates to the LHC data schema --- .../FaserAuthentication/data/dblookup.xml | 13 +- LHCData/LHCDataAlgs/src/LHCDataAlg.cxx | 145 +++++++++++++++--- LHCData/LHCDataAlgs/src/LHCDataAlg.h | 12 +- .../LHCDataTools/LHCDataTools/ILHCDataTool.h | 16 +- LHCData/LHCDataTools/src/LHCDataTool.cxx | 108 +++++-------- LHCData/LHCDataTools/src/LHCDataTool.h | 30 ++-- .../python/WaveformRangeConfig.py | 10 +- ...CDataAux_v1.cxx => FaserLHCDataAux_v1.cxx} | 23 ++- ...aserLHCData_v1.cxx => FaserLHCData_v1.cxx} | 32 ++-- .../versions/FaserLHCDataAux_v1.h | 14 +- .../xAODFaserLHC/versions/FaserLHCData_v1.h | 42 +++-- 11 files changed, 290 insertions(+), 155 deletions(-) rename xAOD/xAODFaserLHC/Root/{xAODFaserLHCDataAux_v1.cxx => FaserLHCDataAux_v1.cxx} (77%) rename xAOD/xAODFaserLHC/Root/{xAODFaserLHCData_v1.cxx => FaserLHCData_v1.cxx} (82%) diff --git a/Database/ConnectionManagement/FaserAuthentication/data/dblookup.xml b/Database/ConnectionManagement/FaserAuthentication/data/dblookup.xml index 9913f4be0..1518267ee 100644 --- a/Database/ConnectionManagement/FaserAuthentication/data/dblookup.xml +++ b/Database/ConnectionManagement/FaserAuthentication/data/dblookup.xml @@ -26,19 +26,24 @@ <service name="sqlite_file:///cvmfs/faser.cern.ch/repo/sw/database/DBRelease/current/sqlite200/CABP200.db" accessMode="read" /> </logicalservice> + <logicalservice name="COOLOFL_DIGITIZER"> + <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> + <logicalservice name="COOLOFL_INDET"> <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> <logicalservice name="COOLOFL_TRIGGER"> - <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" /> + <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> <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" /> + <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/src/LHCDataAlg.cxx b/LHCData/LHCDataAlgs/src/LHCDataAlg.cxx index 9463ab877..de2269858 100644 --- a/LHCData/LHCDataAlgs/src/LHCDataAlg.cxx +++ b/LHCData/LHCDataAlgs/src/LHCDataAlg.cxx @@ -40,21 +40,20 @@ LHCDataAlg::execute(const EventContext& ctx) const { lhcDataHandle->set_fillNumber(m_lhcTool->getFillNumber(ctx)); lhcDataHandle->set_machineMode(m_lhcTool->getMachineMode(ctx)); - lhcDataHandle->set_injectionScheme(m_lhcTool->getInjectionScheme(ctx)); + lhcDataHandle->set_beamMode(m_lhcTool->getBeamMode(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)); + lhcDataHandle->set_stableBeams(m_lhcTool->getStableBeams(ctx)); + lhcDataHandle->set_injectionScheme(m_lhcTool->getInjectionScheme(ctx)); + + // Slight naming mismatch here, but oh well + lhcDataHandle->set_numBunchBeam1(m_lhcTool->getBeam1Bunches(ctx)); + lhcDataHandle->set_numBunchBeam2(m_lhcTool->getBeam2Bunches(ctx)); + lhcDataHandle->set_numBunchColliding(m_lhcTool->getCollidingBunches(ctx)); // Fill BCID information @@ -65,28 +64,48 @@ LHCDataAlg::execute(const EventContext& ctx) const { SG::ReadHandle<xAOD::EventInfo> xevt(m_eventInfo, ctx); unsigned int bcid = xevt->bcid(); - int nearest = findDistance(bcid, bcid_mask, 3); // Colliding beams + int nearest = findNearest(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 + nearest = findNearest(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 + nearest = findNearest(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 "); + // Add 127 to current BCID to check if inbound B1 is nearby FASER + int offset_bcid = (bcid + 127) % 3564; + nearest = findNearest(offset_bcid, bcid_mask, 1); // Inbound B1 + int nearest2 = findNearest(offset_bcid, bcid_mask, 3); // Inbound B1 + if (abs(nearest2) < abs(nearest)) nearest = nearest2; + lhcDataHandle->set_distanceToInboundB1(nearest); + ATH_MSG_DEBUG("Found distance of " << nearest << " from BCID " << bcid + << " to the nearest inbound B1 "); + + unsigned int previous; + previous = previousColliding(bcid, bcid_mask); + lhcDataHandle->set_distanceToPreviousColliding(previous); + ATH_MSG_DEBUG("Found distance of " << previous << " from BCID " << bcid + << " to the previous colliding bunch "); + + previous = previousTrain(bcid, bcid_mask); + lhcDataHandle->set_distanceToTrainStart(previous); + ATH_MSG_DEBUG("Found distance of " << previous << " from BCID " << bcid + << " to the start of the previous train "); + 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 { +LHCDataAlg::findNearest(unsigned int bcid, const std::vector<unsigned char>& bcid_mask, unsigned char mask) const { // Does the BCID make sense? if ((bcid > 3564) || (bcid <= 0)) { @@ -100,10 +119,7 @@ LHCDataAlg::findDistance(unsigned int bcid, const std::vector<unsigned char>& bc 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; + test_bcid = (bcid + i) % 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) @@ -114,15 +130,12 @@ LHCDataAlg::findDistance(unsigned int bcid, const std::vector<unsigned char>& bc 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; + test_bcid = (bcid - i) % 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 (test_bcid != 0) if (mask == bcid_mask[test_bcid]) return -i; } @@ -134,3 +147,91 @@ LHCDataAlg::findDistance(unsigned int bcid, const std::vector<unsigned char>& bc return -3565; } + +// Function to find previous colliding BCID +unsigned int +LHCDataAlg::previousColliding(unsigned int bcid, const std::vector<unsigned char>& bcid_mask) const { + + // Does the BCID make sense? + if ((bcid > 3564) || (bcid <= 0)) { + ATH_MSG_WARNING("Requested distance from invalid BCID " << bcid << "!"); + return 9999; + } + + unsigned int test_bcid; + + // Move backwards from the bcid (starting at offset of 0) + for (unsigned int i=0; i < 3564; i++) { + + // Try positive offsets + test_bcid = (bcid - i) % 3564; + + // BCID 3564 doesn't exist in our array (but will always be empty) + // So avoid these pathologies + if (test_bcid != 0) + if (0x03 == 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 colliding from BCID " << bcid << "!"); + ATH_MSG_WARNING(bcid_mask); + return 9999; + +} + +unsigned int +LHCDataAlg::previousTrain(unsigned int bcid, const std::vector<unsigned char>& bcid_mask) const { + + // Does the BCID make sense? + if ((bcid > 3564) || (bcid <= 0)) { + ATH_MSG_WARNING("Requested distance from invalid BCID " << bcid << "!"); + return 9999; + } + + unsigned int test_bcid; + + bool in_train = false; + // Move backwards from the bcid (starting at offset of 0) + for (unsigned int i=0; i < 3564; i++) { + + // BCID to test + test_bcid = (bcid - i) % 3564; + + // BCID 3564 doesn't exist in our array (but will always be empty) + // So avoid these pathologies + ATH_MSG_DEBUG("Test " << test_bcid << " In train: " << in_train << " mask[test_bcid]"); + + // If we are not in a train before, lets see if we are in a train now + if (!in_train) { + + // If we found a colliding bunch, set in_train, otherwise we are still not in a train + if ((test_bcid != 0) && (0x03 == bcid_mask[test_bcid])) + in_train = true; + + // Either way, move to next BCID + continue; + + } else { + + // Currently in a train, lets see if now we are not + if ((test_bcid == 0) || (0x03 != bcid_mask[test_bcid])) { + in_train = false; + // Previous BCID was first in train + return i-1; + } else { + in_train = true; + } + + } + + } + + // If we got to here, there was no match + // Does the BCID make sense? + ATH_MSG_WARNING("Couldn't find first in train from BCID " << bcid << "!"); + ATH_MSG_WARNING(bcid_mask); + return 9999; + +} diff --git a/LHCData/LHCDataAlgs/src/LHCDataAlg.h b/LHCData/LHCDataAlgs/src/LHCDataAlg.h index f53257bc5..a8785725a 100644 --- a/LHCData/LHCDataAlgs/src/LHCDataAlg.h +++ b/LHCData/LHCDataAlgs/src/LHCDataAlg.h @@ -32,10 +32,16 @@ class LHCDataAlg : public AthReentrantAlgorithm { {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; + int findNearest(unsigned int bcid, const std::vector<unsigned char>& bcid_mask, + unsigned char mask) const; + + unsigned int + previousColliding(unsigned int bcid, const std::vector<unsigned char>& bcid_mask) const; + + // Find the distance to the first BCID of the previous train + unsigned int + previousTrain(unsigned int bcid, const std::vector<unsigned char>& bcid_mask) const; - //ServiceHandle<ICondSvc> m_condSvc{this, "CondSvc", "CondSvc"}; }; #endif // LHCDATAALG_H diff --git a/LHCData/LHCDataTools/LHCDataTools/ILHCDataTool.h b/LHCData/LHCDataTools/LHCDataTools/ILHCDataTool.h index cf6abd17d..6e469d0eb 100644 --- a/LHCData/LHCDataTools/LHCDataTools/ILHCDataTool.h +++ b/LHCData/LHCDataTools/LHCDataTools/ILHCDataTool.h @@ -43,15 +43,6 @@ class ILHCDataTool: virtual public IAlgTool { 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; @@ -62,6 +53,9 @@ class ILHCDataTool: virtual public IAlgTool { virtual float getCrossingAngle(const EventContext& ctx) const = 0; virtual float getCrossingAngle(void) const = 0; + virtual bool getStableBeams(const EventContext& ctx) const = 0; + virtual bool getStableBeams(void) const = 0; + // Methods to return BCID data virtual unsigned int getBeam1Bunches(const EventContext& ctx) const = 0; virtual unsigned int getBeam1Bunches(void) const = 0; @@ -69,8 +63,8 @@ class ILHCDataTool: virtual public IAlgTool { 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 unsigned int getCollidingBunches(const EventContext& ctx) const = 0; + virtual unsigned int getCollidingBunches(void) const = 0; virtual std::vector<unsigned char> getBCIDMasks(const EventContext& ctx) const = 0; virtual std::vector<unsigned char> getBCIDMasks(void) const = 0; diff --git a/LHCData/LHCDataTools/src/LHCDataTool.cxx b/LHCData/LHCDataTools/src/LHCDataTool.cxx index 64a2d5365..7b0fafe00 100644 --- a/LHCData/LHCDataTools/src/LHCDataTool.cxx +++ b/LHCData/LHCDataTools/src/LHCDataTool.cxx @@ -22,7 +22,7 @@ LHCDataTool::initialize() { ATH_MSG_DEBUG("LHCDataTool::initialize()"); // Read Handles - ATH_CHECK(m_fillDataKey.initialize()); + //ATH_CHECK(m_fillDataKey.initialize()); ATH_CHECK(m_beamDataKey.initialize()); ATH_CHECK(m_bcidDataKey.initialize()); @@ -40,7 +40,7 @@ LHCDataTool::finalize() { int LHCDataTool::getFillNumber(const EventContext& ctx) const { // Read Cond Handle - SG::ReadCondHandle<AthenaAttributeList> readHandle{m_fillDataKey, ctx}; + SG::ReadCondHandle<AthenaAttributeList> readHandle{m_beamDataKey, ctx}; return (**readHandle)["FillNumber"].data<int>(); } @@ -54,7 +54,7 @@ LHCDataTool::getFillNumber(void) const { std::string LHCDataTool::getMachineMode(const EventContext& ctx) const { // Read Cond Handle - SG::ReadCondHandle<AthenaAttributeList> readHandle{m_fillDataKey, ctx}; + SG::ReadCondHandle<AthenaAttributeList> readHandle{m_beamDataKey, ctx}; return (**readHandle)["MachineMode"].data<std::string>(); } @@ -66,23 +66,22 @@ LHCDataTool::getMachineMode(void) const { //---------------------------------------------------------------------- std::string -LHCDataTool::getInjectionScheme(const EventContext& ctx) const { - // Read Cond Handle - SG::ReadCondHandle<AthenaAttributeList> readHandle{m_fillDataKey, ctx}; - return (**readHandle)["InjectionScheme"].data<std::string>(); -} +LHCDataTool::getBeamMode(const EventContext& ctx) const { + SG::ReadCondHandle<AthenaAttributeList> readHandle{m_beamDataKey, ctx}; + return(**readHandle)["BeamMode"].data<std::string>(); +} std::string -LHCDataTool::getInjectionScheme(void) const { +LHCDataTool::getBeamMode(void) const { const EventContext& ctx{Gaudi::Hive::currentContext()}; - return getInjectionScheme(ctx); + return getBeamMode(ctx); } //---------------------------------------------------------------------- int LHCDataTool::getBeamType1(const EventContext& ctx) const { // Read Cond Handle - SG::ReadCondHandle<AthenaAttributeList> readHandle{m_fillDataKey, ctx}; + SG::ReadCondHandle<AthenaAttributeList> readHandle{m_beamDataKey, ctx}; return (**readHandle)["BeamType1"].data<int>(); } @@ -96,7 +95,7 @@ LHCDataTool::getBeamType1(void) const { int LHCDataTool::getBeamType2(const EventContext& ctx) const { // Read Cond Handle - SG::ReadCondHandle<AthenaAttributeList> readHandle{m_fillDataKey, ctx}; + SG::ReadCondHandle<AthenaAttributeList> readHandle{m_beamDataKey, ctx}; return (**readHandle)["BeamType2"].data<int>(); } @@ -106,56 +105,6 @@ LHCDataTool::getBeamType2(void) const { 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 { @@ -182,6 +131,33 @@ LHCDataTool::getCrossingAngle(void) const { return getCrossingAngle(ctx); } +//---------------------------------------------------------------------- +bool +LHCDataTool::getStableBeams(const EventContext& ctx) const { + SG::ReadCondHandle<AthenaAttributeList> readHandle{m_beamDataKey, ctx}; + return(**readHandle)["StableBeams"].data<bool>(); +} + +bool +LHCDataTool::getStableBeams(void) const { + const EventContext& ctx{Gaudi::Hive::currentContext()}; + return getStableBeams(ctx); +} + +//---------------------------------------------------------------------- +std::string +LHCDataTool::getInjectionScheme(const EventContext& ctx) const { + // Read Cond Handle + SG::ReadCondHandle<AthenaAttributeList> readHandle{m_beamDataKey, ctx}; + return (**readHandle)["InjectionScheme"].data<std::string>(); +} + +std::string +LHCDataTool::getInjectionScheme(void) const { + const EventContext& ctx{Gaudi::Hive::currentContext()}; + return getInjectionScheme(ctx); +} + //---------------------------------------------------------------------- unsigned int LHCDataTool::getBeam1Bunches(const EventContext& ctx) const { @@ -208,15 +184,15 @@ LHCDataTool::getBeam2Bunches(void) const { } unsigned int -LHCDataTool::getLuminousBunches(const EventContext& ctx) const { +LHCDataTool::getCollidingBunches(const EventContext& ctx) const { SG::ReadCondHandle<AthenaAttributeList> readHandle{m_bcidDataKey, ctx}; - return(**readHandle)["LuminousBunches"].data<unsigned int>(); + return(**readHandle)["CollidingBunches"].data<unsigned int>(); } unsigned int -LHCDataTool::getLuminousBunches(void) const { +LHCDataTool::getCollidingBunches(void) const { const EventContext& ctx{Gaudi::Hive::currentContext()}; - return getLuminousBunches(ctx); + return getCollidingBunches(ctx); } diff --git a/LHCData/LHCDataTools/src/LHCDataTool.h b/LHCData/LHCDataTools/src/LHCDataTool.h index 6a969cc02..3acabfb75 100644 --- a/LHCData/LHCDataTools/src/LHCDataTool.h +++ b/LHCData/LHCDataTools/src/LHCDataTool.h @@ -43,14 +43,15 @@ class LHCDataTool: public extends<AthAlgTool, ILHCDataTool> { virtual StatusCode finalize() override; //!< Gaudi finaliser // Methods to return fill data + // Methods to return beam 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 std::string getBeamMode(const EventContext& ctx) const override; + virtual std::string getBeamMode(void) const override; virtual int getBeamType1(const EventContext& ctx) const override; virtual int getBeamType1(void) const override; @@ -58,25 +59,18 @@ class LHCDataTool: public extends<AthAlgTool, ILHCDataTool> { 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; + virtual bool getStableBeams(const EventContext& ctx) const override; + virtual bool getStableBeams(void) const override; + + virtual std::string getInjectionScheme(const EventContext& ctx) const override; + virtual std::string getInjectionScheme(void) const override; + // Methods to return BCID data virtual unsigned int getBeam1Bunches(const EventContext& ctx) const override; virtual unsigned int getBeam1Bunches(void) const override; @@ -84,8 +78,8 @@ class LHCDataTool: public extends<AthAlgTool, ILHCDataTool> { 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; + virtual unsigned int getCollidingBunches(const EventContext& ctx) const override; + virtual unsigned int getCollidingBunches(void) const override; // This returns a char for each BCID encoding beam1/beam2 // A colliding BCID will have value 3 @@ -95,7 +89,7 @@ class LHCDataTool: public extends<AthAlgTool, ILHCDataTool> { private: // Read Cond Handles - SG::ReadCondHandleKey<AthenaAttributeList> m_fillDataKey{this, "FillDataKey", "/LHC/FillData", "Key of fill data folder"}; + //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"}; diff --git a/Waveform/WaveformConditions/WaveformConditionsTools/python/WaveformRangeConfig.py b/Waveform/WaveformConditions/WaveformConditionsTools/python/WaveformRangeConfig.py index 6450bcec8..8ac69d912 100644 --- a/Waveform/WaveformConditions/WaveformConditionsTools/python/WaveformRangeConfig.py +++ b/Waveform/WaveformConditions/WaveformConditionsTools/python/WaveformRangeConfig.py @@ -20,8 +20,14 @@ def WaveformRangeCfg(flags, **kwargs): acc = ComponentAccumulator() # tool = kwargs.get("WaveformRangeTool", WaveformRangeTool(flags)) # Probably need to figure this out! - dbInstance = kwargs.get("dbInstance", "TDAQ_OFL") + + dbInstance = kwargs.get("dbInstance", "COOLOFL_DIGITIZER") dbFolder = kwargs.get("dbFolder", "/WAVE/DAQ/Range") - acc.merge(addFolders(flags, dbFolder, dbInstance, className="CondAttrListCollection")) + dbName = flags.IOVDb.DatabaseInstance # e.g. CONDBR3 + + # COOLOFL_DIGITIZER is not known to ATLAS IOVDBSvc + # Must use non-shorthand folder specification here + folder_string = f"<db>{dbInstance}/{dbName}</db> {dbFolder}" + acc.merge(addFolders(flags, folder_string, className="CondAttrListCollection")) return acc diff --git a/xAOD/xAODFaserLHC/Root/xAODFaserLHCDataAux_v1.cxx b/xAOD/xAODFaserLHC/Root/FaserLHCDataAux_v1.cxx similarity index 77% rename from xAOD/xAODFaserLHC/Root/xAODFaserLHCDataAux_v1.cxx rename to xAOD/xAODFaserLHC/Root/FaserLHCDataAux_v1.cxx index 37e37fa78..e9b981b33 100644 --- a/xAOD/xAODFaserLHC/Root/xAODFaserLHCDataAux_v1.cxx +++ b/xAOD/xAODFaserLHC/Root/FaserLHCDataAux_v1.cxx @@ -16,30 +16,39 @@ namespace xAOD { beamMode(""), beamType1(0), beamType2(0), - numBunchBeam1(0), - numBunchBeam2(0), - numBunchColliding(0), betaStar(0), crossingAngle(0), + stableBeams(false), injectionScheme(""), + numBunchBeam1(0), + numBunchBeam2(0), + numBunchColliding(0), distanceToCollidingBCID(0), distanceToUnpairedB1(0), - distanceToUnpairedB2(0) + distanceToUnpairedB2(0), + distanceToInboundB1(0), + distanceToTrainStart(0), + distanceToPreviousColliding(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( stableBeams ); AUX_VARIABLE( injectionScheme ); + AUX_VARIABLE( numBunchBeam1 ); + AUX_VARIABLE( numBunchBeam2 ); + AUX_VARIABLE( numBunchColliding ); AUX_VARIABLE( distanceToCollidingBCID ); AUX_VARIABLE( distanceToUnpairedB1 ); AUX_VARIABLE( distanceToUnpairedB2 ); + AUX_VARIABLE( distanceToInboundB1 ); + AUX_VARIABLE( distanceToTrainStart ); + AUX_VARIABLE( distanceToPreviousColliding ); + } } // namespace xAOD diff --git a/xAOD/xAODFaserLHC/Root/xAODFaserLHCData_v1.cxx b/xAOD/xAODFaserLHC/Root/FaserLHCData_v1.cxx similarity index 82% rename from xAOD/xAODFaserLHC/Root/xAODFaserLHCData_v1.cxx rename to xAOD/xAODFaserLHC/Root/FaserLHCData_v1.cxx index 05403bcfc..b09c8c85d 100644 --- a/xAOD/xAODFaserLHC/Root/xAODFaserLHCData_v1.cxx +++ b/xAOD/xAODFaserLHC/Root/FaserLHCData_v1.cxx @@ -34,24 +34,27 @@ namespace xAOD { 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, bool, + stableBeams, set_stableBeams ) + AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( FaserLHCData_v1, std::string, injectionScheme, set_injectionScheme ) + 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, int, distanceToCollidingBCID, set_distanceToCollidingBCID ) @@ -61,6 +64,17 @@ namespace xAOD { AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( FaserLHCData_v1, int, distanceToUnpairedB2, set_distanceToUnpairedB2 ) + AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( FaserLHCData_v1, int, + distanceToInboundB1, set_distanceToInboundB1 ) + + AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( FaserLHCData_v1, unsigned int, + distanceToTrainStart, set_distanceToTrainStart ) + + AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( FaserLHCData_v1, unsigned int, + distanceToPreviousColliding, set_distanceToPreviousColliding ) + + + } // namespace xAOD namespace xAOD { diff --git a/xAOD/xAODFaserLHC/xAODFaserLHC/versions/FaserLHCDataAux_v1.h b/xAOD/xAODFaserLHC/xAODFaserLHC/versions/FaserLHCDataAux_v1.h index c2daf68db..bc1034143 100644 --- a/xAOD/xAODFaserLHC/xAODFaserLHC/versions/FaserLHCDataAux_v1.h +++ b/xAOD/xAODFaserLHC/xAODFaserLHC/versions/FaserLHCDataAux_v1.h @@ -41,18 +41,24 @@ namespace xAOD { int beamType1; int beamType2; - unsigned int numBunchBeam1; - unsigned int numBunchBeam2; - unsigned int numBunchColliding; - float betaStar; float crossingAngle; + bool stableBeams; std::string injectionScheme; + unsigned int numBunchBeam1; + unsigned int numBunchBeam2; + unsigned int numBunchColliding; + int distanceToCollidingBCID; int distanceToUnpairedB1; int distanceToUnpairedB2; + int distanceToInboundB1; + + unsigned int distanceToTrainStart; + unsigned int distanceToPreviousColliding; + /// @} }; // class FaserLHCDataAuxContainer_v1 diff --git a/xAOD/xAODFaserLHC/xAODFaserLHC/versions/FaserLHCData_v1.h b/xAOD/xAODFaserLHC/xAODFaserLHC/versions/FaserLHCData_v1.h index 982ce1053..fd6c68cc0 100644 --- a/xAOD/xAODFaserLHC/xAODFaserLHC/versions/FaserLHCData_v1.h +++ b/xAOD/xAODFaserLHC/xAODFaserLHC/versions/FaserLHCData_v1.h @@ -31,6 +31,7 @@ namespace xAOD { /// First batch comes from ATLAS /LHC/DCS/FILLSTATE folder /// which is a copy of information sent to ATLAS by LHC over DIP + /// This is from the FASER COOL folder /LHC/BeamData /// LHC fill number unsigned int fillNumber() const; @@ -51,14 +52,6 @@ namespace xAOD { 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); @@ -67,14 +60,29 @@ namespace xAOD { float crossingAngle() const; void set_crossingAngle(float value); + // LHC stable beam flag + bool stableBeams() const; + void set_stableBeams(bool value); + // Injection scheme std::string injectionScheme() const; void set_injectionScheme(std::string value); // Second batch is information is related to the BCID structure + // This comes from the LPC filling scheme (nominal values) + // and is stored in /LHC/BCIDData + + /// 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); // 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); @@ -84,8 +92,24 @@ namespace xAOD { int distanceToUnpairedB2() const; void set_distanceToUnpairedB2(int value); - /// Collimator settings? + int distanceToInboundB1() const; + void set_distanceToInboundB1(int value); + + /// Distance of current BCID to start of previous bunch train + unsigned int distanceToTrainStart() const; + void set_distanceToTrainStart(unsigned int value); + + /// Distance of current BCID frmo previous colliding BCID (0 if this is colliding) + unsigned int distanceToPreviousColliding() const; + void set_distanceToPreviousColliding(unsigned int value); + /// Simple boolean functions to access useful values + /// In most cases, these check that the distance above is zero + bool isStable() const { return this->stableBeams(); } + bool isColliding() const { return this->distanceToCollidingBCID() == 0; } + bool isUnpairedBeam1() const { return this->distanceToUnpairedB1() == 0; } + bool isUnpairedBeam2() const { return this->distanceToUnpairedB2() == 0; } + bool isInboundBeam1() const { return this->distanceToInboundB1() == 0; } /// @} -- GitLab From ae3351dac147f542280f35877a26400a892ea030 Mon Sep 17 00:00:00 2001 From: Eric Torrence <eric.torrence@cern.ch> Date: Tue, 22 Nov 2022 09:16:45 +0100 Subject: [PATCH 3/4] Only turn on LHC data for 2022 TI12 data --- .../Reconstruction/scripts/faser_reco.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/Control/CalypsoExample/Reconstruction/scripts/faser_reco.py b/Control/CalypsoExample/Reconstruction/scripts/faser_reco.py index 4daa95ec8..72235c1f2 100755 --- a/Control/CalypsoExample/Reconstruction/scripts/faser_reco.py +++ b/Control/CalypsoExample/Reconstruction/scripts/faser_reco.py @@ -101,7 +101,11 @@ else: ConfigFlags.Input.ProjectName = "data20" ConfigFlags.GeoModel.Align.Dynamic = False +# Flags for later useCKF = True +useCal = False +useLHC = False + # Enable ACTS material corrections, this crashes testbeam geometries ConfigFlags.TrackingGeometry.MaterialSource = "/cvmfs/faser.cern.ch/repo/sw/database/DBRelease/current/acts/material-maps.json" @@ -115,6 +119,7 @@ elif runtype == "TestBeamData" or runtype == "TestBeamMC": ConfigFlags.GeoModel.FaserVersion = "FASER-TB00" ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-TB00" useCKF = False + useCal = True # New TI12 geometry (ugh) elif runtype == "TI12Data02": @@ -127,6 +132,8 @@ elif runtype == "TI12Data03": # ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02" # Use the updated field map ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-03" + useCal = True + useLHC = True else: print("Invalid run type found:", runtype) @@ -178,8 +185,9 @@ else: from FaserGeoModel.FaserGeoModelConfig import FaserGeometryCfg acc.merge(FaserGeometryCfg(ConfigFlags)) -from LHCDataAlgs.LHCDataAlgConfig import LHCDataAlgCfg -acc.merge(LHCDataAlgCfg(ConfigFlags)) +if useLHC: + from LHCDataAlgs.LHCDataAlgConfig import LHCDataAlgCfg + acc.merge(LHCDataAlgCfg(ConfigFlags)) # Set up algorithms from WaveRecAlgs.WaveRecAlgsConfig import WaveformReconstructionCfg @@ -189,7 +197,7 @@ acc.merge(WaveformReconstructionCfg(ConfigFlags)) if args.isMC: # Not ready for MC quite yet pass -else: +elif useCal: from CaloRecAlgs.CaloRecAlgsConfig import CalorimeterReconstructionCfg acc.merge(CalorimeterReconstructionCfg(ConfigFlags)) @@ -232,8 +240,6 @@ 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#*" @@ -241,6 +247,9 @@ itemList = [ "xAOD::EventInfo#*" , "TrackCollection#*" ] # +if useLHC: + itemList.extend( ["xAOD::FaserLHCData#*", "xAOD::FaserLHCDataAux#*"] ) + if args.isMC: # Make xAOD versions of truth from Reconstruction.xAODTruthCnvAlgConfig import xAODTruthCnvAlgCfg -- GitLab From a4ce4dd4106982be887d8da25e1629f021ef8507 Mon Sep 17 00:00:00 2001 From: Eric Torrence <eric.torrence@cern.ch> Date: Tue, 22 Nov 2022 09:26:11 +0100 Subject: [PATCH 4/4] Remove code moved to calibration package --- 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 ------------------ 6 files changed, 1435 deletions(-) delete mode 100644 LHCData/LHCDataUtils/CMakeLists.txt delete mode 100644 LHCData/LHCDataUtils/README.md delete mode 100644 LHCData/LHCDataUtils/python/CoolDataReader.py delete mode 100644 LHCData/LHCDataUtils/python/LumiBlobConversion.py delete mode 100644 LHCData/LHCDataUtils/python/LumiDBHandler.py delete mode 100755 LHCData/LHCDataUtils/scripts/makeLHCFillData.py diff --git a/LHCData/LHCDataUtils/CMakeLists.txt b/LHCData/LHCDataUtils/CMakeLists.txt deleted file mode 100644 index 2c4494950..000000000 --- a/LHCData/LHCDataUtils/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -################################################################################ -# 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 deleted file mode 100644 index 0ca447fe3..000000000 --- a/LHCData/LHCDataUtils/README.md +++ /dev/null @@ -1 +0,0 @@ -Utilities to produce and update COOL databases for LHC information diff --git a/LHCData/LHCDataUtils/python/CoolDataReader.py b/LHCData/LHCDataUtils/python/CoolDataReader.py deleted file mode 100644 index 238f8987c..000000000 --- a/LHCData/LHCDataUtils/python/CoolDataReader.py +++ /dev/null @@ -1,181 +0,0 @@ -# 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 deleted file mode 100644 index bf57d1d28..000000000 --- a/LHCData/LHCDataUtils/python/LumiBlobConversion.py +++ /dev/null @@ -1,388 +0,0 @@ -# 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 deleted file mode 100644 index 7e6d5619b..000000000 --- a/LHCData/LHCDataUtils/python/LumiDBHandler.py +++ /dev/null @@ -1,121 +0,0 @@ -# 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 deleted file mode 100755 index f0187ab20..000000000 --- a/LHCData/LHCDataUtils/scripts/makeLHCFillData.py +++ /dev/null @@ -1,734 +0,0 @@ -#!/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() -- GitLab