diff --git a/Control/AthenaServices/CMakeLists.txt b/Control/AthenaServices/CMakeLists.txt index a38c8d4836cfde4cb27e30b966db0931aded5986..c05e525daa6408a9e96371a179bcc9aa400c852a 100644 --- a/Control/AthenaServices/CMakeLists.txt +++ b/Control/AthenaServices/CMakeLists.txt @@ -87,6 +87,12 @@ atlas_add_test( DelayedConditionsCleanerSvc_test EXTRA_PATTERNS "^JobOptionsSvc +INFO|^//GP:=" ENVIRONMENT "JOBOPTSEARCHPATH=${CMAKE_CURRENT_SOURCE_DIR}/share" ) +atlas_add_test( ThinningCacheTool_test + SOURCES test/ThinningCacheTool_test.cxx + LINK_LIBRARIES TestTools AsgTools AthenaKernel GaudiKernel EventInfo AthenaBaseComps + EXTRA_PATTERNS "^JobOptionsSvc +INFO|^//GP:=" + ENVIRONMENT "JOBOPTSEARCHPATH=${CMAKE_CURRENT_SOURCE_DIR}/share" ) + atlas_add_test( TestStopRun SCRIPT test/TestStopRun.sh EXTRA_PATTERNS "Warning in <TFile::Init>: no StreamerInfo found|SGAudSvc +INFO Finalizing|SGAudSvc +INFO Initializing|^Py:Athena +INFO executing ROOT6Setup|No duplicates have been found|duplicate entry.*ignored|^Py:ConfigurableDb WARNING|Read module info for|^ToolSvc.* INFO( finalize:)? Removing all tools|^CoreDumpSvc *INFO Handling signals|types added|including file|^(Mon|Tue|Wed|Thu|Fri|Sat|Sun) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)|local .* libGaudiKernelDict.so|^Number of My|^Py:Athena +INFO using release|^StoreGateSvc +INFO Start|^Py:AthenaDsoDb +INFO could not install alias|Bindings.py:660newobj = object.__new__|Updating ROOT::Reflex::PluginService::SetDebug|^ApplicationMgr +INFO|^StoreGateSvc +INFO" diff --git a/Control/AthenaServices/share/ThinningCacheTool_test.ref b/Control/AthenaServices/share/ThinningCacheTool_test.ref new file mode 100644 index 0000000000000000000000000000000000000000..a79dae8c34641bd562e1e6f59a6982c7fb6f45bf --- /dev/null +++ b/Control/AthenaServices/share/ThinningCacheTool_test.ref @@ -0,0 +1,17 @@ +AthenaServices/ThinningCacheTool_test + + +Initializing Gaudi ApplicationMgr using job opts ../share/ThinningCacheTool_test.txt +JobOptionsSvc INFO Job options successfully read in from ../share/ThinningCacheTool_test.txt +ApplicationMgr SUCCESS +==================================================================================================================================== + Welcome to ApplicationMgr (GaudiCoreSvc v27r1p99) + running on karma on Thu Oct 24 11:24:46 2019 +==================================================================================================================================== +ApplicationMgr INFO Application Manager Configured successfully +EventLoopMgr WARNING Unable to locate service "EventSelector" +EventLoopMgr WARNING No events will be processed from external input. +HistogramPersis...WARNING Histograms saving not required. +ApplicationMgr INFO Application Manager Initialized successfully +ApplicationMgr Ready +test1 diff --git a/Control/AthenaServices/share/ThinningCacheTool_test.txt b/Control/AthenaServices/share/ThinningCacheTool_test.txt new file mode 100644 index 0000000000000000000000000000000000000000..31822146db0894f48015b2a1d607a62f56a0a0ba --- /dev/null +++ b/Control/AthenaServices/share/ThinningCacheTool_test.txt @@ -0,0 +1,3 @@ +// job opts for ThinningCacheTool test + +ToolSvc.TestCacheTool.StreamName = "MyStream"; diff --git a/Control/AthenaServices/src/ThinningCacheTool.cxx b/Control/AthenaServices/src/ThinningCacheTool.cxx new file mode 100644 index 0000000000000000000000000000000000000000..0f02690c5a0f8c9059fc6e7c269b7bd488d9ac66 --- /dev/null +++ b/Control/AthenaServices/src/ThinningCacheTool.cxx @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration. + */ +/** + * @file AthenaServices/src/ThinningCacheTool.h + * @author scott snyder <snyder@bnl.gov> + * @date Aug, 2019 + * @brief Create ThinningCache for a given stream. + */ + + +#include "ThinningCacheTool.h" +#include "AthContainers/ThinningDecision.h" +#include "SGTools/DataProxy.h" +#include "AthenaKernel/IProxyDict.h" +#include "AthenaKernel/ExtendedEventContext.h" +#include "GaudiKernel/ThreadLocalContext.h" + + +namespace Athena { + + +/** + * @brief Called at the end of initialize. A no-op here. + */ +StatusCode ThinningCacheTool::postInitialize() +{ + return StatusCode::SUCCESS; +} + + +StatusCode ThinningCacheTool::preExecute() +{ + m_cache.clear(); + + // Examine all ThinningDecision objects in the store. + SG::ConstIterator<SG::ThinningDecision> beg; + SG::ConstIterator<SG::ThinningDecision> end; + // Will return FAILURE if no matches, so ignore. + evtStore()->retrieve<SG::ThinningDecision>(beg, end).ignore(); + for (; beg != end; ++beg) + { + // See if this is for this stream. + std::string key = wantedKey (beg.key()); + if (!key.empty()) { + + // Get the set of hashed SG keys for this object. + std::vector<SG::sgkey_t> sgkeys; + SG::DataProxy* proxy = beg->link().proxy (true); + if (proxy) { + sgkeys.push_back (proxy->sgkey()); + CLID pCLID = proxy->clID(); + IProxyDict* store = proxy->store(); + for (CLID clid : proxy->transientID()) { + if (clid != pCLID) { + sgkeys.push_back (store->stringToKey (key, clid)); + } + } + } + + // Add it to the cache. + m_cache.addThinning (key, sgkeys, &*beg); + } + } + + // If there was any thinning for this stream, then install the cache + // in the EventContext. + if (!m_cache.empty()) { + m_cache.lockOwned(); + EventContext ctx = Gaudi::Hive::currentContext(); + Atlas::getExtendedEventContext (ctx).setThinningCache (&m_cache); + Gaudi::Hive::setCurrentContext (ctx); + } + + return StatusCode::SUCCESS; +} + + +/** + * @brief Called at the end of execute. + * Remove ThinningCache from the EventContext and clear the cache instance. + */ +StatusCode ThinningCacheTool::postExecute() +{ + if (!m_cache.empty()) { + EventContext ctx = Gaudi::Hive::currentContext(); + Atlas::getExtendedEventContext (ctx).setThinningCache (nullptr); + Gaudi::Hive::setCurrentContext (ctx); + } + + m_cache.clear(); + return StatusCode::SUCCESS; +} + + +/** + * @brief Called at the beginning of finalize. A no-op here. + */ +StatusCode ThinningCacheTool::preFinalize() +{ + return StatusCode::SUCCESS; +} + + +/** + * @brief Test if a thinning decision object is for our stream. + * @param key The SG key of the @c ThinningDecision object. + * + * If this thinning decision is for our stream, then return the SG key + * of the object being thinned. Otherwise, return an empty string. + */ +std::string ThinningCacheTool::wantedKey (const std::string& key) const +{ + std::string suffix = "_THINNED_" + m_streamName; + std::string::size_type spos = key.find (suffix); + if (spos != std::string::npos && + (spos + suffix.size() == key.size() || + key[spos + suffix.size()] == '.')) + { + return key.substr (0, spos); + } + return ""; +} + + +} // namespace Athena + diff --git a/Control/AthenaServices/src/ThinningCacheTool.h b/Control/AthenaServices/src/ThinningCacheTool.h new file mode 100644 index 0000000000000000000000000000000000000000..e7092cb7142e8b10b1baa0bfbceb4e4ecc1e5c76 --- /dev/null +++ b/Control/AthenaServices/src/ThinningCacheTool.h @@ -0,0 +1,91 @@ +// This file's extension implies that it's C, but it's really -*- C++ -*-. +/* + * Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration. + */ +/** + * @file AthenaServices/src/ThinningCacheTool.h + * @author scott snyder <snyder@bnl.gov> + * @date Aug, 2019 + * @brief Create ThinningCache for a given stream. + */ + + +#ifndef ATHENASERVICES_THINNINGCACHETOOL_H +#define ATHENASERVICES_THINNINGCACHETOOL_H + + +#include "AthenaBaseComps/AthAlgTool.h" +#include "AthenaKernel/ThinningCache.h" +#include "AthenaKernel/IAthenaOutputTool.h" + + +namespace Athena { + + +/** + * @brief Create ThinningCache for a given stream. + * + * Hooks in this class are called before and after output is written for a given + * stream. The @c preExecute hook will query the event store for any thinning + * to be done for the stream, build a ThinningCache object for all of these, + * and install it in the current EventContext. The @c postExecute hook + * will then clear the cache from the EventContext. + */ +class ThinningCacheTool : public extends<AthAlgTool, IAthenaOutputTool> +{ +public: + using base_class::base_class; + + + /** + * @brief Called at the end of initialize. A no-op here. + */ + virtual StatusCode postInitialize() override; + + + /** + * @brief Called at the beginning of execute. + * Find all thinning requests for this stream, build the @c ThinningCache, + * and install it in the current @c EventContext. + */ + virtual StatusCode preExecute() override; + + + /** + * @brief Called at the end of execute. + * Remove ThinningCache from the EventContext and clear the cache instance. + */ + virtual StatusCode postExecute() override; + + + /** + * @brief Called at the beginning of finalize. A no-op here. + */ + virtual StatusCode preFinalize() override; + + +private: + /** + * @brief Test if a thinning decision object is for our stream. + * @param key The SG key of the @c ThinningDecision object. + * + * If this thinning decision is for our stream, then return the SG key + * of the object being thinned. Otherwise, return an empty string. + */ + std::string wantedKey (const std::string& key) const; + + + /// Name of the stream for this tool. + StringProperty m_streamName + { this, "StreamName", "", "Name of the stream being written." }; + + + /// Thinning cache instance for this stream. + SG::ThinningCache m_cache; +}; + + +} // namespace Athena + + +#endif // not ATHENASERVICES_THINNINGCACHETOOL_H diff --git a/Control/AthenaServices/src/components/AthenaServices_entries.cxx b/Control/AthenaServices/src/components/AthenaServices_entries.cxx index 70a66efc0e4291e19a4e2380ca7b057d4e3121a4..8e6b28eb73f8362c5ef228c41fab64111a824bd7 100644 --- a/Control/AthenaServices/src/components/AthenaServices_entries.cxx +++ b/Control/AthenaServices/src/components/AthenaServices_entries.cxx @@ -8,6 +8,7 @@ #include "../MixingEventSelector.h" #include "../ThinningSvc.h" #include "../ThinningOutputTool.h" +#include "../ThinningCacheTool.h" //#include "../EventDumperSvc.h" #include "../MemoryRescueSvc.h" #include "../FPEControlSvc.h" @@ -67,6 +68,7 @@ DECLARE_COMPONENT( MetaDataSvc ) DECLARE_COMPONENT( OutputStreamSequencerSvc ) DECLARE_COMPONENT( AthenaOutputStreamTool ) DECLARE_COMPONENT( ThinningOutputTool ) +DECLARE_COMPONENT( Athena::ThinningCacheTool ) DECLARE_COMPONENT( AthenaStopperAlg ) DECLARE_COMPONENT( AthIncFirerAlg ) DECLARE_COMPONENT( ToyNextPassFilterAlg ) diff --git a/Control/AthenaServices/test/ThinningCacheTool_test.cxx b/Control/AthenaServices/test/ThinningCacheTool_test.cxx new file mode 100644 index 0000000000000000000000000000000000000000..80a356e9335e3fe3137bec9804c7d69fc39fdca5 --- /dev/null +++ b/Control/AthenaServices/test/ThinningCacheTool_test.cxx @@ -0,0 +1,124 @@ +/* + Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration +*/ +/** + * @file AthenaServices/test/ThinningCacheTool_test.cxx + * @author scott snyder + * @date Oct 2019 + * @brief Test for ThinningCacheTool. + */ + + +#undef NDEBUG +#include "StoreGate/StoreGateSvc.h" +#include "AthContainers/DataVector.h" +#include "AthContainers/ThinningDecision.h" +#include "AthenaKernel/ExtendedEventContext.h" +#include "AthenaKernel/IAthenaOutputTool.h" +#include "AthenaKernel/getThinningCache.h" +#include "AthenaKernel/CLASS_DEF.h" +#include "TestTools/initGaudi.h" +#include "GaudiKernel/ToolHandle.h" +#include "GaudiKernel/ServiceHandle.h" +#include "GaudiKernel/ThreadLocalContext.h" +#include <iostream> +#include <cassert> + + +class Test {}; +CLASS_DEF (DataVector<Test>, 29384894, 0) + + +void thin (SG::ThinningDecisionBase& dec, unsigned int mask) +{ + for (size_t i = 0; mask != 0; i++, mask>>=1) { + if ((mask & 1) != 0) { + dec.thin (i); + } + } +} + + +unsigned int thinning (const SG::ThinningDecisionBase& dec) +{ + unsigned int mask = 0; + for (size_t i = 0; i < dec.size(); i++) { + if (dec.thinned(i)) { + mask |= (1<<i); + } + } + return mask; +} + + +void test1() +{ + std::cout << "test1\n"; + + ToolHandle<IAthenaOutputTool> tool ("Athena::ThinningCacheTool/TestCacheTool"); + assert (tool.retrieve().isSuccess()); + + ServiceHandle<StoreGateSvc> sg ("StoreGateSvc", "test"); + assert (sg.retrieve().isSuccess()); + + assert (tool->preExecute().isSuccess()); + assert (SG::getThinningCache() == nullptr); + assert (tool->postExecute().isSuccess()); + assert (SG::getThinningCache() == nullptr); + + assert( sg->record (std::make_unique<DataVector<Test> >(16), "v1", false).isSuccess() ); + assert( sg->record (std::make_unique<DataVector<Test> >(16), "v2", false).isSuccess() ); + + auto d1 = std::make_unique<SG::ThinningDecision> ("v1"); + auto d2 = std::make_unique<SG::ThinningDecision> ("v2"); + auto d3 = std::make_unique<SG::ThinningDecision> ("v2"); + + thin (*d1, 0x5555); + thin (*d2, 0x6753); + thin (*d3, 0x7234); + + const SG::ThinningDecisionBase* d1p = d1.get(); + const SG::ThinningDecisionBase* d2p = d2.get(); + const SG::ThinningDecisionBase* d3p = d3.get(); + + assert( sg->record (std::move (d1), "v1_THINNED_MyStream", false).isSuccess() ); + assert( sg->record (std::move (d2), "v2_THINNED_MyStream.a", false).isSuccess() ); + assert( sg->record (std::move (d3), "v2_THINNED_MyStream.b", false).isSuccess() ); + + assert (tool->preExecute().isSuccess()); + assert (SG::getThinningCache() != nullptr); + + assert (SG::getThinningDecision ("v1") == d1p); + assert (SG::getThinningDecision ("v2") != nullptr); + assert (SG::getThinningDecision ("v2") != d2p); + assert (SG::getThinningDecision ("v2") != d3p); + + assert (thinning (*SG::getThinningDecision ("v1")) == 0x5555); + assert (thinning (*SG::getThinningDecision ("v2")) == 0x6210); + assert (SG::getThinningDecision ("v3") == nullptr); + + assert (SG::getThinningDecision ("v1")->index (7) == 3); + assert (SG::getThinningDecision ("v2")->index (7) == 6); + + assert (tool->postExecute().isSuccess()); + assert (SG::getThinningCache() == nullptr); +} + + +int main() +{ + std::cout << "AthenaServices/ThinningCacheTool_test\n"; + ISvcLocator* svcloc = nullptr; + if (!Athena_test::initGaudi("ThinningCacheTool_test.txt", svcloc)) { + std::cerr << "This test can not be run" << std::endl; + return 1; + } + assert(svcloc); + + EventContext ctx; + Atlas::setExtendedEventContext (ctx, Atlas::ExtendedEventContext()); + Gaudi::Hive::setCurrentContext (ctx); + + test1(); + return 0; +}