From b76c563946dcd4c1aba0ec2cbff81d54d3001ed5 Mon Sep 17 00:00:00 2001
From: Joerg Stelzer <joerg.stelzer@cern.ch>
Date: Mon, 7 Sep 2020 18:00:54 +0000
Subject: [PATCH] Added Condalg to provide L1Prescales

This is needed for instance for writing the xAOD trigger metadata (ATR-21373)
---
 .../TrigConfigSvc/python/TrigConfigSvcCfg.py  |  29 +++
 .../TrigConfigSvc/src/L1PrescaleCondAlg.cxx   | 189 ++++++++++++++++++
 .../TrigConfigSvc/src/L1PrescaleCondAlg.h     |  66 ++++++
 .../src/components/TrigConfigSvc_entries.cxx  |   2 +
 4 files changed, 286 insertions(+)
 create mode 100644 Trigger/TrigConfiguration/TrigConfigSvc/src/L1PrescaleCondAlg.cxx
 create mode 100644 Trigger/TrigConfiguration/TrigConfigSvc/src/L1PrescaleCondAlg.h

diff --git a/Trigger/TrigConfiguration/TrigConfigSvc/python/TrigConfigSvcCfg.py b/Trigger/TrigConfiguration/TrigConfigSvc/python/TrigConfigSvcCfg.py
index 81e31ee22aa..21a01776c99 100644
--- a/Trigger/TrigConfiguration/TrigConfigSvc/python/TrigConfigSvcCfg.py
+++ b/Trigger/TrigConfiguration/TrigConfigSvc/python/TrigConfigSvcCfg.py
@@ -7,6 +7,9 @@ from AthenaConfiguration.ComponentFactory import CompFactory
 from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator
 import json
 
+def getL1PrescaleFolderName():
+    return "/TRIGGER/LVL1/Lvl1ConfigKey <tag>HEAD</tag>"
+
 def getHLTPrescaleFolderName():
     return "/TRIGGER/HLT/PrescaleKey <tag>HEAD</tag>"
 
@@ -193,6 +196,32 @@ def TrigConfigSvcCfg( flags ):
     acc.addService( getHLTConfigSvc( flags ) )
     return acc
 
+def L1PrescaleCondAlgCfg( flags ):
+    log = logging.getLogger('TrigConfigSvcCfg')
+    log.info("Setting up L1PrescaleCondAlg")
+    from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator
+    acc = ComponentAccumulator()
+    TrigConf__L1PrescaleCondAlg = CompFactory.getComp("TrigConf::L1PrescaleCondAlg")
+    l1PrescaleCondAlg = TrigConf__L1PrescaleCondAlg("L1PrescaleCondAlg")
+
+    tc = getTrigConfigFromFlag( flags )
+    l1PrescaleCondAlg.Source = tc["source"]
+    from AthenaCommon.AthenaCommonFlags import athenaCommonFlags
+    if athenaCommonFlags.isOnline():
+        from IOVDbSvc.IOVDbSvcConfig import addFolders
+        acc.merge(addFolders(flags, getL1PrescaleFolderName(), "TRIGGER_ONL", className="AthenaAttributeList"))
+        log.info("Adding folder %s to CompAcc", getL1PrescaleFolderName() )
+    if tc["source"] == "COOL":
+        l1PrescaleCondAlg.TriggerDB = tc["dbconn"]
+    elif tc["source"] == "DB":
+        l1PrescaleCondAlg.TriggerDB = tc["dbconn"]
+        l1PrescaleCondAlg.L1Psk    = tc["l1psk"]
+    elif tc["source"] == "FILE":
+        l1PrescaleCondAlg.Filename = getL1PrescalesSetFileName( flags )
+    else:
+        raise RuntimeError("trigger configuration flag 'trigConfig' starts with %s, which is not understood" % tc["source"])
+    acc.addCondAlgo(l1PrescaleCondAlg)
+    return acc
 
 def HLTPrescaleCondAlgCfg( flags ):
     log = logging.getLogger('TrigConfigSvcCfg')
diff --git a/Trigger/TrigConfiguration/TrigConfigSvc/src/L1PrescaleCondAlg.cxx b/Trigger/TrigConfiguration/TrigConfigSvc/src/L1PrescaleCondAlg.cxx
new file mode 100644
index 00000000000..1925d6ba680
--- /dev/null
+++ b/Trigger/TrigConfiguration/TrigConfigSvc/src/L1PrescaleCondAlg.cxx
@@ -0,0 +1,189 @@
+/*
+  Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+*/ 
+
+#include "./L1PrescaleCondAlg.h"
+#include "TrigConfIO/TrigDBL1PrescalesSetLoader.h"
+#include "TrigConfIO/JsonFileLoader.h"
+#include "TrigConfInterfaces/IJobOptionsSvc.h"
+
+#include "CoolKernel/types.h"
+
+#include <memory>
+
+TrigConf::L1PrescaleCondAlg::L1PrescaleCondAlg(const std::string& name, ISvcLocator* pSvcLocator) :
+   AthReentrantAlgorithm(name, pSvcLocator)
+{}
+
+
+std::shared_ptr<TrigConf::L1PrescalesSet>
+TrigConf::L1PrescaleCondAlg::createFromFile( const std::string & filename ) const {
+   auto pss = std::make_shared<L1PrescalesSet>();
+   // load the file into the L1 prescales set
+   ATH_MSG_DEBUG( "Setting up JsonFileLoader with file " << filename );
+   TrigConf::JsonFileLoader psLoader;
+   psLoader.setLevel(TrigConf::MSGTC::WARNING); 
+   ATH_MSG_DEBUG( "Going to load prescales" );
+   if( psLoader.loadFile( filename, *pss) ) {
+      ATH_MSG_INFO( "L1 prescales set successfully loaded from file " << filename );
+   } else {
+      ATH_MSG_WARNING( "Failed loading L1 prescales set from file " << filename ); // will be made an error later
+      pss = nullptr;
+   }
+   return pss;
+}
+
+
+std::shared_ptr<TrigConf::L1PrescalesSet>
+TrigConf::L1PrescaleCondAlg::createFromDB( unsigned int psk, bool isRun3 ) const {
+   if( ! isRun3 ) {
+      ATH_MSG_WARNING( "Currently it is not possible to load run 2 prescale sets from the database. Will not load L1 psk " << psk ); 
+      return nullptr;
+   }
+   auto pss = std::make_shared<L1PrescalesSet>();
+   // load the L1 psk into the L1 prescales set
+   ATH_MSG_DEBUG( "Setting up TrigDBL1PrescalesSetLoader with DB connection " << m_dbConnection.value() );
+   TrigConf::TrigDBL1PrescalesSetLoader psLoader(m_dbConnection);
+   psLoader.setLevel(TrigConf::MSGTC::WARNING); 
+   ATH_MSG_DEBUG( "Going to load prescales" );
+   try {
+      psLoader.loadL1Prescales( psk, *pss );
+   }
+   catch(std::exception & e) {
+      ATH_MSG_WARNING( "Failed loading L1 prescales set from db with key " << psk ); // will be made an error later
+      ATH_MSG_WARNING( e.what() );
+      pss = nullptr;
+   }
+   return pss;
+}
+
+
+
+StatusCode
+TrigConf::L1PrescaleCondAlg::initialize() {
+
+   ATH_MSG_DEBUG("L1PrescaleCondAlg::initialize()");
+
+   ATH_CHECK(m_pskFolderInputKey.initialize());
+   if( m_configSource == "FILE" || m_configSource == "DB" ) {
+      renounce( m_pskFolderInputKey ); // need to disable updates when the COOL folder changes
+   }
+
+   ATH_CHECK(m_l1PrescalesSetOutputKey.initialize());
+
+   if( m_configSource == "COOL" && m_dbConnection == "JOSVC" ) {
+      if( auto joSvc = serviceLocator()->service<TrigConf::IJobOptionsSvc>( "JobOptionsSvc" ) ) {
+         if( joSvc->l1PrescaleKey()>0 ) {
+            m_psk = joSvc->l1PrescaleKey();
+            m_dbConnection = joSvc->server();
+            ATH_MSG_INFO("Set psk to " << m_psk <<  " and db connection to " << m_dbConnection );
+         }
+      } else {
+         ATH_MSG_DEBUG("Did not locate TrigConf::IJobOptionsSvc");
+      }
+   }
+
+   ATH_MSG_INFO(m_configSource);
+   ATH_MSG_INFO(m_dbConnection);
+   ATH_MSG_INFO(m_psk);
+   ATH_MSG_INFO(m_filename);
+
+   if( m_configSource == "FILE" ) {
+
+      // index 0 indicates that the configuration is from a file, a DB
+      // PSK is greater than 0
+      m_pssMap[0] = createFromFile( m_filename );
+
+   } else if( m_psk != 0u ) {
+
+      // this is for the case where the reading from the DB was
+      // configured and also when we read from COOL online and get a
+      // PSK through the JobOptionsSvc
+      m_pssMap[m_psk] = createFromDB( m_psk, true );
+
+   }
+
+   return StatusCode::SUCCESS;
+}
+
+StatusCode
+TrigConf::L1PrescaleCondAlg::execute(const EventContext& ctx) const {
+
+   ATH_MSG_DEBUG("L1PrescaleCondAlg::execute with lb " << ctx.eventID().lumi_block());
+
+   unsigned int l1Psk = m_psk;
+   EventIDRange range;
+
+   if(m_configSource == "COOL") {
+      // get prescale key and range from COOL
+      SG::ReadCondHandle<AthenaAttributeList> readH(m_pskFolderInputKey, ctx);
+      const AthenaAttributeList * pskAL{ *readH };
+      if ( pskAL == nullptr ) {
+         ATH_MSG_FATAL("Null pointer to the read conditions object of " << m_pskFolderInputKey.key());
+         return StatusCode::FAILURE;
+      }
+      if (not readH.range(range)) {
+         ATH_MSG_FATAL("Failed to retrieve validity range for " << readH.key());
+         return StatusCode::FAILURE;
+      } else {
+         ATH_MSG_DEBUG("Read handle has range " << range);
+      }
+      // get the prescale key from the cool folder 
+      l1Psk = (*pskAL)["Lvl1PrescaleConfigurationKey"].data<cool::UInt32>();
+      ATH_MSG_INFO( "Extracted the L1 PSK " << l1Psk << " for run " << ctx.eventID().run_number()
+                     << " and lb " << ctx.eventID().lumi_block() );
+   } else {
+
+      // in case of reading from DB or from FILE, the EventID range is always the full run
+      EventIDBase::number_type run = ctx.eventID().run_number();
+      EventIDBase start, stop;
+      start.set_run_number(run);
+      start.set_lumi_block(0);
+      stop.set_run_number(run+1);
+      stop.set_lumi_block(0);
+      range = EventIDRange(start,stop);
+
+   }
+
+   std::shared_ptr<L1PrescalesSet> pss;
+
+   if( m_configSource == "FILE" ) {
+
+      pss = m_pssMap.at(0);
+
+   } else if ( l1Psk != 0 ) {
+
+      auto pssi = m_pssMap.find( l1Psk );
+
+      if( pssi == m_pssMap.end()) {
+
+         bool isRun3 = range.start().run_number()>350000;
+
+         pss = m_pssMap[l1Psk] = createFromDB( l1Psk, isRun3 );
+
+      } else {
+      
+         pss = pssi->second;
+
+      }
+
+   } else {
+
+      ATH_MSG_ERROR( "Failed loading L1 prescales set (not reading from FILE and no psk known)" );
+      return StatusCode::FAILURE;
+
+   }
+
+   // record L1 prescales set
+   SG::WriteCondHandle<TrigConf::L1PrescalesSet> writeCondHandle(m_l1PrescalesSetOutputKey, ctx);
+
+   if( pss == nullptr ) {
+      ATH_MSG_INFO("Recording empty L1 prescales set with range " << range);
+      ATH_CHECK( writeCondHandle.record( range, new L1PrescalesSet ) );
+   } else {
+      ATH_MSG_INFO("Recording L1 prescales set with range " << range);
+      ATH_CHECK( writeCondHandle.record( range, new L1PrescalesSet(*pss) ) );
+   }
+
+   return StatusCode::SUCCESS;
+}
diff --git a/Trigger/TrigConfiguration/TrigConfigSvc/src/L1PrescaleCondAlg.h b/Trigger/TrigConfiguration/TrigConfigSvc/src/L1PrescaleCondAlg.h
new file mode 100644
index 00000000000..ee0743d212b
--- /dev/null
+++ b/Trigger/TrigConfiguration/TrigConfigSvc/src/L1PrescaleCondAlg.h
@@ -0,0 +1,66 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/ 
+
+#ifndef TRIGCONFIGSVC__L1PRESCALECONDALG
+#define TRIGCONFIGSVC__L1PRESCALECONDALG
+
+#include "AthenaBaseComps/AthReentrantAlgorithm.h"
+
+#include "StoreGate/ReadCondHandleKey.h"
+#include "StoreGate/WriteCondHandleKey.h"
+#include "TrigConfData/L1PrescalesSet.h"
+
+#include "AthenaPoolUtilities/AthenaAttributeList.h"
+
+#include <tbb/concurrent_unordered_map.h>
+
+#include <memory>
+
+namespace TrigConf {
+
+   /**
+    * @brief Condition algorithm to provide the L1 trigger menu
+    *
+    * The algorithm will provide the menu configuration through a conditions handle using \c TrigConf::L1Menu to present the data
+    *
+    * It is able to load the configuration in the following 3 ways
+    * * File-based menu description: for most test environments: for test jobs with no database involved
+    * * DB-based menu description where the DB keys are provided as algorithm job-properties: for reprocessing grid jobs with new menus
+    * * DB-based menu description where the DB keys are taken from COOL: for offline reconstruction jobs
+    */
+   class L1PrescaleCondAlg : public AthReentrantAlgorithm {
+   public:
+      L1PrescaleCondAlg(const std::string& name, ISvcLocator* pSvcLocator);
+      virtual ~L1PrescaleCondAlg() = default;
+
+      virtual StatusCode initialize() override;
+      virtual StatusCode execute(const EventContext& ctx) const override;
+
+   private:
+      
+      // helper function to load a L1 prescales set from a file
+      std::shared_ptr<L1PrescalesSet> createFromFile( const std::string & filename ) const;
+
+      // helper function to load a L1 prescales set from a prescale key
+      std::shared_ptr<L1PrescalesSet> createFromDB( unsigned int psk, bool isRun3 ) const;
+
+      // map the prescale key to a L1PrescalesSet
+      mutable tbb::concurrent_unordered_map<unsigned int, std::shared_ptr<L1PrescalesSet>> m_pssMap;
+
+      // input key to the L1 Prescale Key folder
+      SG::ReadCondHandleKey<AthenaAttributeList> m_pskFolderInputKey{ this, "PSKFolder", "/TRIGGER/LVL1/Lvl1ConfigKey", "SG Key of AthenaAttributeList containing l1 psk"};
+
+      // output key to store the L1PrescalesSet
+      SG::WriteCondHandleKey<TrigConf::L1PrescalesSet> m_l1PrescalesSetOutputKey{ this, "L1Prescales", "L1Prescales", "L1 prescales"};
+
+      // properties
+      Gaudi::Property< std::string > m_configSource { this, "Source", "FILE", "Configuration source, can be 'FILE', 'DB', or 'COOL'" };
+      Gaudi::Property< std::string > m_dbConnection { this, "TriggerDB", "", "DB connection alias or 'JOSVC', used when property Source set to 'DB' or set to 'COOL'." };
+      Gaudi::Property< unsigned int > m_psk { this, "L1Psk", 0, "L1 prescale key, used when property 'Source' set to 'DB'" };
+      Gaudi::Property< std::string > m_filename { this, "Filename", "", "L1 prescale json file, used when property 'Source' set to 'FILE'" };
+
+   };
+
+}
+#endif
diff --git a/Trigger/TrigConfiguration/TrigConfigSvc/src/components/TrigConfigSvc_entries.cxx b/Trigger/TrigConfiguration/TrigConfigSvc/src/components/TrigConfigSvc_entries.cxx
index 23687473007..2a8fb252576 100644
--- a/Trigger/TrigConfiguration/TrigConfigSvc/src/components/TrigConfigSvc_entries.cxx
+++ b/Trigger/TrigConfiguration/TrigConfigSvc/src/components/TrigConfigSvc_entries.cxx
@@ -5,6 +5,7 @@
 #include "../TrigConfJobOptionsSvc.h"
 #include "../TrigConfigSvc.h"
 
+#include "../L1PrescaleCondAlg.h"
 #include "../HLTPrescaleCondAlg.h"
 
 DECLARE_COMPONENT( TrigConf::L1TopoConfigSvc )
@@ -14,4 +15,5 @@ DECLARE_COMPONENT( TrigConf::JobOptionsSvc )
 DECLARE_COMPONENT( TrigConf::DSConfigSvc )
 DECLARE_COMPONENT( TrigConf::TrigConfigSvc )
 
+DECLARE_COMPONENT( TrigConf::L1PrescaleCondAlg )
 DECLARE_COMPONENT( TrigConf::HLTPrescaleCondAlg )
-- 
GitLab