diff --git a/Control/AthToolSupport/AsgTools/AsgTools/SgTEventMeta.h b/Control/AthToolSupport/AsgTools/AsgTools/SgTEventMeta.h index 2fbc0a931d0580b5261cf44fd96c56bcf0637568..567184dfae0d516dd942bcae2a6c33dc621b09ae 100644 --- a/Control/AthToolSupport/AsgTools/AsgTools/SgTEventMeta.h +++ b/Control/AthToolSupport/AsgTools/AsgTools/SgTEventMeta.h @@ -13,6 +13,9 @@ # error "This header should not be used in Athena" #endif // XAOD_STANDALONE +#include <memory> +#include <vector> + // Forward declaration(s): namespace xAOD { class TEvent; diff --git a/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/ATLAS_CHECK_THREAD_SAFETY b/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/ATLAS_CHECK_THREAD_SAFETY new file mode 100644 index 0000000000000000000000000000000000000000..e6ccd312a2759d50dedd9b132eec468edec1bc13 --- /dev/null +++ b/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/ATLAS_CHECK_THREAD_SAFETY @@ -0,0 +1 @@ +PhysicsAnalysis/D3PDTools/AnaAlgorithm diff --git a/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/AlgorithmWorkerData.h b/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/AlgorithmWorkerData.h index 714967b7b8bd3e73bdf740edbda481b14d43023e..d99eb32b1c54f8478e599fd3d02e8dc1f0a066b7 100644 --- a/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/AlgorithmWorkerData.h +++ b/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/AlgorithmWorkerData.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration + Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration */ /// @author Nils Krumnack @@ -10,8 +10,10 @@ #define ANA_ALGORITHM__ALGORITHM_WORKER_DATA_H #ifndef ROOTCORE +#ifndef __CPPCHECK__ #error only include this header in AnalysisBase #endif +#endif namespace asg { diff --git a/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/AnaAlgorithm.h b/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/AnaAlgorithm.h index a44020341c7e7fc19cf7bba7218051a97abc8aa3..93ee4e57c2a05bcbd5af55a262a6ecae910d3f12 100644 --- a/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/AnaAlgorithm.h +++ b/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/AnaAlgorithm.h @@ -1,6 +1,6 @@ // Dear emacs, this is -*- c++ -*- /* - Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration + Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration */ /// @author Nils Krumnack @@ -29,6 +29,7 @@ class TH1; class TH2; class TH3; +class TEfficiency; class TTree; class ISvcLocator; @@ -108,15 +109,24 @@ namespace EL #ifdef XAOD_STANDALONE /// Type of the metadata store pointer in standalone mode typedef asg::SgTEventMeta* MetaStorePtr_t; + typedef const asg::SgTEventMeta* ConstMetaStorePtr_t; #else /// Type of the metadata store pointer in standalone mode typedef ServiceHandle< StoreGateSvc >& MetaStorePtr_t; + typedef const ServiceHandle< StoreGateSvc >& ConstMetaStorePtr_t; #endif // XAOD_STANDALONE + ///@{ /// Accessor for the input metadata store - MetaStorePtr_t inputMetaStore() const; + ConstMetaStorePtr_t inputMetaStore() const; + MetaStorePtr_t inputMetaStore(); + ///@} + + ///@{ /// Accessor for the output metadata store - MetaStorePtr_t outputMetaStore() const; + ConstMetaStorePtr_t outputMetaStore() const; + MetaStorePtr_t outputMetaStore(); + ///@} #ifdef XAOD_STANDALONE /// \brief get the (main) event store for this algorithm @@ -139,13 +149,23 @@ namespace EL ::StatusCode book (const TH1& hist); + /// \brief book the given histogram + /// \par Guarantee + /// strong + /// \par Failures + /// histogram booking error + public: + ::StatusCode book (const TEfficiency& hist); + + /// \brief get the histogram with the given name /// \par Guarantee /// strong /// \par Failures /// histogram not found public: - TH1 *hist (const std::string& name) const; + template<typename T=TH1> + T *hist (const std::string& name) const; /// \brief get the 2-d histogram with the given name @@ -166,6 +186,20 @@ namespace EL TH3 *hist3d (const std::string& name) const; + /// \brief get the efficiency histogram with the given name + /// + /// This exists with two names, since the originally chosen name doesn't + /// match the name used in Athena. + /// + /// \par Guarantee + /// strong + /// \par Failures + /// histogram not found + public: + TEfficiency *histeff (const std::string& name) const; + TEfficiency *efficiency (const std::string& name) const; + + /// \brief the histogram worker interface /// \par Guarantee /// strong @@ -519,11 +553,11 @@ namespace EL /// \brief Object accessing the input metadata store private: - mutable MetaStore_t m_inputMetaStore; + MetaStore_t m_inputMetaStore; /// \brief Object accessing the output metadata store private: - mutable MetaStore_t m_outputMetaStore; + MetaStore_t m_outputMetaStore; #ifdef XAOD_STANDALONE /// \brief the value of \ref histogramWorker diff --git a/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/AnaAlgorithm.icc b/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/AnaAlgorithm.icc index 518ebcd747c5ce38b758c645981b119a84cddee6..6bd7c6da4a0a8083b992662933234747d1d3e161 100644 --- a/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/AnaAlgorithm.icc +++ b/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/AnaAlgorithm.icc @@ -16,4 +16,27 @@ namespace EL { +#ifdef XAOD_STANDALONE + template<typename T> T *AnaAlgorithm :: + hist (const std::string& name) const + { + T *result = dynamic_cast<T*>(hist<TObject>(name)); + if (result == nullptr) + throw std::runtime_error ("histogram not of the right type: " + name + " " + typeid(T).name()); + return result; + } + + + + inline TEfficiency *AnaAlgorithm :: + efficiency (const std::string& name) const + { + return histeff (name); + } + + + + template<> TObject *AnaAlgorithm :: + hist<TObject> (const std::string& name) const; +#endif } diff --git a/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/AnaAlgorithmConfig.h b/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/AnaAlgorithmConfig.h index 69065c73d5aa08b44c89158a825631e266e903d1..b5ae1be731e48d8bb05dd37794a6377b081733cb 100644 --- a/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/AnaAlgorithmConfig.h +++ b/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/AnaAlgorithmConfig.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration + Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration */ /// @author Nils Krumnack @@ -10,8 +10,10 @@ #define ANA_ALGORITHM__ANA_ALGORITHM_CONFIG_H #ifndef ROOTCORE +#ifndef __CPPCHECK__ #error only include this header in AnalysisBase #endif +#endif #include <AnaAlgorithm/Global.h> diff --git a/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/AnaReentrantAlgorithmConfig.h b/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/AnaReentrantAlgorithmConfig.h index 49133783353ba93f17d5a0e90c8a656ed23996e6..c2b106c5b6825f59cfdd8713b4a0f2717e71cc98 100644 --- a/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/AnaReentrantAlgorithmConfig.h +++ b/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/AnaReentrantAlgorithmConfig.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration + Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration */ /// @author Nils Krumnack @@ -10,8 +10,10 @@ #define ANA_ALGORITHM__ANA_REENTRANT_ALGORITHM_CONFIG_H #ifndef ROOTCORE +#ifndef __CPPCHECK__ #error only include this header in AnalysisBase #endif +#endif #include <AnaAlgorithm/Global.h> diff --git a/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/IHistogramWorker.h b/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/IHistogramWorker.h index c1861d04df3baf90bd77e6e9af7f7dc1037a6aeb..f86cfa284b4eab0097aa7a7fb1ea7701b47edb5e 100644 --- a/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/IHistogramWorker.h +++ b/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/IHistogramWorker.h @@ -58,7 +58,7 @@ namespace EL /// object not found /// \post result != 0 public: - virtual TH1 *getOutputHist (const std::string& name) const = 0; + virtual TObject *getOutputHist (const std::string& name) const = 0; }; } diff --git a/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/PythonConfigBase.h b/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/PythonConfigBase.h index 721b389cc0e275d007138e9ac34c09b0abecf2c2..11e349e2a3bed374f412534352abe3055d8b2ab3 100644 --- a/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/PythonConfigBase.h +++ b/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/PythonConfigBase.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration + Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration */ /// @author Nils Krumnack @@ -10,8 +10,10 @@ #define ANA_ALGORITHM__PYTHON_CONFIG_BASE_H #ifndef ROOTCORE +#ifndef __CPPCHECK__ #error only include this header in AnalysisBase #endif +#endif #include <AnaAlgorithm/Global.h> diff --git a/PhysicsAnalysis/D3PDTools/AnaAlgorithm/Root/AnaAlgorithm.cxx b/PhysicsAnalysis/D3PDTools/AnaAlgorithm/Root/AnaAlgorithm.cxx index 0ab827e9add99b3f30d98b1738444a7747d31dd4..7791426adeeba5a7ac57015690b6263c123214bf 100644 --- a/PhysicsAnalysis/D3PDTools/AnaAlgorithm/Root/AnaAlgorithm.cxx +++ b/PhysicsAnalysis/D3PDTools/AnaAlgorithm/Root/AnaAlgorithm.cxx @@ -1,5 +1,5 @@ /* - Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration + Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration */ /// @author Nils Krumnack @@ -16,6 +16,7 @@ #include <TH1.h> #include <TH2.h> #include <TH3.h> +#include <TEfficiency.h> #include <stdexcept> #ifdef XAOD_STANDALONE @@ -68,7 +69,16 @@ namespace EL - AnaAlgorithm::MetaStorePtr_t AnaAlgorithm::inputMetaStore() const + AnaAlgorithm::ConstMetaStorePtr_t AnaAlgorithm::inputMetaStore() const + { +#ifdef XAOD_STANDALONE + return &m_inputMetaStore; +#else + return m_inputMetaStore; +#endif // XAOD_STANDALONE + } + + AnaAlgorithm::MetaStorePtr_t AnaAlgorithm::inputMetaStore() { #ifdef XAOD_STANDALONE return &m_inputMetaStore; @@ -79,7 +89,16 @@ namespace EL - AnaAlgorithm::MetaStorePtr_t AnaAlgorithm::outputMetaStore() const + AnaAlgorithm::ConstMetaStorePtr_t AnaAlgorithm::outputMetaStore() const + { +#ifdef XAOD_STANDALONE + return &m_outputMetaStore; +#else + return m_outputMetaStore; +#endif // XAOD_STANDALONE + } + + AnaAlgorithm::MetaStorePtr_t AnaAlgorithm::outputMetaStore() { #ifdef XAOD_STANDALONE return &m_outputMetaStore; @@ -110,8 +129,17 @@ namespace EL - TH1 *AnaAlgorithm :: - hist (const std::string& name) const + ::StatusCode AnaAlgorithm :: + book (const TEfficiency& hist) + { + histogramWorker()->addOutput (hist.Clone()); + return ::StatusCode::SUCCESS; + } + + + + template<> TObject *AnaAlgorithm :: + hist<TObject> (const std::string& name) const { return histogramWorker()->getOutputHist (name); } @@ -121,10 +149,7 @@ namespace EL TH2 *AnaAlgorithm :: hist2d (const std::string& name) const { - TH2 *hist = dynamic_cast<TH2*>(histogramWorker()->getOutputHist (name)); - if (hist == nullptr) - throw std::runtime_error ("histogram not a 2d-histogram: " + name); - return hist; + return hist<TH2>(name); } @@ -132,10 +157,15 @@ namespace EL TH3 *AnaAlgorithm :: hist3d (const std::string& name) const { - TH3 *hist = dynamic_cast<TH3*>(histogramWorker()->getOutputHist (name)); - if (hist == nullptr) - throw std::runtime_error ("histogram not a 3d-histogram: " + name); - return hist; + return hist<TH3>(name); + } + + + + TEfficiency *AnaAlgorithm :: + histeff (const std::string& name) const + { + return hist<TEfficiency>(name); } diff --git a/PhysicsAnalysis/D3PDTools/EventLoop/CMakeLists.txt b/PhysicsAnalysis/D3PDTools/EventLoop/CMakeLists.txt index 5c543a240cfbb08a139f2c24d58f1afe111f87ab..54c2664774cb9dbcf2d22b2c4cd9d20b27ff855b 100644 --- a/PhysicsAnalysis/D3PDTools/EventLoop/CMakeLists.txt +++ b/PhysicsAnalysis/D3PDTools/EventLoop/CMakeLists.txt @@ -28,7 +28,7 @@ atlas_add_root_dictionary( EventLoop EventLoop/LLDriver.h EventLoop/LSFDriver.h EventLoop/LocalDriver.h EventLoop/OutputStream.h EventLoop/MetricsSvc.h EventLoop/SoGEDriver.h EventLoop/StatusCode.h EventLoop/TorqueDriver.h - EventLoop/VomsProxySvc.h EventLoop/SlurmDriver.h + EventLoop/VomsProxySvc.h EventLoop/SlurmDriver.h EventLoop/WorkerConfig.h Root/LinkDef.h EXTERNAL_PACKAGES ROOT ) @@ -53,11 +53,16 @@ atlas_add_test (EventLoop_gt_OutputStreamData INCLUDE_DIRS ${ROOT_INCLUDE_DIRS} LINK_LIBRARIES ${ROOT_LIBRARIES} EventLoop AsgTestingLib ) -atlas_add_test (EventLoop_gt_SubmitDirManager + atlas_add_test (EventLoop_gt_SubmitDirManager SOURCES test/gt_SubmitDirManager.cxx INCLUDE_DIRS ${ROOT_INCLUDE_DIRS} LINK_LIBRARIES ${ROOT_LIBRARIES} EventLoop AsgTestingLib ) +atlas_add_test (EventLoop_gt_DirectInputModule + SOURCES test/gt_DirectInputModule.cxx + INCLUDE_DIRS ${Boost_INCLUDE_DIRS} ${ROOT_INCLUDE_DIRS} + LINK_LIBRARIES ${Boost_LIBRARIES} ${ROOT_LIBRARIES} EventLoop AsgTestingLib ) + # Install files from the package: atlas_install_scripts( scripts/el_retrieve scripts/el_resubmit scripts/el_wait scripts/el_build_docker ) atlas_install_data( data/*.root data/Dockerfile data/docker_analysis_setup.sh data/*.yml ) diff --git a/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/BatchInputModule.h b/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/BatchInputModule.h new file mode 100644 index 0000000000000000000000000000000000000000..5b7cd00f38709394fdcb2a4f9f3976674ea7c5b0 --- /dev/null +++ b/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/BatchInputModule.h @@ -0,0 +1,44 @@ +/* + Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration +*/ + +/// @author Nils Krumnack + + +#ifndef EVENT_LOOP__BATCH_INPUT_MODULE_H +#define EVENT_LOOP__BATCH_INPUT_MODULE_H + +#include <EventLoop/Module.h> + +namespace EL +{ + struct BatchSample; + struct BatchSegment; + + namespace Detail + { + /// @brief the @ref IInputModule implementation for the batch driver + + class BatchInputModule final : public Module + { + /// Public Members + /// ============== + + public: + + BatchSample *sample = nullptr; + BatchSegment *segment = nullptr; + + + + /// Inherited Members + /// ================= + + public: + + StatusCode processInputs (ModuleData& data, IInputModuleActions& actions) override; + }; + } +} + +#endif diff --git a/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/DirectInputModule.h b/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/DirectInputModule.h new file mode 100644 index 0000000000000000000000000000000000000000..da68e8c4991d62b33796f1a167d089ec6e355fe3 --- /dev/null +++ b/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/DirectInputModule.h @@ -0,0 +1,44 @@ +/* + Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration +*/ + +/// @author Nils Krumnack + + +#ifndef EVENT_LOOP__DIRECT_INPUT_MODULE_H +#define EVENT_LOOP__DIRECT_INPUT_MODULE_H + +#include <EventLoop/Module.h> +#include <optional> +#include <vector> + +namespace EL +{ + namespace Detail + { + /// @brief the @ref IInputModule implementation for the direct driver + + class DirectInputModule final : public Module + { + /// Public Members + /// ============== + + public: + + std::vector<std::string> fileList; + std::optional<uint64_t> skipEvents; + std::optional<uint64_t> maxEvents; + + + + /// Inherited Members + /// ================= + + public: + + StatusCode processInputs (ModuleData& data, IInputModuleActions& actions) override; + }; + } +} + +#endif diff --git a/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/GridReportingModule.h b/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/GridReportingModule.h index 993a386164e2f559917403578c119bbcf3382c9f..62f4b740d029583baf15059d3b7fd86cb03cd458 100644 --- a/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/GridReportingModule.h +++ b/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/GridReportingModule.h @@ -10,6 +10,7 @@ #define EVENT_LOOP_GRID__GRID_REPORTING_MODULE_H #include <EventLoop/Module.h> +#include <vector> namespace EL { @@ -21,11 +22,19 @@ namespace EL class GridReportingModule final : public Module { /// the panda error code for bad input files - public: static constexpr int EC_BADINPUT = 223; + /// the list of files we processed + std::vector<std::string> m_files; + + /// the number of events we processed + unsigned m_eventsProcessed = 0; + public: - virtual void reportInputFailure (ModuleData& data); + virtual ::StatusCode onNewInputFile (ModuleData& data) override; + virtual ::StatusCode onExecute (ModuleData& data) override; + virtual ::StatusCode postFileClose (ModuleData& data) override; + virtual void reportInputFailure (ModuleData& data) override; }; } } diff --git a/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/IInputModuleActions.h b/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/IInputModuleActions.h new file mode 100644 index 0000000000000000000000000000000000000000..8cef35220b860ae6befe2b26506e95d81d924749 --- /dev/null +++ b/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/IInputModuleActions.h @@ -0,0 +1,82 @@ +/* + Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration +*/ + +/// @author Nils Krumnack + + +#ifndef EVENT_LOOP__I_INPUT_MODULE_ACTIONS_H +#define EVENT_LOOP__I_INPUT_MODULE_ACTIONS_H + +#include <Rtypes.h> +#include <optional> +#include <string> + +class StatusCode; + +namespace EL +{ + struct EventRange; + + namespace Detail + { + struct ModuleData; + + + /// @brief the actions that @ref Module::processInputs can perform + /// + /// The main reason to have this interface is that it is much easier to + /// write an input module if it can call functions to perform actions + /// directly (as opposed to e.g. returning which file to open next, etc.). + /// + /// There is a canonical implementation of this interface that is used in + /// the worker, but I'm using an abstract interface to decouple the input + /// modules from the actual worker implementation. In addition, this makes + /// it easier to write tests for input modules, or to employ a decorator + /// pattern, should the need arise. + /// + /// This interface is not frozen, but reflects the need of the input modules + /// I have defined. + + class IInputModuleActions + { + public: + + /// @brief standard virtual destructor + virtual ~IInputModuleActions () noexcept = default; + + + /// \brief process the given event range + /// + /// This will update `eventRange` if the end is set to eof + /// + /// \par Guarantee + /// basic + /// \par Failures + /// file can't be opened\n + /// event range exceeds length of file\n + /// processing failures + virtual ::StatusCode processEvents (EventRange& eventRange) = 0; + + + /// \brief open the given input file without processing it + /// + /// This is mostly to allow the driver to query the number of + /// events in the input file without processing it, usually to + /// determine the range of events to process. + /// + /// \par Guarantee + /// basic + /// \par Failures + /// file can't be opened + virtual ::StatusCode openInputFile (const std::string& inputFileUrl) = 0; + + + /// \brief the number of events in the input file + /// \pre inputFile() != 0 + [[nodiscard]] virtual Long64_t inputFileNumEntries () const = 0; + }; + } +} + +#endif diff --git a/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/Job.h b/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/Job.h index 040d064bfd30a65838a204c52eceb76b0d94d54b..4527f771ca585d472fd2bfd45296e02b973e1e7a 100644 --- a/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/Job.h +++ b/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/Job.h @@ -23,11 +23,14 @@ #include <EventLoop/Global.h> -#include <vector> + #include <AnaAlgorithm/Global.h> #include <EventLoop/JobConfig.h> #include <SampleHandler/SampleHandler.h> #include <SampleHandler/MetaObject.h> +#include <vector> +#include <memory> +class AnaReentrantAlgorithmConfig; namespace asg { @@ -218,6 +221,19 @@ namespace EL static const std::string optSkipEvents; + /// \brief a python configuration file that will be executed on + /// the worker + /// + /// This allows to inspect the meta-data of the first input file + /// and configure the job accordingly. + /// + /// EXPERIMENTAL: This feature is currently (23 Feb 23) new and + /// experimental and details of its implementation and usage may + /// still change. + public: + static const std::string optWorkerConfigFile; + + /// description: the name of the option for selecting the number /// of files per batch job. (only BatchDriver and derived /// drivers). @@ -463,6 +479,9 @@ namespace EL static const std::string optOfficial; static const std::string optVoms; + /// whether to use grid reporting even when not running on the grid + static const std::string optGridReporting; + /// these options are defined in \ref SH::MetaNames /// \{ diff --git a/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/MessageCheck.h b/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/MessageCheck.h index 81c2639d850aa5814b99b1d64b83f5d29672c8bd..abbfd15579523da5081edc67ec5e734b43d489f1 100644 --- a/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/MessageCheck.h +++ b/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/MessageCheck.h @@ -11,7 +11,8 @@ #include <EventLoop/Global.h> -#include <AsgTools/MessageCheck.h> +#include <AsgMessaging/MessageCheck.h> +#include <exception> namespace EL { @@ -20,7 +21,7 @@ namespace EL namespace Detail { /// \brief print out the currently evaluated exception - void report_exception (); + void report_exception (std::exception_ptr eptr); } } diff --git a/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/Module.h b/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/Module.h index 0dd63c214d502edbee3758855c818b71de465666..53d92ec4c5e3fca9296da2928af53c1faf3a0fa3 100644 --- a/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/Module.h +++ b/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/Module.h @@ -17,6 +17,9 @@ namespace EL { namespace Detail { + class IInputModuleActions; + + /// \brief the base class for EventLoop instrumentation module /// /// These are **internal** modules for EventLoop that allow to @@ -71,6 +74,15 @@ namespace EL virtual ::StatusCode onInitialize (ModuleData& data); + /// \brief process all input files + /// + /// This deviates slightly from the usual pattern for module functions in + /// that I pass in the possible actions as an argument. See @ref + /// IInputModuleActions for details. + public: + virtual StatusCode processInputs (ModuleData& data, IInputModuleActions& actions); + + /// \brief action after processing first event /// /// This is mostly meant to set up benchmarks that record diff --git a/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/ModuleData.h b/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/ModuleData.h index 2b9db442f356b95601261c458dbbfdc5ef9a089f..cf3efe724ec4b02eefb71cab8b11d1da6dd28465 100644 --- a/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/ModuleData.h +++ b/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/ModuleData.h @@ -106,6 +106,13 @@ namespace EL std::map<std::string,Detail::OutputStreamData> m_outputs; + + /// \brief explicit constructor for dependency reduction + ModuleData () noexcept; + + /// \brief explicit destructor for dependency reduction + ~ModuleData () noexcept; + /// \brief add the given output object to the histogram output stream /// \par Guarantee /// basic diff --git a/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/OutputStreamData.h b/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/OutputStreamData.h index bf7aa0b46e7990ac1d3dd34e3022f9c00e558947..217c0438cb5254dfc00496df0c28dd0a3e327dd8 100644 --- a/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/OutputStreamData.h +++ b/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/OutputStreamData.h @@ -144,7 +144,7 @@ namespace EL /// \par Guarantee /// no-fail public: - TH1 *getOutputHist (const std::string& name) const noexcept; + TObject *getOutputHist (const std::string& name) const noexcept; /// \brief get the output tree with the given name, or nullptr @@ -171,7 +171,7 @@ namespace EL /// \brief the output histogram map private: - std::unordered_map<std::string,TH1*> m_outputHistMap; + std::unordered_map<std::string,TObject*> m_outputHistMap; /// \brief the output tree map private: diff --git a/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/Worker.h b/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/Worker.h index 89f2816e2aa5e8063622810a925eca1f55988cc8..86ac5348f4a031432634ab9fa621d2265446484e 100644 --- a/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/Worker.h +++ b/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/Worker.h @@ -7,6 +7,7 @@ #include <EventLoop/Global.h> +#include <EventLoop/IInputModuleActions.h> #include <EventLoop/IWorker.h> #include <EventLoop/ModuleData.h> #include <EventLoop/OutputStreamData.h> @@ -14,7 +15,7 @@ namespace EL { - class Worker final : public IWorker, private Detail::ModuleData + class Worker final : public IWorker, private Detail::ModuleData, private Detail::IInputModuleActions { // // public interface @@ -69,7 +70,7 @@ namespace EL /// object not found /// \post result != 0 public: - TH1 *getOutputHist (const std::string& name) const final override; + TObject *getOutputHist (const std::string& name) const final override; /// effects: get the output file that goes into the dataset with @@ -238,14 +239,6 @@ namespace EL public: ::StatusCode gridExecute (const std::string& sampleName); - - private: - void gridNotifyJobFinished(uint64_t eventsProcessed, - const std::vector<std::string>& fileList); - - private: - void gridAbort(); - private: enum GridErrorCodes { EC_FAIL = 220, @@ -321,6 +314,15 @@ namespace EL ::StatusCode initialize (); + /// \brief process all the inputs + /// + /// This method ought to be called after @ref initialize and before @ref + /// finalize. It will rely on the defined modules to steer it to the files + /// and events it ought to process. + protected: + ::StatusCode processInputs (); + + /// \brief finalize the worker /// /// This method ought to be called after all events have been @@ -346,7 +348,7 @@ namespace EL /// event range exceeds length of file\n /// processing failures protected: - ::StatusCode processEvents (EventRange& eventRange); + ::StatusCode processEvents (EventRange& eventRange) override; /// \brief open the given input file without processing it @@ -360,7 +362,7 @@ namespace EL /// \par Failures /// file can't be opened protected: - ::StatusCode openInputFile (std::string inputFileUrl); + ::StatusCode openInputFile (const std::string& inputFileUrl) override; /// effects: add another output file @@ -387,7 +389,7 @@ namespace EL /// no-fail /// \pre inputFile() != 0 protected: - Long64_t inputFileNumEntries () const; + Long64_t inputFileNumEntries () const override; /// \brief the number of events that have been processed diff --git a/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/WorkerConfig.h b/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/WorkerConfig.h new file mode 100644 index 0000000000000000000000000000000000000000..4e616fe748ec654226b9074dba242c4ff9ca3a2c --- /dev/null +++ b/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/WorkerConfig.h @@ -0,0 +1,80 @@ +/* + Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration +*/ + +/// @author Nils Krumnack + + +#ifndef EVENT_LOOP__WORKER_CONFIG_H +#define EVENT_LOOP__WORKER_CONFIG_H + +#include <EventLoop/Global.h> + +#include <AsgTools/SgTEventMeta.h> +#include <TObject.h> + +namespace EL +{ + namespace Detail + { + class ModuleData; + } + + class PythonConfigBase; + + + class WorkerConfig final : public TObject + { + /// Public Members + /// ============== + + public: + + /// \brief access the meta store in the input file + [[nodiscard]] const asg::SgTEventMeta *metaStore() const noexcept; + + + /// \brief add the given component + void add (const PythonConfigBase& config); + + + + /// Internal/Detail Members + /// ======================= + + public: + + WorkerConfig (Detail::ModuleData *val_data) noexcept; + ~WorkerConfig () noexcept; + + + + /// Private Members + /// =============== + + private: + + Detail::ModuleData *m_data = nullptr; //! + asg::SgTEventMeta m_metaStore; //! + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpragmas" +#pragma GCC diagnostic ignored "-Wunknown-pragmas" +#pragma GCC diagnostic ignored "-Winconsistent-missing-override" + ClassDef(WorkerConfig, 1); +#pragma GCC diagnostic pop + }; + + + + /// Inline/Template Functions + /// ========================= + + [[nodiscard]] inline const asg::SgTEventMeta *WorkerConfig :: + metaStore() const noexcept + { + return &m_metaStore; + } +} + +#endif diff --git a/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/WorkerConfigModule.h b/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/WorkerConfigModule.h new file mode 100644 index 0000000000000000000000000000000000000000..6352226f42d0e1898d876c7db1cea2ae7646458f --- /dev/null +++ b/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/WorkerConfigModule.h @@ -0,0 +1,31 @@ +/* + Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration +*/ + +/// @author Nils Krumnack + + +#ifndef EVENT_LOOP__WORKER_CONFIG_MODULE_H +#define EVENT_LOOP__WORKER_CONFIG_MODULE_H + +#include <EventLoop/Global.h> + +#include <EventLoop/Module.h> + +namespace EL +{ + namespace Detail + { + /// \brief a \ref Module implementation for running user + /// configuration on the worker node + + class WorkerConfigModule final : public Module + { + public: + + virtual StatusCode onInitialize (ModuleData& data) override; + }; + } +} + +#endif diff --git a/PhysicsAnalysis/D3PDTools/EventLoop/Root/Algorithm.cxx b/PhysicsAnalysis/D3PDTools/EventLoop/Root/Algorithm.cxx index e0be18661d1bc7d284e91c7f1899c05577735492..0d6f4694cc3b6d7d810dad7a26bd6a561e12c3ab 100644 --- a/PhysicsAnalysis/D3PDTools/EventLoop/Root/Algorithm.cxx +++ b/PhysicsAnalysis/D3PDTools/EventLoop/Root/Algorithm.cxx @@ -64,7 +64,7 @@ namespace EL hist (const std::string& name) const { RCU_READ_INVARIANT (this); - return wk()->getOutputHist (name); + return dynamic_cast<TH1*>(wk()->getOutputHist (name)); } diff --git a/PhysicsAnalysis/D3PDTools/EventLoop/Root/AlgorithmStateModule.cxx b/PhysicsAnalysis/D3PDTools/EventLoop/Root/AlgorithmStateModule.cxx index 769eec9d08f74ff770f7bf7abfa88b8efde88222..dee879da225667fac29213bb98116ef69d41c4d9 100644 --- a/PhysicsAnalysis/D3PDTools/EventLoop/Root/AlgorithmStateModule.cxx +++ b/PhysicsAnalysis/D3PDTools/EventLoop/Root/AlgorithmStateModule.cxx @@ -20,6 +20,7 @@ #include <EventLoop/Worker.h> #include <RootCoreUtils/Assert.h> #include <TTree.h> +#include <exception> // // method implementations @@ -47,7 +48,7 @@ namespace EL } } catch (...) { - report_exception (); + report_exception (std::current_exception()); ANA_MSG_ERROR ("executing " << funcName << " on algorithm " << alg->getName()); return StatusCode::FAILURE; } diff --git a/PhysicsAnalysis/D3PDTools/EventLoop/Root/BatchDriver.cxx b/PhysicsAnalysis/D3PDTools/EventLoop/Root/BatchDriver.cxx index f6d161f2aac10765e8009f498c4ec8cf549e6c28..9afd0685c8aede8fd50f20956f6dc458aedad229 100644 --- a/PhysicsAnalysis/D3PDTools/EventLoop/Root/BatchDriver.cxx +++ b/PhysicsAnalysis/D3PDTools/EventLoop/Root/BatchDriver.cxx @@ -50,7 +50,7 @@ namespace EL /// guarantee: basic /// failures: out of memory II void fillJob (BatchJob& myjob, const Job& job, - const std::string& submitDir) + const std::string& submitDir) { myjob.job = job; myjob.location = submitDir; @@ -64,7 +64,7 @@ namespace EL /// guarantee: basic /// failures: out of memory II void fillSample (BatchSample& mysample, - const SH::Sample& sample, const SH::MetaObject& meta) + const SH::Sample& sample, const SH::MetaObject& meta) { mysample.name = sample.name(); mysample.meta = *sample.meta(); @@ -82,7 +82,7 @@ namespace EL /// failures: segment misconfiguration /// requires: !segments.empty() void addSample (BatchJob& job, BatchSample sample, - const std::vector<BatchSegment>& segments) + const std::vector<BatchSegment>& segments) { RCU_REQUIRE (!segments.empty()); @@ -92,7 +92,7 @@ namespace EL RCU_ASSERT (segments[0].begin_event == 0); for (std::size_t iter = 0, end = segments.size(); iter != end; ++ iter) { - BatchSegment segment = segments[iter]; + BatchSegment segment = segments[iter]; segment.sampleName = sample.name; { @@ -106,19 +106,19 @@ namespace EL segment.segmentName = myname.str(); } - segment.sample = job.samples.size(); - segment.job_id = job.segments.size(); - if (iter+1 < end) - { - segment.end_file = segments[iter+1].begin_file; - segment.end_event = segments[iter+1].begin_event; - } else - { - segment.end_file = sample.files.size(); - segment.end_event = 0; - } - RCU_ASSERT (segment.begin_file < segment.end_file || (segment.begin_file == segment.end_file && segment.begin_event <= segment.end_event)); - job.segments.push_back (segment); + segment.sample = job.samples.size(); + segment.job_id = job.segments.size(); + if (iter+1 < end) + { + segment.end_file = segments[iter+1].begin_file; + segment.end_event = segments[iter+1].begin_event; + } else + { + segment.end_file = sample.files.size(); + segment.end_event = 0; + } + RCU_ASSERT (segment.begin_file < segment.end_file || (segment.begin_file == segment.end_file && segment.begin_event <= segment.end_event)); + job.segments.push_back (segment); } sample.end_segments = job.segments.size(); @@ -137,19 +137,19 @@ namespace EL /// requires: numJobs == rint (numJobs) /// postcondition: !segments.empty() void splitSampleByFile (const BatchSample& sample, - std::vector<BatchSegment>& segments, - double numJobs) + std::vector<BatchSegment>& segments, + double numJobs) { // RCU_REQUIRE (!sample.files.empty()); RCU_REQUIRE (numJobs == rint (numJobs)); RCU_REQUIRE (numJobs <= sample.files.size()); for (std::size_t index = 0, end = numJobs; - index < end; ++ index) + index < end; ++ index) { - BatchSegment segment; - segment.begin_file = rint (index * (sample.files.size() / numJobs)); - segments.push_back (segment); + BatchSegment segment; + segment.begin_file = rint (index * (sample.files.size() / numJobs)); + segments.push_back (segment); } } @@ -165,9 +165,9 @@ namespace EL /// requires: numJobs == rint (numJobs) /// postcondition: !segments.empty() void splitSampleByEvent (const BatchSample& sample, - std::vector<BatchSegment>& segments, - const std::vector<Long64_t>& eventsFile, - double numJobs) + std::vector<BatchSegment>& segments, + const std::vector<Long64_t>& eventsFile, + double numJobs) { RCU_REQUIRE (!sample.files.empty()); RCU_REQUIRE (sample.files.size() == eventsFile.size()); @@ -175,29 +175,29 @@ namespace EL Long64_t eventsSum = 0; for (std::vector<Long64_t>::const_iterator nevents = eventsFile.begin(), - end = eventsFile.end(); nevents != end; ++ nevents) + end = eventsFile.end(); nevents != end; ++ nevents) { - RCU_ASSERT_SOFT (*nevents >= 0); - eventsSum += *nevents; + RCU_ASSERT_SOFT (*nevents >= 0); + eventsSum += *nevents; } if (numJobs > eventsSum) - numJobs = eventsSum; + numJobs = eventsSum; Long64_t eventsMax - = sample.meta.castDouble (Job::optEventsPerWorker); + = sample.meta.castDouble (Job::optEventsPerWorker); if (eventsMax > 0) - numJobs = ceil (double (eventsSum) / eventsMax); + numJobs = ceil (double (eventsSum) / eventsMax); eventsMax = Long64_t (ceil (double (eventsSum) / numJobs)); BatchSegment segment; while (std::size_t (segment.begin_file) < eventsFile.size()) { - while (segment.begin_event < eventsFile[segment.begin_file]) - { - segments.push_back (segment); - segment.begin_event += eventsMax; - } - segment.begin_event -= eventsFile[segment.begin_file]; - ++ segment.begin_file; + while (segment.begin_event < eventsFile[segment.begin_file]) + { + segments.push_back (segment); + segment.begin_event += eventsMax; + } + segment.begin_event -= eventsFile[segment.begin_file]; + ++ segment.begin_file; } RCU_ASSERT (segments.size() == numJobs); } @@ -213,7 +213,7 @@ namespace EL /// requires: !sample.files.empty() /// postcondition: !segments.empty() void splitSample (const BatchSample& sample, - std::vector<BatchSegment>& segments) + std::vector<BatchSegment>& segments) { // RCU_REQUIRE (!sample.files.empty()); @@ -223,36 +223,36 @@ namespace EL // it will balance out things slightly if we only have a few // files and multiple files per job. const double filesPerWorker - = sample.meta.castDouble (Job::optFilesPerWorker, 1); + = sample.meta.castDouble (Job::optFilesPerWorker, 1); if (filesPerWorker < 1) { - std::ostringstream msg; - msg << "invalid number of files per worker: " << filesPerWorker; - RCU_THROW_MSG (msg.str()); + std::ostringstream msg; + msg << "invalid number of files per worker: " << filesPerWorker; + RCU_THROW_MSG (msg.str()); } double numJobs = ceil (sample.files.size() / filesPerWorker); const TObject *meta - = sample.meta.get (SH::MetaFields::numEventsPerFile); + = sample.meta.get (SH::MetaFields::numEventsPerFile); if (meta) { - const SH::MetaVector<Long64_t> *const meta_nentries - = dynamic_cast<const SH::MetaVector<Long64_t> *>(meta); - RCU_ASSERT_SOFT (meta == meta_nentries); - RCU_ASSERT_SOFT (meta_nentries->value.size() == sample.files.size()); - splitSampleByEvent (sample, segments, meta_nentries->value, numJobs); + const SH::MetaVector<Long64_t> *const meta_nentries + = dynamic_cast<const SH::MetaVector<Long64_t> *>(meta); + RCU_ASSERT_SOFT (meta == meta_nentries); + RCU_ASSERT_SOFT (meta_nentries->value.size() == sample.files.size()); + splitSampleByEvent (sample, segments, meta_nentries->value, numJobs); } else { - splitSampleByFile (sample, segments, numJobs); + splitSampleByFile (sample, segments, numJobs); } if (segments.empty()) { - // rationale: this isn't really the proper thing to do. if a - // sample is empty I should just run the job locally. - BatchSegment empty; - segments.push_back (empty); + // rationale: this isn't really the proper thing to do. if a + // sample is empty I should just run the job locally. + BatchSegment empty; + segments.push_back (empty); } RCU_PROVIDE (!segments.empty()); @@ -264,23 +264,23 @@ namespace EL /// guarantee: basic /// failures: out of memory II void fillFullJob (BatchJob& myjob, const Job& job, - const std::string& location, - const SH::MetaObject& meta) + const std::string& location, + const SH::MetaObject& meta) { fillJob (myjob, job, location); *myjob.job.options() = meta; for (std::size_t sampleIndex = 0, end = job.sampleHandler().size(); - sampleIndex != end; ++ sampleIndex) + sampleIndex != end; ++ sampleIndex) { - BatchSample mysample; - fillSample (mysample, *job.sampleHandler()[sampleIndex], meta); + BatchSample mysample; + fillSample (mysample, *job.sampleHandler()[sampleIndex], meta); - std::vector<BatchSegment> subsegments; - splitSample (mysample, subsegments); - myjob.njobs_old.push_back (subsegments.size()); + std::vector<BatchSegment> subsegments; + splitSample (mysample, subsegments); + myjob.njobs_old.push_back (subsegments.size()); - addSample (myjob, mysample, subsegments); + addSample (myjob, mysample, subsegments); } } } @@ -443,15 +443,15 @@ namespace EL case Detail::ManagerStep::batchJobStatusResubmit: case Detail::ManagerStep::batchJobStatusRetrieve: { - for (std::size_t job = 0; job != data.batchJob->segments.size(); ++ job) - { - std::ostringstream completedFile; - completedFile << data.submitDir << "/status/completed-" << job; + for (std::size_t job = 0; job != data.batchJob->segments.size(); ++ job) + { + std::ostringstream completedFile; + completedFile << data.submitDir << "/status/completed-" << job; const bool hasCompleted = (gSystem->AccessPathName (completedFile.str().c_str()) == 0); - std::ostringstream failFile; - failFile << data.submitDir << "/status/fail-" << job; + std::ostringstream failFile; + failFile << data.submitDir << "/status/fail-" << job; const bool hasFail = (gSystem->AccessPathName (failFile.str().c_str()) == 0); @@ -552,8 +552,18 @@ namespace EL // <path of build dir>/x86_64-slc6-gcc62-opt (comes from CMake, we need this) const char *WORKDIR_DIR = getenv ("WorkDir_DIR"); - if (WORKDIR_DIR == nullptr) - RCU_THROW_MSG ("could not find environment variable $WorkDir_DIR"); + // As a backup, keep the CMAKE_PREFIX_PATH + std::string CMAKE_DIR_str ( getenv ("CMAKE_PREFIX_PATH") ); + if (WORKDIR_DIR == nullptr){ + msgEventLoop::ANA_MSG_INFO ("Could not find environment variable $WorkDir_DIR"); + // Instead, build from the first path in CMAKE_PREFIX_PATH + if (CMAKE_DIR_str.find(":") != std::string::npos){ + // Erase everything from the colon onwards + CMAKE_DIR_str.erase( CMAKE_DIR_str.find(":") , std::string::npos ); + } + // Provide the remainder of the string to the workdir + WORKDIR_DIR = CMAKE_DIR_str.data(); + } if(!data.sharedFileSystem) { @@ -724,7 +734,7 @@ namespace EL RCU_ASSERT (data.batchJob->njobs_old.size() == data.batchJob->samples.size()); for (std::size_t sample = 0, end = data.batchJob->samples.size(); - sample != end; ++ sample) + sample != end; ++ sample) { const BatchSample& mysample (data.batchJob->samples[sample]); @@ -734,62 +744,62 @@ namespace EL { ANA_MSG_VERBOSE ("merge files for sample " << data.batchJob->samples[sample].name); - bool complete = true; - std::vector<std::string> input; - for (std::size_t segment = mysample.begin_segments, - end = mysample.end_segments; segment != end; ++ segment) - { - const BatchSegment& mysegment = data.batchJob->segments[segment]; + bool complete = true; + std::vector<std::string> input; + for (std::size_t segment = mysample.begin_segments, + end = mysample.end_segments; segment != end; ++ segment) + { + const BatchSegment& mysegment = data.batchJob->segments[segment]; - const std::string hist_file = origHistOutput->targetURL + const std::string hist_file = origHistOutput->targetURL (mysegment.sampleName, mysegment.segmentName, ".root"); ANA_MSG_VERBOSE ("merge segment " << segment << " completed=" << (data.batchJobSuccess.find(segment)!=data.batchJobSuccess.end()) << " fail=" << (data.batchJobFailure.find(segment)!=data.batchJobFailure.end()) << " unknown=" << (data.batchJobUnknown.find(segment)!=data.batchJobUnknown.end())); - input.push_back (hist_file); - - if (data.batchJobFailure.find(segment)!=data.batchJobFailure.end()) - { - std::ostringstream message; - message << "subjob " << segment << "/" << mysegment.fullName - << " failed"; - RCU_THROW_MSG (message.str()); - } - else if (data.batchJobSuccess.find(segment)==data.batchJobSuccess.end()) - complete = false, result = false; - } - if (complete) - { - RCU::hadd (output.str(), input); - - // Merge output data directories - for (Job::outputIter out = data.batchJob->job.outputBegin(), - end = data.batchJob->job.outputEnd(); out != end; ++ out) - { - output.str(""); - output << data.submitDir << "/data-" << out->label(); - - if(gSystem->AccessPathName(output.str().c_str())) - gSystem->mkdir(output.str().c_str(),true); - - - output << "/" << data.batchJob->samples[sample].name << ".root"; - - std::vector<std::string> input; - for (std::size_t segment = mysample.begin_segments, - end = mysample.end_segments; segment != end; ++ segment) - { - const BatchSegment& mysegment = data.batchJob->segments[segment]; - - const std::string infile = - data.submitDir + "/fetch/data-" + out->label() + "/" + mysegment.fullName + ".root"; - - input.push_back (infile); - } - - RCU::hadd(output.str(), input); - } - } + input.push_back (hist_file); + + if (data.batchJobFailure.find(segment)!=data.batchJobFailure.end()) + { + std::ostringstream message; + message << "subjob " << segment << "/" << mysegment.fullName + << " failed"; + RCU_THROW_MSG (message.str()); + } + else if (data.batchJobSuccess.find(segment)==data.batchJobSuccess.end()) + complete = false, result = false; + } + if (complete) + { + RCU::hadd (output.str(), input); + + // Merge output data directories + for (Job::outputIter out = data.batchJob->job.outputBegin(), + end = data.batchJob->job.outputEnd(); out != end; ++ out) + { + output.str(""); + output << data.submitDir << "/data-" << out->label(); + + if(gSystem->AccessPathName(output.str().c_str())) + gSystem->mkdir(output.str().c_str(),true); + + + output << "/" << data.batchJob->samples[sample].name << ".root"; + + std::vector<std::string> input; + for (std::size_t segment = mysample.begin_segments, + end = mysample.end_segments; segment != end; ++ segment) + { + const BatchSegment& mysegment = data.batchJob->segments[segment]; + + const std::string infile = + data.submitDir + "/fetch/data-" + out->label() + "/" + mysegment.fullName + ".root"; + + input.push_back (infile); + } + + RCU::hadd(output.str(), input); + } + } } } return result; diff --git a/PhysicsAnalysis/D3PDTools/EventLoop/Root/BatchInputModule.cxx b/PhysicsAnalysis/D3PDTools/EventLoop/Root/BatchInputModule.cxx new file mode 100644 index 0000000000000000000000000000000000000000..9749d3a5665e2fcbba020c47cc34fb2bdc9e91cb --- /dev/null +++ b/PhysicsAnalysis/D3PDTools/EventLoop/Root/BatchInputModule.cxx @@ -0,0 +1,55 @@ +/* + Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration +*/ + +/// @author Nils Krumnack + + +// +// includes +// + +#include <EventLoop/BatchInputModule.h> + +#include <EventLoop/BatchSample.h> +#include <EventLoop/BatchSegment.h> +#include <EventLoop/EventRange.h> +#include <EventLoop/IInputModuleActions.h> +#include <EventLoop/MessageCheck.h> +#include <RootCoreUtils/Assert.h> + +// +// method implementations +// + +namespace EL +{ + namespace Detail + { + StatusCode BatchInputModule :: + processInputs (ModuleData& /*data*/, IInputModuleActions& actions) + { + using namespace msgEventLoop; + + Long64_t beginFile = segment->begin_file; + Long64_t endFile = segment->end_file; + Long64_t lastFile = segment->end_file; + RCU_ASSERT (beginFile <= endFile); + Long64_t beginEvent = segment->begin_event; + Long64_t endEvent = segment->end_event; + if (endEvent > 0) endFile += 1; + + for (Long64_t file = beginFile; file != endFile; ++ file) + { + RCU_ASSERT (std::size_t(file) < sample->files.size()); + EventRange eventRange; + eventRange.m_url = sample->files[file]; + eventRange.m_beginEvent = (file == beginFile ? beginEvent : 0); + eventRange.m_endEvent = (file == lastFile ? endEvent : EventRange::eof); + ANA_CHECK (actions.processEvents (eventRange)); + } + + return StatusCode::SUCCESS; + } + } +} diff --git a/PhysicsAnalysis/D3PDTools/EventLoop/Root/DirectInputModule.cxx b/PhysicsAnalysis/D3PDTools/EventLoop/Root/DirectInputModule.cxx new file mode 100644 index 0000000000000000000000000000000000000000..81a7c49780e1c3af84de2288dbf83b9bba46b742 --- /dev/null +++ b/PhysicsAnalysis/D3PDTools/EventLoop/Root/DirectInputModule.cxx @@ -0,0 +1,78 @@ +/* + Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration +*/ + +/// @author Nils Krumnack + + +// +// includes +// + +#include <EventLoop/DirectInputModule.h> + +#include <EventLoop/IInputModuleActions.h> +#include <EventLoop/EventRange.h> +#include <EventLoop/MessageCheck.h> +#include <RootCoreUtils/Assert.h> + +// +// method implementations +// + +namespace EL +{ + namespace Detail + { + StatusCode DirectInputModule :: + processInputs (ModuleData& /*data*/, IInputModuleActions& actions) + { + using namespace msgEventLoop; + Long64_t toSkip = this->skipEvents.value_or (0); + std::optional<Long64_t> toProcess; + if (this->maxEvents.has_value()) + toProcess = this->maxEvents.value(); + for (const std::string& fileName : fileList) + { + // open the input file to inspect it + ANA_CHECK (actions.openInputFile (fileName)); + ANA_MSG_DEBUG ("Opened input file: " << fileName); + + EventRange eventRange; + eventRange.m_url = fileName; + eventRange.m_endEvent = actions.inputFileNumEntries(); + + if (toSkip > 0) + { + if (toSkip >= eventRange.m_endEvent) + { + toSkip -= eventRange.m_endEvent; + ANA_MSG_INFO ("File " << fileName << " has only " << eventRange.m_endEvent << " events, skipping it."); + continue; + } + eventRange.m_beginEvent = toSkip; + toSkip = 0u; + } + + if (toProcess.has_value()) + { + if (eventRange.m_endEvent >= eventRange.m_beginEvent + toProcess.value()) + { + eventRange.m_endEvent = eventRange.m_beginEvent + toProcess.value(); + toProcess = 0u; + } else + { + toProcess.value() -= eventRange.m_endEvent - eventRange.m_beginEvent; + } + } + ANA_CHECK (actions.processEvents (eventRange)); + if (toProcess.has_value() && toProcess.value() == 0u) + { + ANA_MSG_INFO ("Reached maximum number of events, stopping."); + break; + } + } + return StatusCode::SUCCESS; + } + } +} diff --git a/PhysicsAnalysis/D3PDTools/EventLoop/Root/Driver.cxx b/PhysicsAnalysis/D3PDTools/EventLoop/Root/Driver.cxx index b0285da0a63ecd4196298fc7d1837ef002ddf41e..84560f2a42b86d6b40dc2f2c66c4acef2f0c7eb7 100644 --- a/PhysicsAnalysis/D3PDTools/EventLoop/Root/Driver.cxx +++ b/PhysicsAnalysis/D3PDTools/EventLoop/Root/Driver.cxx @@ -212,7 +212,7 @@ namespace EL } std::string to = location; while (!to.empty() && to[to.size()-1] == '/') - to = to.substr (0,to.size()-1); + to.resize (to.size()-1); { SH::SampleHandler sh; sh.load (location + "/hist"); diff --git a/PhysicsAnalysis/D3PDTools/EventLoop/Root/GridReportingModule.cxx b/PhysicsAnalysis/D3PDTools/EventLoop/Root/GridReportingModule.cxx index 738c7d7450c4e76e41d30022d43114e934b7af8a..5ee0e17d05cc1766e4557eaa08f7a414a672cb66 100644 --- a/PhysicsAnalysis/D3PDTools/EventLoop/Root/GridReportingModule.cxx +++ b/PhysicsAnalysis/D3PDTools/EventLoop/Root/GridReportingModule.cxx @@ -13,6 +13,9 @@ #include <EventLoop/GridReportingModule.h> #include <EventLoop/MessageCheck.h> +#include <EventLoop/ModuleData.h> +#include <algorithm> +#include <fstream> // // method implementations @@ -22,6 +25,48 @@ namespace EL { namespace Detail { + ::StatusCode GridReportingModule :: + onNewInputFile (ModuleData& data) + { + if (std::find (m_files.begin(), m_files.end(), data.m_inputFileUrl) == m_files.end()) + m_files.push_back (data.m_inputFileUrl); + return ::StatusCode::SUCCESS; + } + + + + ::StatusCode GridReportingModule :: + onExecute (ModuleData& /*data*/) + { + ++m_eventsProcessed; + return ::StatusCode::SUCCESS; + } + + + + ::StatusCode GridReportingModule :: + postFileClose (ModuleData& /*data*/) + { + using namespace msgEventLoop; + + // createJobSummary + std::ofstream summaryfile("../AthSummary.txt"); + if (summaryfile.is_open()) { + unsigned int nFiles = m_files.size(); + summaryfile << "Files read: " << nFiles << std::endl; + for (auto& file : m_files) + summaryfile << " " << file << std::endl; + summaryfile << "Events Read: " << m_eventsProcessed << std::endl; + summaryfile.close(); + } + else { + ANA_MSG_WARNING ("Failed to write summary file."); + } + return ::StatusCode::SUCCESS; + } + + + void GridReportingModule :: reportInputFailure (ModuleData& /*data*/) { diff --git a/PhysicsAnalysis/D3PDTools/EventLoop/Root/Job.cxx b/PhysicsAnalysis/D3PDTools/EventLoop/Root/Job.cxx index 0799640f1c3b4ace6bf248632ca815c13beb34b7..437c4252142066eca2c5fc0e1850c26082fda9f5 100644 --- a/PhysicsAnalysis/D3PDTools/EventLoop/Root/Job.cxx +++ b/PhysicsAnalysis/D3PDTools/EventLoop/Root/Job.cxx @@ -37,6 +37,7 @@ namespace EL const std::string Job::optAlgorithmTimer = "nc_EventLoop_AlgorithmTimer"; const std::string Job::optMaxEvents = "nc_EventLoop_MaxEvents"; const std::string Job::optSkipEvents = "nc_EventLoop_SkipEvents"; + const std::string Job::optWorkerConfigFile = "nc_EventLoop_WorkerConfigFile"; const std::string Job::optFilesPerWorker = "nc_EventLoop_FilesPerWorker"; const std::string Job::optEventsPerWorker = "nc_EventLoop_EventsPerWorker"; const std::string Job::optWorkerPostClosedOutputsExecutable = "nc_EventLoop_WorkerPostClosedOutputsExecutable"; @@ -86,6 +87,7 @@ namespace EL const std::string Job::optGridCpuTimePerEvent = "nc_cpuTimePerEvent"; const std::string Job::optGridMaxWalltime = "nc_maxWalltime"; const std::string Job::optGridAvoidVP = "nc_avoidVP"; + const std::string Job::optGridReporting = "nc_gridReporting"; const std::string Job::optBatchSharedFileSystem = "nc_sharedFileSystem"; const std::string Job::optBatchSlurmExtraConfigLines = "nc_SlurmExtraConfig"; const std::string Job::optBatchSlurmWrapperExec = "nc_SlurmWrapperExec"; diff --git a/PhysicsAnalysis/D3PDTools/EventLoop/Root/LinkDef.h b/PhysicsAnalysis/D3PDTools/EventLoop/Root/LinkDef.h index 0e12dff5f665550367b25f3e54fe91e03443e893..63a969859280f35a20d8e92219621866b382f3ea 100644 --- a/PhysicsAnalysis/D3PDTools/EventLoop/Root/LinkDef.h +++ b/PhysicsAnalysis/D3PDTools/EventLoop/Root/LinkDef.h @@ -35,6 +35,7 @@ #pragma link C++ class EL::TorqueDriver+; #pragma link C++ class EL::VomsProxySvc+; #pragma link C++ class EL::IWorker+; +#pragma link C++ class EL::WorkerConfig+; #pragma link C++ class std::pair<Long64_t,Long64_t>+; #pragma link C++ class std::vector<std::pair<Long64_t,Long64_t> >+; diff --git a/PhysicsAnalysis/D3PDTools/EventLoop/Root/LocalDriver.cxx b/PhysicsAnalysis/D3PDTools/EventLoop/Root/LocalDriver.cxx index c963edbbdb491f7ba4cf141623658b3da3644b94..33480e0f81b14adf8dd0c3bded15a805e8f7b2a5 100644 --- a/PhysicsAnalysis/D3PDTools/EventLoop/Root/LocalDriver.cxx +++ b/PhysicsAnalysis/D3PDTools/EventLoop/Root/LocalDriver.cxx @@ -85,7 +85,7 @@ namespace EL if (gSystem->MakeDirectory (basedirName.str().c_str()) != 0) RCU_THROW_MSG ("failed to create directory " + basedirName.str()); } - auto submitSingle = [&, this] (std::size_t index) noexcept -> StatusCode + auto submitSingle = [&] (std::size_t index) noexcept -> StatusCode { try { @@ -129,7 +129,7 @@ namespace EL bool abort = false; while (threads.size() < unsigned (numParallelProcs)) { - threads.emplace_back ([&,this] () noexcept + threads.emplace_back ([&] () noexcept { std::unique_lock<std::mutex> lock (mutex); while (indexIter != data.batchJobIndices.end() && !abort) diff --git a/PhysicsAnalysis/D3PDTools/EventLoop/Root/MessageCheck.cxx b/PhysicsAnalysis/D3PDTools/EventLoop/Root/MessageCheck.cxx index 0bfee6cc4e4187577ffb6642f368f3d588f23c10..7cb80f43e1a925d59b81ea5e90cdbd60dd809cfb 100644 --- a/PhysicsAnalysis/D3PDTools/EventLoop/Root/MessageCheck.cxx +++ b/PhysicsAnalysis/D3PDTools/EventLoop/Root/MessageCheck.cxx @@ -1,5 +1,5 @@ /* - Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration + Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration */ /// @author Nils Krumnack @@ -24,12 +24,14 @@ namespace EL namespace Detail { - void report_exception () + void report_exception (std::exception_ptr eptr) { using namespace msgEventLoop; try { - throw; + if (eptr) { + std::rethrow_exception(eptr); + } } catch (std::exception& e) { ANA_MSG_ERROR ("caught exception: " << e.what()); diff --git a/PhysicsAnalysis/D3PDTools/EventLoop/Root/Module.cxx b/PhysicsAnalysis/D3PDTools/EventLoop/Root/Module.cxx index f69757c491b42dbba7077a86e093e3b85c39b88d..28d1e206afdecbcede7050731cd7ee9934dd1ad0 100644 --- a/PhysicsAnalysis/D3PDTools/EventLoop/Root/Module.cxx +++ b/PhysicsAnalysis/D3PDTools/EventLoop/Root/Module.cxx @@ -80,6 +80,12 @@ namespace EL return ::StatusCode::SUCCESS; } + ::StatusCode Module :: + processInputs (ModuleData& /*data*/, IInputModuleActions& /*actions*/) + { + return ::StatusCode::SUCCESS; + } + ::StatusCode Module :: onFinalize (ModuleData& /*data*/) { diff --git a/PhysicsAnalysis/D3PDTools/EventLoop/Root/ModuleData.cxx b/PhysicsAnalysis/D3PDTools/EventLoop/Root/ModuleData.cxx index f7275dec2a96893bbad1ecf344ed11c164d76e43..b5cefe823f55469a709759e735d1361816cd610b 100644 --- a/PhysicsAnalysis/D3PDTools/EventLoop/Root/ModuleData.cxx +++ b/PhysicsAnalysis/D3PDTools/EventLoop/Root/ModuleData.cxx @@ -14,6 +14,7 @@ #include <EventLoop/OutputStreamData.h> #include <RootCoreUtils/Assert.h> +#include <TTree.h> // // method implementations @@ -23,6 +24,12 @@ namespace EL { namespace Detail { + ModuleData :: ModuleData () noexcept = default; + + ModuleData :: ~ModuleData () noexcept = default; + + + void ModuleData :: addOutput (std::unique_ptr<TObject> output) { diff --git a/PhysicsAnalysis/D3PDTools/EventLoop/Root/OutputStreamData.cxx b/PhysicsAnalysis/D3PDTools/EventLoop/Root/OutputStreamData.cxx index 9f0d435794a383d17813083bcbd05cc9adaae0d9..dbc38932ddc0331393f689878b579bef17f59ef5 100644 --- a/PhysicsAnalysis/D3PDTools/EventLoop/Root/OutputStreamData.cxx +++ b/PhysicsAnalysis/D3PDTools/EventLoop/Root/OutputStreamData.cxx @@ -222,12 +222,10 @@ namespace EL { TH1 *const hist = dynamic_cast<TH1*> (outputObject.get()); + m_outputHistMap[outputObject->GetName()] = outputObject.get(); m_output.emplace_back (std::move (outputObject)); if (hist) - { - m_outputHistMap[hist->GetName()] = hist; hist->SetDirectory (nullptr); - } } } @@ -285,7 +283,7 @@ namespace EL - TH1 *OutputStreamData :: + TObject *OutputStreamData :: getOutputHist (const std::string& name) const noexcept { RCU_READ_INVARIANT (this); diff --git a/PhysicsAnalysis/D3PDTools/EventLoop/Root/Worker.cxx b/PhysicsAnalysis/D3PDTools/EventLoop/Root/Worker.cxx index 25aae16ac20f0cd1e2fa726a1d41578551c1671c..06643b06e2093ae89b0528b2c071ace365a04fdd 100644 --- a/PhysicsAnalysis/D3PDTools/EventLoop/Root/Worker.cxx +++ b/PhysicsAnalysis/D3PDTools/EventLoop/Root/Worker.cxx @@ -20,9 +20,11 @@ #include <AnaAlgorithm/IAlgorithmWrapper.h> #include <EventLoop/AlgorithmStateModule.h> #include <EventLoop/AlgorithmTimerModule.h> +#include <EventLoop/BatchInputModule.h> #include <EventLoop/BatchJob.h> #include <EventLoop/BatchSample.h> #include <EventLoop/BatchSegment.h> +#include <EventLoop/DirectInputModule.h> #include <EventLoop/Driver.h> #include <EventLoop/EventCountModule.h> #include <EventLoop/EventRange.h> @@ -37,6 +39,7 @@ #include <EventLoop/StopwatchModule.h> #include <EventLoop/PostClosedOutputsModule.h> #include <EventLoop/TEventModule.h> +#include <EventLoop/WorkerConfigModule.h> #include <RootCoreUtils/Assert.h> #include <RootCoreUtils/RootUtils.h> #include <RootCoreUtils/ThrowMsg.h> @@ -55,6 +58,7 @@ #include <fstream> #include <memory> #include <xAODRootAccess/LoadDictionaries.h> +#include <exception> // // method implementations @@ -113,12 +117,12 @@ namespace EL - TH1 *Worker :: + TObject *Worker :: getOutputHist (const std::string& name) const { RCU_READ_INVARIANT (this); - TH1 *result = m_histOutput->getOutputHist (name); + TObject *result = m_histOutput->getOutputHist (name); if (result == nullptr) RCU_THROW_MSG ("unknown output histogram: " + name); return result; } @@ -383,10 +387,13 @@ namespace EL m_modules.push_back (std::make_unique<Detail::TEventModule> ()); m_modules.push_back (std::make_unique<Detail::LeakCheckModule> ()); m_modules.push_back (std::make_unique<Detail::StopwatchModule> ()); + if (metaData()->castBool (Job::optGridReporting, false)) + m_modules.push_back (std::make_unique<Detail::GridReportingModule>()); if (metaData()->castBool (Job::optAlgorithmTimer, false)) m_modules.push_back (std::make_unique<Detail::AlgorithmTimerModule> ()); m_modules.push_back (std::make_unique<Detail::FileExecutedModule> ()); m_modules.push_back (std::make_unique<Detail::EventCountModule> ()); + m_modules.push_back (std::make_unique<Detail::WorkerConfigModule> ()); m_modules.push_back (std::make_unique<Detail::AlgorithmStateModule> ()); m_modules.push_back (std::make_unique<Detail::PostClosedOutputsModule> ()); @@ -416,6 +423,20 @@ namespace EL + ::StatusCode Worker :: + processInputs () + { + using namespace msgEventLoop; + + RCU_CHANGE_INVARIANT (this); + + for (auto& module : m_modules) + ANA_CHECK (module->processInputs (*this, *this)); + return ::StatusCode::SUCCESS; + } + + + ::StatusCode Worker :: finalize () { @@ -541,7 +562,7 @@ namespace EL ::StatusCode Worker :: - openInputFile (std::string inputFileUrl) + openInputFile (const std::string& inputFileUrl) { using namespace msgEventLoop; @@ -575,7 +596,7 @@ namespace EL inputFile = SH::openFile (inputFileUrl, *metaData()); } catch (...) { - Detail::report_exception (); + Detail::report_exception (std::current_exception()); } if (inputFile.get() == 0) { @@ -664,7 +685,7 @@ namespace EL } } catch (...) { - Detail::report_exception (); + Detail::report_exception (std::current_exception()); ANA_MSG_ERROR ("while calling execute() on algorithm " << iter->m_algorithm->getName()); return ::StatusCode::FAILURE; } @@ -684,7 +705,7 @@ namespace EL } } catch (...) { - Detail::report_exception (); + Detail::report_exception (std::current_exception()); ANA_MSG_ERROR ("while calling postExecute() on algorithm " << iter->m_algorithm->getName()); return ::StatusCode::FAILURE; } @@ -760,47 +781,20 @@ namespace EL ANA_CHECK (addOutputStream (out->label(), std::move (data))); } - ANA_CHECK (initialize ()); - - Long64_t maxEvents - = metaData()->castDouble (Job::optMaxEvents, -1); - Long64_t skipEvents - = metaData()->castDouble (Job::optSkipEvents, 0); - - std::vector<std::string> files = sample->makeFileList(); - for (const std::string& fileName : files) { - EventRange eventRange; - eventRange.m_url = fileName; - if (skipEvents == 0 && maxEvents == -1) - { - ANA_CHECK (processEvents (eventRange)); - } else - { - // just open the input file to inspect it - ANA_CHECK (openInputFile (fileName)); - eventRange.m_endEvent = inputFileNumEntries(); - - if (skipEvents != 0 && skipEvents >= eventRange.m_endEvent) - { - skipEvents -= eventRange.m_endEvent; - continue; - } - eventRange.m_beginEvent = skipEvents; - skipEvents = 0; - - if (maxEvents != -1) - { - if (eventRange.m_endEvent > eventRange.m_beginEvent + maxEvents) - eventRange.m_endEvent = eventRange.m_beginEvent + maxEvents; - maxEvents -= eventRange.m_endEvent - eventRange.m_beginEvent; - assert (maxEvents >= 0); - } - ANA_CHECK (processEvents (eventRange)); - if (maxEvents == 0) - break; - } + auto module = std::make_unique<Detail::DirectInputModule> (); + module->fileList = sample->makeFileList(); + Long64_t maxEvents = metaData()->castDouble (Job::optMaxEvents, -1); + if (maxEvents != -1) + module->maxEvents = maxEvents; + Long64_t skipEvents = metaData()->castDouble (Job::optSkipEvents, 0); + if (skipEvents != 0) + module->skipEvents = skipEvents; + addModule (std::move (module)); } + + ANA_CHECK (initialize ()); + ANA_CHECK (processInputs ()); ANA_CHECK (finalize ()); return ::StatusCode::SUCCESS; } @@ -866,25 +860,15 @@ namespace EL ANA_CHECK (addOutputStream (out->label(), std::move (data))); } - Long64_t beginFile = segment->begin_file; - Long64_t endFile = segment->end_file; - Long64_t lastFile = segment->end_file; - RCU_ASSERT (beginFile <= endFile); - Long64_t beginEvent = segment->begin_event; - Long64_t endEvent = segment->end_event; - if (endEvent > 0) endFile += 1; - - ANA_CHECK (initialize ()); - - for (Long64_t file = beginFile; file != endFile; ++ file) { - RCU_ASSERT (std::size_t(file) < sample->files.size()); - EventRange eventRange; - eventRange.m_url = sample->files[file]; - eventRange.m_beginEvent = (file == beginFile ? beginEvent : 0); - eventRange.m_endEvent = (file == lastFile ? endEvent : EventRange::eof); - ANA_CHECK (processEvents (eventRange)); + auto module = std::make_unique<Detail::BatchInputModule> (); + module->sample = sample; + module->segment = segment; + addModule (std::move (module)); } + + ANA_CHECK (initialize ()); + ANA_CHECK (processInputs ()); ANA_CHECK (finalize ()); std::ostringstream job_name; @@ -893,7 +877,7 @@ namespace EL return ::StatusCode::SUCCESS; } catch (...) { - Detail::report_exception (); + Detail::report_exception (std::current_exception()); return ::StatusCode::FAILURE; } } @@ -933,6 +917,8 @@ namespace EL ANA_CHECK (xAOD::LoadDictionaries()); mo = dynamic_cast<SH::MetaObject*>(f->Get(sampleName.c_str())); + if (!mo) + mo = dynamic_cast<SH::MetaObject*>(f->Get("defaultMetaObject")); if (!mo) { ANA_MSG_ERROR ("Could not read in sample meta object"); return ::StatusCode::FAILURE; @@ -972,6 +958,7 @@ namespace EL const std::string location = "."; + mo->setBool (Job::optGridReporting, true); setMetaData (mo); setOutputHist (location); setSegmentName ("output"); @@ -998,11 +985,8 @@ namespace EL setJobConfig (std::move (*jobConfig)); - addModule (std::make_unique<Detail::GridReportingModule> ()); - ANA_CHECK (initialize()); - - std::vector<std::string> fileList; { + auto module = std::make_unique<Detail::DirectInputModule> (); std::ifstream infile("input.txt"); while (infile) { std::string sLine; @@ -1011,53 +995,27 @@ namespace EL while (ssLine) { std::string sFile; if (!getline(ssLine, sFile, ',')) break; - fileList.push_back(sFile); + module->fileList.push_back(sFile); } } - } - if (fileList.size() == 0) { - ANA_MSG_ERROR ("no input files provided"); - //User was expecting input after all. - gSystem->Exit(EC_BADINPUT); - } - - for (const std::string& file : fileList) - { - EventRange eventRange; - eventRange.m_url = file; - - ANA_CHECK (processEvents (eventRange)); + if (module->fileList.size() == 0) { + ANA_MSG_ERROR ("no input files provided"); + //User was expecting input after all. + gSystem->Exit(EC_BADINPUT); + } + addModule (std::move (module)); } + ANA_CHECK (initialize()); + ANA_CHECK (processInputs ()); ANA_CHECK (finalize ()); int nEvents = eventsProcessed(); - int nFiles = fileList.size(); ANA_MSG_INFO ("Loop finished."); - ANA_MSG_INFO ("Read " << nEvents << " events in " << nFiles << " files."); - - gridNotifyJobFinished(eventsProcessed(), fileList); + ANA_MSG_INFO ("Read/processed " << nEvents << " events."); ANA_MSG_INFO ("EventLoop Grid worker finished"); ANA_MSG_INFO ("Saving output"); return ::StatusCode::SUCCESS; } - - void Worker::gridNotifyJobFinished(uint64_t eventsProcessed, - const std::vector<std::string>& fileList) { - // createJobSummary - std::ofstream summaryfile("../AthSummary.txt"); - if (summaryfile.is_open()) { - unsigned int nFiles = fileList.size(); - summaryfile << "Files read: " << nFiles << std::endl; - for (unsigned int i = 0; i < nFiles; i++) { - summaryfile << " " << fileList.at(i) << std::endl; - } - summaryfile << "Events Read: " << eventsProcessed << std::endl; - summaryfile.close(); - } - else { - //cout << "Failed to write summary file.\n"; - } - } } diff --git a/PhysicsAnalysis/D3PDTools/EventLoop/Root/WorkerConfig.cxx b/PhysicsAnalysis/D3PDTools/EventLoop/Root/WorkerConfig.cxx new file mode 100644 index 0000000000000000000000000000000000000000..553bde6eba4c58651826db41f535d37567f02afe --- /dev/null +++ b/PhysicsAnalysis/D3PDTools/EventLoop/Root/WorkerConfig.cxx @@ -0,0 +1,69 @@ +/* + Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration +*/ + +/// @author Nils Krumnack + + +// +// includes +// + +#include <EventLoop/WorkerConfig.h> + +#include <AnaAlgorithm/AnaAlgorithmWrapper.h> +#include <AnaAlgorithm/AnaReentrantAlgorithmWrapper.h> +#include <AnaAlgorithm/PythonConfigBase.h> +#include <AsgTools/SgTEventMeta.h> +#include <EventLoop/AsgServiceWrapper.h> +#include <EventLoop/AsgToolWrapper.h> +#include <EventLoop/ModuleData.h> + +#include <EventLoop/MessageCheck.h> + +// +// method implementations +// + +ClassImp (EL::WorkerConfig) + +namespace EL +{ + WorkerConfig :: + WorkerConfig (Detail::ModuleData *val_data) noexcept + : m_data (val_data), + m_metaStore (asg::SgTEventMeta::InputStore, nullptr) + {} + + + + // FIX ME: something bad happens in the linker step if this is not + // here and instead rely on the implicit destructor, which I don't + // want to debug. + WorkerConfig :: ~WorkerConfig () noexcept = default; + + + + void WorkerConfig :: + add (const EL::PythonConfigBase& config) + { + using namespace msgEventLoop; + ANA_MSG_INFO ("in add"); + // no invariant used + std::unique_ptr<IAlgorithmWrapper> alg; + if (config.componentType() == "AnaAlgorithm") + alg = std::make_unique<AnaAlgorithmWrapper> (AnaAlgorithmConfig (config)); + else if (config.componentType() == "AnaReentrantAlgorithm") + alg = std::make_unique<AnaReentrantAlgorithmWrapper> (AnaReentrantAlgorithmConfig (config)); + else if (config.componentType() == "AsgTool") + alg = std::make_unique<AsgToolWrapper> (asg::AsgToolConfig (config)); + else if (config.componentType() == "AsgService") + alg = std::make_unique<AsgServiceWrapper> (asg::AsgServiceConfig (config)); + else + { + ANA_MSG_ERROR ("unknown component type: \"" << config.componentType() << "\""); + throw std::runtime_error ("unknown component type: \"" + config.componentType() + "\""); + } + m_data->m_algs.emplace_back (std::move (alg)); + } +} diff --git a/PhysicsAnalysis/D3PDTools/EventLoop/Root/WorkerConfigModule.cxx b/PhysicsAnalysis/D3PDTools/EventLoop/Root/WorkerConfigModule.cxx new file mode 100644 index 0000000000000000000000000000000000000000..301d19132ab467fb2ae3d7ab1e4cd8a84258c76c --- /dev/null +++ b/PhysicsAnalysis/D3PDTools/EventLoop/Root/WorkerConfigModule.cxx @@ -0,0 +1,49 @@ +/* + Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration +*/ + +/// @author Nils Krumnack + + +// +// includes +// + +#include <EventLoop/WorkerConfigModule.h> + +#include <EventLoop/Job.h> +#include <EventLoop/MessageCheck.h> +#include <EventLoop/ModuleData.h> +#include <EventLoop/WorkerConfig.h> +#include <EventLoop/Worker.h> +#include <PathResolver/PathResolver.h> +#include <SampleHandler/MetaObject.h> +#include <TPython.h> + +// +// method implementations +// + +namespace EL +{ + namespace Detail + { + StatusCode WorkerConfigModule :: + onInitialize (ModuleData& data) + { + using namespace msgEventLoop; + std::string configFile = data.m_worker->metaData()->castString (Job::optWorkerConfigFile, ""); + if (!configFile.empty()) + { + configFile = PathResolverFindDataFile (configFile); + TPython::LoadMacro (configFile.c_str()); + WorkerConfig config (&data); + TPython::Bind (&config, "workerConfig"); + TPython::Eval ("fillWorkerConfig (workerConfig)"); + TPython::Bind (nullptr, "workerConfig"); + } + + return StatusCode::SUCCESS; + } + } +} diff --git a/PhysicsAnalysis/D3PDTools/EventLoop/test/gt_DirectInputModule.cxx b/PhysicsAnalysis/D3PDTools/EventLoop/test/gt_DirectInputModule.cxx new file mode 100644 index 0000000000000000000000000000000000000000..f0f48513e5813ec93219667d339e74100a48819b --- /dev/null +++ b/PhysicsAnalysis/D3PDTools/EventLoop/test/gt_DirectInputModule.cxx @@ -0,0 +1,201 @@ +/* + Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration +*/ + +/// @author Nils Krumnack + + + +// +// includes +// + +#include <AsgTesting/UnitTest.h> +#include <EventLoop/IInputModuleActions.h> +#include <EventLoop/ModuleData.h> +#include <EventLoop/DirectInputModule.h> +#include <EventLoop/EventRange.h> +#include <EventLoop/MessageCheck.h> +#include <TH1F.h> +#include <TKey.h> +#include <TSystem.h> +#include <TTree.h> +#include <filesystem> + +// +// unit test +// + +namespace EL +{ + namespace Detail + { + struct TestActions final : public IInputModuleActions + { + std::unordered_map<std::string,unsigned> inputFiles; + std::optional<Long64_t> inputFileNumEntries_result; + std::vector<EventRange> toProcessFiles; + std::size_t processIndex = 0u; + + virtual ::StatusCode processEvents (EventRange& eventRange) override + { + using namespace msgEventLoop; + + // do the standard behavior + ANA_CHECK (openInputFile (eventRange.m_url)); + if (eventRange.m_endEvent == EventRange::eof) + eventRange.m_endEvent = inputFileNumEntries(); + + if (processIndex >= toProcessFiles.size()) + return StatusCode::FAILURE; + EXPECT_EQ (eventRange.m_url, toProcessFiles[processIndex].m_url); + EXPECT_EQ (eventRange.m_beginEvent, toProcessFiles[processIndex].m_beginEvent); + EXPECT_EQ (eventRange.m_endEvent, toProcessFiles[processIndex].m_endEvent); + processIndex += 1u; + return StatusCode::SUCCESS; + } + + virtual ::StatusCode openInputFile (const std::string& inputFileUrl) override + { + using namespace msgEventLoop; + + if (inputFileUrl.empty()) + { + inputFileNumEntries_result.reset(); + return StatusCode::SUCCESS; + } + auto iter = inputFiles.find (inputFileUrl); + if (iter == inputFiles.end()) + { + ANA_MSG_ERROR ("unknown input file: " << inputFileUrl); + return StatusCode::FAILURE; + } + inputFileNumEntries_result = iter->second; + return StatusCode::SUCCESS; + } + + [[nodiscard]] virtual Long64_t inputFileNumEntries () const override + { + if (!inputFileNumEntries_result.has_value()) + throw std::logic_error ("no input file open"); + return inputFileNumEntries_result.value(); + } + }; + + + + class DirectInputModuleTest : public ::testing::Test { + public: + DirectInputModuleTest() + { + actions.inputFiles["empty.root"] = 0u; + actions.inputFiles["test1.root"] = 10u; + actions.inputFiles["test2.root"] = 20u; + } + + ModuleData data; + TestActions actions; + }; + + + + TEST_F (DirectInputModuleTest, simpleTest) + { + auto module = std::make_unique<DirectInputModule> (); + module->fileList = {"test1.root", "test2.root"}; + + actions.toProcessFiles = + { + {"test1.root", 0, 10}, + {"test2.root", 0, 20}, + }; + + ASSERT_SUCCESS (module->processInputs (data, actions)); + } + + + + TEST_F (DirectInputModuleTest, skipLimitTest) + { + auto module = std::make_unique<DirectInputModule> (); + module->fileList = {"test1.root", "test2.root"}; + module->skipEvents = 5u; + module->maxEvents = 10u; + + actions.toProcessFiles = + { + {"test1.root", 5, 10}, + {"test2.root", 0, 5}, + }; + + ASSERT_SUCCESS (module->processInputs (data, actions)); + } + + + + TEST_F (DirectInputModuleTest, skipFileTest) + { + auto module = std::make_unique<DirectInputModule> (); + module->fileList = {"test1.root", "test2.root"}; + module->skipEvents = 15u; + + actions.toProcessFiles = + { + {"test2.root", 5, 20}, + }; + + ASSERT_SUCCESS (module->processInputs (data, actions)); + } + + + + TEST_F (DirectInputModuleTest, limitFileTest) + { + auto module = std::make_unique<DirectInputModule> (); + module->fileList = {"test1.root", "test2.root"}; + module->maxEvents = 5u; + + actions.toProcessFiles = + { + {"test1.root", 0, 5}, + }; + + ASSERT_SUCCESS (module->processInputs (data, actions)); + } + + + + TEST_F (DirectInputModuleTest, emptyFileTest) + { + auto module = std::make_unique<DirectInputModule> (); + module->fileList = {"empty.root", "test1.root", "test2.root"}; + + actions.toProcessFiles = + { + {"empty.root", 0, 0}, + {"test1.root", 0, 10}, + {"test2.root", 0, 20}, + }; + ASSERT_SUCCESS (module->processInputs (data, actions)); + } + + + + TEST_F (DirectInputModuleTest, skipToEmptyTest) + { + auto module = std::make_unique<DirectInputModule> (); + module->fileList = {"test1.root", "empty.root", "test2.root"}; + module->skipEvents = 10u; + + actions.toProcessFiles = + { + {"empty.root", 0, 0}, + {"test2.root", 0, 20}, + }; + + ASSERT_SUCCESS (module->processInputs (data, actions)); + } + } +} + +ATLAS_GOOGLE_TEST_MAIN diff --git a/PhysicsAnalysis/D3PDTools/EventLoopAlgs/Root/NTupleSvc.cxx b/PhysicsAnalysis/D3PDTools/EventLoopAlgs/Root/NTupleSvc.cxx index 1c7183c063c7139ecd82d022b13d2fd490a38897..5ec8cc68e921e70f9fa022de25df9a10b53279bd 100644 --- a/PhysicsAnalysis/D3PDTools/EventLoopAlgs/Root/NTupleSvc.cxx +++ b/PhysicsAnalysis/D3PDTools/EventLoopAlgs/Root/NTupleSvc.cxx @@ -108,7 +108,7 @@ namespace EL while (!line.empty() && isspace (line[0])) line = line.substr (1); while (!line.empty() && isspace (line[line.size()-1])) - line = line.substr (0, line.size()-1); + line.pop_back(); if (!line.empty() && line[0] != '#') copyBranch (line); } diff --git a/PhysicsAnalysis/D3PDTools/EventLoopAlgs/test/gt_DuplicateChecker.cxx b/PhysicsAnalysis/D3PDTools/EventLoopAlgs/test/gt_DuplicateChecker.cxx index 543b417bdf114b6e4d795becdae463e2b5f820da..b97c20eee088c7da2351fb2353019bc9a2af489e 100644 --- a/PhysicsAnalysis/D3PDTools/EventLoopAlgs/test/gt_DuplicateChecker.cxx +++ b/PhysicsAnalysis/D3PDTools/EventLoopAlgs/test/gt_DuplicateChecker.cxx @@ -17,6 +17,7 @@ #include <AsgTools/StatusCode.h> #include <RootCoreUtils/Assert.h> #include <RootCoreUtils/ShellExec.h> +#include <RootCoreUtils/UnitTestDir.h> #include <SampleHandler/SampleLocal.h> #include <EventLoop/DirectDriver.h> #include <EventLoop/Job.h> @@ -150,26 +151,26 @@ TEST (DuplicateCheckerTest, all_tests) xAOD::TReturnCode::enableFailure(); xAOD::Init ().ignore(); - std::string prefix = "DuplicateCheckerSubmit"; + RCU::UnitTestDir dir ("EventLoopAlgs", "DuplicateChecker"); - RCU::Shell::exec ("rm -rf " + prefix + "[123]"); + std::string prefix = dir.path() + "/"; - if (makeXAOD ("test1.root", 1, 0).isFailure()) + if (makeXAOD (prefix + "test1.root", 1, 0).isFailure()) { FAIL() << "failed to make test file"; } - if (makeXAOD ("test2.root", 1, 8000).isFailure()) + if (makeXAOD (prefix + "test2.root", 1, 8000).isFailure()) { FAIL() << "failed to make test file"; } - if (makeXAOD ("test3.root", 2, 0).isFailure()) + if (makeXAOD (prefix + "test3.root", 2, 0).isFailure()) { FAIL() << "failed to make test file"; } std::unique_ptr<SH::SampleLocal> sample (new SH::SampleLocal ("sample")); - sample->add ("test1.root"); - sample->add ("test2.root"); - sample->add ("test3.root"); + sample->add (prefix + "test1.root"); + sample->add (prefix + "test2.root"); + sample->add (prefix + "test3.root"); SH::SampleHandler sh; sh.add (sample.release()); @@ -183,15 +184,15 @@ TEST (DuplicateCheckerTest, all_tests) { DirectDriver driver; - driver.submit (job, prefix + "1"); - checkHistograms (prefix + "1", 30000, 26000, true); + driver.submit (job, prefix + "submit1"); + checkHistograms (prefix + "submit1", 30000, 26000, true); } { LocalDriver driver; - driver.submit (job, prefix + "2"); - checkHistograms (prefix + "2", 30000, 27000, false); + driver.submit (job, prefix + "submit2"); + checkHistograms (prefix + "submit2", 30000, 27000, false); } - RCU::Shell::exec ("cmp " + prefix + "1/duplicates " + prefix + "2/duplicates"); + RCU::Shell::exec ("cmp " + prefix + "submit1/duplicates " + prefix + "submit2/duplicates"); } { @@ -199,14 +200,14 @@ TEST (DuplicateCheckerTest, all_tests) std::unique_ptr<DuplicateChecker> alg (new DuplicateChecker); alg->setEventInfoName ("MyEventInfo"); alg->setOutputTreeName ("summary"); - alg->addKnownDuplicatesFile (prefix + "1/duplicates"); + alg->addKnownDuplicatesFile (prefix + "submit1/duplicates"); job.algsAdd (alg.release()); job.sampleHandler (sh); { LocalDriver driver; - driver.submit (job, prefix + "3"); - checkHistograms (prefix + "3", 30000, 26000, true); + driver.submit (job, prefix + "submit3"); + checkHistograms (prefix + "submit3", 30000, 26000, true); } } } diff --git a/PhysicsAnalysis/D3PDTools/EventLoopGrid/CMakeLists.txt b/PhysicsAnalysis/D3PDTools/EventLoopGrid/CMakeLists.txt index 17925a4c51022f8ed47760cf39abd57dabf7381d..4aba3551a76fda6950560ca0a4a8b5aa7273946b 100644 --- a/PhysicsAnalysis/D3PDTools/EventLoopGrid/CMakeLists.txt +++ b/PhysicsAnalysis/D3PDTools/EventLoopGrid/CMakeLists.txt @@ -16,7 +16,7 @@ find_package( ROOT COMPONENTS Core RIO PyROOT Tree ) # Library in the package: atlas_add_root_dictionary( EventLoopGrid EventLoopGridCintDict - ROOT_HEADERS EventLoopGrid/GridDriver.h + ROOT_HEADERS EventLoopGrid/PrunDriver.h Root/LinkDef.h EXTERNAL_PACKAGES ROOT ) diff --git a/PhysicsAnalysis/D3PDTools/EventLoopGrid/EventLoopGrid/GridDriver.h b/PhysicsAnalysis/D3PDTools/EventLoopGrid/EventLoopGrid/GridDriver.h deleted file mode 100644 index 9e574e0dc56aeed241789caaacb498cedb82e594..0000000000000000000000000000000000000000 --- a/PhysicsAnalysis/D3PDTools/EventLoopGrid/EventLoopGrid/GridDriver.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration -*/ - -/// @author Alexander Madsen -/// @author Nils Krumnack - - - -#ifndef EVENT_LOOP_GRID_DRIVER_H -#define EVENT_LOOP_GRID_DRIVER_H - -#include "EventLoop/Driver.h" - -namespace SH { - class SampleGrid; - class MetaObject; -} - -namespace EL { - - std::string getRootCoreConfig (); - - /// \brief a \ref Driver to submit jobs via ganga - - class GridDriver final : public Driver { - - public: - - GridDriver (); - - void testInvariant () const; - - static void status(const std::string& location); - - static void status(); - - static void kill(const std::string& location); - - static void killAll(); - - static std::string listActive(); - - static void reset(); - - static bool startServer(); - - static bool stopServer(); - - std::string outputSampleName; - bool express; - bool mergeOutput; - std::string destSE; - std::string site; - std::string cloud; - int memory; - int maxCpuCount; - int nFiles; - int nFilesPerJob; - int nJobs; - int nMinorRetries; - int nMajorRetries; - float nGBPerJob; - std::string workDir; - std::string extFile; //To be made obsolete, please do not use! - std::string excludedSite; - std::string rootVer; - std::string cmtConfig; - - static std::string gangaLogFile; - - //These fields are currently not supported - //std::string excludeFile; - //bool noSubmit; //Should maybe enable this again - //int maxNFilesPerJob; //no clear way to do this in ganga - //int maxFileSize; //no clear way to do this in ganga - //std::string spaceToken; //users can only output to scratchdisk - //std::string useChirpServer; //ganga has this possibility - //bool enableRetries; //done by tasks - - //std::string downloadIncludeFileMask; - //std::string downloadExcludeFileMask; - - //These functions are kept for backwards compatibility. - //They should be considered obsolete and will be removed in a future version. - void gather(const std::string location) const; - SH::SampleGrid* createSampleFromDQ2(const std::string& dataset) const; - - protected: - virtual ::StatusCode - doManagerStep (Detail::ManagerData& data) const override; - - private: - ::StatusCode doRetrieve (Detail::ManagerData& data) const; - - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wpragmas" -#pragma GCC diagnostic ignored "-Wunknown-pragmas" -#pragma GCC diagnostic ignored "-Winconsistent-missing-override" - ClassDef(EL::GridDriver, 1); -#pragma GCC diagnostic pop - }; -} - -#endif diff --git a/PhysicsAnalysis/D3PDTools/EventLoopGrid/Root/GridDriver.cxx b/PhysicsAnalysis/D3PDTools/EventLoopGrid/Root/GridDriver.cxx deleted file mode 100644 index 2853c5f5a656c977d8fd109021a1ce1f8b4bf8f7..0000000000000000000000000000000000000000 --- a/PhysicsAnalysis/D3PDTools/EventLoopGrid/Root/GridDriver.cxx +++ /dev/null @@ -1,1227 +0,0 @@ -/* - Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration -*/ - -/// @author Alexander Madsen -/// @author Nils Krumnack - - - -#include "EventLoopGrid/GridDriver.h" -#include "EventLoop/Algorithm.h" -#include "EventLoop/Job.h" -#include "EventLoop/ManagerData.h" -#include "EventLoop/ManagerStep.h" -#include "EventLoop/MessageCheck.h" -#include "EventLoop/OutputStream.h" -#include <PathResolver/PathResolver.h> -#include "RootCoreUtils/Assert.h" -#include "RootCoreUtils/StringUtil.h" -#include <RootCoreUtils/ThrowMsg.h> -#include "RootCoreUtils/hadd.h" -#include "SampleHandler/MetaFields.h" -#include "SampleHandler/MetaObject.h" -#include "SampleHandler/Sample.h" -#include "SampleHandler/SampleGrid.h" -#include "SampleHandler/SampleHandler.h" -#include "TFile.h" -#include "TList.h" -#include "TRegexp.h" -#include "TSystem.h" -#include "TTree.h" -#include "TObjString.h" -#include "TObjArray.h" -#include "TROOT.h" -#include <algorithm> -#include <fstream> -#include <iomanip> -#include <iterator> -#include <list> -#include <map> -#include <memory> -#include <sstream> -#include <string> -#include <vector> - -ClassImp(EL::GridDriver) - -using namespace EL::msgEventLoop; - -namespace EL { - - std::string getRootCoreConfig () - { - boost::regex expr (".*-.*-.*-.*"); - - const char *ROOTCORECONFIG = getenv ("ROOTCORECONFIG"); - if (ROOTCORECONFIG) - { - if (RCU::match_expr (expr, ROOTCORECONFIG)) - return ROOTCORECONFIG; - ANA_MSG_ERROR ("invalid value \"" << ROOTCORECONFIG - << "\" for $ROOTCORECONFIG"); - } - const char *rootCmtConfig = getenv ("rootCmtConfig"); - if (rootCmtConfig) - { - if (RCU::match_expr (expr, rootCmtConfig)) - return rootCmtConfig; - ANA_MSG_ERROR ("invalid value \"" << rootCmtConfig - << "\" for $rootCmtConfig"); - } - ANA_MSG_WARNING ("no valid value for cmt config found."); - ANA_MSG_WARNING ("using \"x86_64-slc6-gcc47-opt\" instead."); - ANA_MSG_WARNING ("consider updating to a more recent RootCore version"); - ANA_MSG_WARNING ("or analysis release"); - return "x86_64-slc6-gcc47-opt"; - } - - - - std::string GridDriver::gangaLogFile = ""; - - namespace { - - const int dummyIdCompleted = -0xDEAD; - const int dummyIdFailed = -0xFA11; - - const char* cvmfsATLASLocalRootBase = - "/cvmfs/atlas.cern.ch/repo/ATLASLocalRootBase"; - const char* shAtlasLocalSetup = - ". ${ATLAS_LOCAL_ROOT_BASE}/user/atlasLocalSetup.sh"; - const char* gangaDir = - gSystem->ExpandPathName("~/gangadir-EL"); - - struct GangaTrfDef { - std::string sampleName; - std::string inDS; - std::string outDS; - std::string rootVer; - std::string inputSdBox; - std::string inputFiles; - std::string outputFiles; - std::string downloadDir; - std::string downloadMask; - std::string userArea; - }; - - struct EnvReqs { - bool needGanga; - bool needPanda; - bool needProxy; - bool needRootCore; - bool needDQ2; - EnvReqs() : needGanga(false), - needPanda(false), - needProxy(false), - needRootCore(false), - needDQ2(false) {} - }; - - /* - bool readJobInfo(const std::string location, bool printInfo = true); - */ - - /* - bool readJobInfo(const std::string location, Int_t &nEventsTotal, - Int_t &nFilesTotal, Float_t &realTimeTotal, - Float_t &cpuTimeTotal, Float_t &eventsPerRealSecondTotal, - Float_t &eventsPerCpuSecondTotal, bool printInfo); - */ - - /* - bool readJobInfo(const std::string location, Int_t &nEventsTotal, - Int_t &nFilesTotal, bool printInfo = false); - */ - - std::string getStrValues(const std::string &inLines, const std::string &key); - - const std::string pyList(const std::string &list); - - bool checkEnvironment(EnvReqs args); - - const std::string getNickname(); - - bool sendGangaCmd(const std::string &cmd); - - bool sendGangaCmd(const std::string &cmd, std::string &out); - - const std::string gangaTrfCmd(const GangaTrfDef &args, - const GridDriver &driver); - - int readTaskID(const std::string& location); - bool writeTaskID(const std::string& location, const int taskId); - - } //namespace - } // namespace EL - - void EL::GridDriver::testInvariant() const { - RCU_INVARIANT (this != 0); - } - - EL::GridDriver::GridDriver() : express(false), - mergeOutput(false), - memory(-1), - maxCpuCount(-1), - nFiles(0), - nFilesPerJob(0), - nJobs(-1), - nMinorRetries(4), - nMajorRetries(4), - nGBPerJob(13), - rootVer(gROOT->GetVersion()), - cmtConfig(getRootCoreConfig()) { - RCU_NEW_INVARIANT (this); - } - - -::StatusCode EL::GridDriver :: -doManagerStep (Detail::ManagerData& data) const -{ - ANA_CHECK (Driver::doManagerStep (data)); - switch (data.step) - { - case Detail::ManagerStep::submitJob: - { - //Create input sandbox dir with ELG files - //TODO: some error checks here - const std::string jobELGDir = data.submitDir + "/elg"; - const std::string taskIdFile = jobELGDir + "/taskID"; - const std::string dsContFile = jobELGDir + "/outputDQ2container"; - const std::string sandboxDir = jobELGDir + "/inputsandbox"; - const std::string downloadDir = jobELGDir + "/download"; - const std::string runShFile = sandboxDir + "/runjob.sh"; - //const std::string runShOrig = "$ROOTCOREBIN/data/EventLoopGrid/runjob.sh"; - const std::string runShOrig = PathResolverFindCalibFile("EventLoopGrid/runjob.sh"); - const std::string mergeShFile = sandboxDir + "/elg_merge"; - //const std::string mergeShOrig = - // "$ROOTCOREBIN/user_scripts/EventLoopGrid/elg_merge"; - const std::string mergeShOrig = PathResolverFindCalibFile("EventLoopGrid/elg_merge"); - const std::string jobDefFile = sandboxDir + "/jobdef.root"; - gSystem->Exec(Form("mkdir -p %s", jobELGDir.c_str())); - gSystem->Exec(Form("mkdir -p %s", sandboxDir.c_str())); - gSystem->Exec(Form("mkdir -p %s", downloadDir.c_str())); - gSystem->Exec(Form("cp %s %s", runShOrig.c_str(), runShFile.c_str())); - gSystem->Exec(Form("chmod +x %s", runShFile.c_str())); - gSystem->Exec(Form("cp %s %s", mergeShOrig.c_str(), mergeShFile.c_str())); - gSystem->Exec(Form("chmod +x %s", mergeShFile.c_str())); - - if (gSystem->Exec("ps aux | grep `whoami` | grep -e [g]anga > /dev/null") == 0) { - if (not stopServer()) { - ANA_MSG_ERROR ("Could not stop currently running ganga service"); - return ::StatusCode::FAILURE; - } - gSystem->Exec("sleep 3"); - } - - ANA_MSG_INFO ("Please wait while grid jobs are created, "); - ANA_MSG_INFO ("this can take several minutes..."); - - EnvReqs env; - env.needGanga = true; - env.needPanda = true; - env.needProxy = true; - env.needRootCore = true; - - if (not checkEnvironment(env)) - { - ANA_MSG_ERROR ("some issue with the environment?"); - return ::StatusCode::FAILURE; - } - - const std::string nickname = getNickname(); - if (nickname[0] == '\0') { - ANA_MSG_ERROR ("Failed to get grid nickname from panda"); - return ::StatusCode::FAILURE; - } - - TList outputs; - - {//Save the Algorithms and sample MetaObjects to be sent with the jobs - TFile f(jobDefFile.c_str(), "RECREATE"); - f.WriteTObject(&data.job->jobConfig(), "jobConfig", "SingleKey"); - - for (EL::Job::outputIter out = data.job->outputBegin(), - end = data.job->outputEnd(); out != end; ++out) { - outputs.Add(out->Clone()); - } - f.WriteTObject(&outputs, "outputs", "SingleKey"); - for (SH::SampleHandler::iterator sample = data.job->sampleHandler().begin(); - sample != data.job->sampleHandler().end(); ++sample) { - SH::MetaObject meta(*(*sample)->meta()); - meta.fetchDefaults(data.options); - f.WriteObject(&meta, (*sample)->name().c_str()); - //f.WriteObject((*sample)->meta(), (*sample)->name().c_str()); - } - f.Close(); - } - SH::MetaObject meta(data.options); - - std::map<std::string, SH::SampleHandler> outMap; // <label,samples> - std::list<std::string> outDSs; //Created dq2 datasets for output - - int iJob = 0; - - std::stringstream gangaCmd; - - for (SH::SampleHandler::iterator sample = data.job->sampleHandler().begin(); - sample != data.job->sampleHandler().end(); ++sample) { - - TString inDS; - TString outDS; - - iJob++; - - //If SampleGrid, loop over childs if SampleComposite - inDS = (*sample)->meta()->castString("nc_grid", (*sample)->name(), SH::MetaObject::CAST_NOCAST_DEFAULT); - - // Determine outDS name - if (not outputSampleName.empty()) - outDS = outputSampleName; - else //TODO: Decide on a good default here - outDS = "user.%nickname%.%in:name%"; - - outDS.ReplaceAll("%in:name%", (*sample)->name()); - outDS.ReplaceAll("%nickname%", nickname); - std::stringstream ss((*sample)->name()); - std::string item; - int field = 0; - while(std::getline(ss, item, '.')) { - std::stringstream sskey; - sskey << "%in:name[" << ++field << "]%"; - outDS.ReplaceAll(sskey.str(), item); - } - while (outDS.Index("%in:") != -1) { - int i1 = outDS.Index("%in:"); - int i2 = outDS.Index("%", i1+1); - TString metaName = outDS(i1+4, i2-i1-4); - outDS.ReplaceAll("%in:"+metaName+"%", - (*sample)->meta()->castString(std::string(metaName.Data()), "", SH::MetaObject::CAST_NOCAST_DEFAULT)); - } - outDS.ReplaceAll("/", ""); - if (!outDS.BeginsWith(Form("user.%s", nickname.c_str()))) { - //ERROR: Malformed outDS - ANA_MSG_ERROR ("Bad output dataset name: " << outDS); - ANA_MSG_ERROR ("It should begin with " << Form("user.%s", nickname.c_str())); - return ::StatusCode::FAILURE; - } - - std::string outputfilenames = "hist-output.root"; - { - TIter itr(&outputs); - TObject *obj = 0; - while ((obj = itr())) { - EL::OutputStream *os = dynamic_cast<EL::OutputStream*>(obj); - const std::string name = os->label() + ".root"; - outputfilenames += "," + name; - } - } - - gSystem->Exec(Form("mkdir -p %s", - (downloadDir + "/" + (*sample)->name()).c_str())); - - GangaTrfDef trfDef; - trfDef.sampleName = (*sample)->name(); - trfDef.inDS = inDS.Data(); - trfDef.outDS = outDS.Data(); - trfDef.inputSdBox = - extFile + (extFile.empty() ? "" : ",") + jobDefFile + "," + runShFile; - trfDef.inputFiles = - (*sample)->meta()->castString("nc_grid_filter", "*.root*", SH::MetaObject::CAST_NOCAST_DEFAULT); - trfDef.outputFiles = outputfilenames; - trfDef.downloadDir = downloadDir; - trfDef.downloadMask = "hist-output.root"; - trfDef.userArea = jobELGDir; - trfDef.rootVer = rootVer; - - gangaCmd << std::endl << "# Transform " << iJob << std::endl; - gangaCmd << gangaTrfCmd(trfDef, *this); - gangaCmd << std::endl; - - outDSs.push_back(std::string(outDS.Data())); - } - - std::string taskName = data.submitDir; - if (taskName.rfind('/') != std::string::npos) - taskName = taskName.substr(taskName.rfind('/')+1); - std::stringstream submitCmd; - submitCmd - << "# This file was generated by EventLoop GridDriver " - << "for EL job " << taskName << "\n" - << "t = AtlasTask()\n" - << "t.name = '" << taskName << "'\n" - << "t.comment += 'location:" << data.submitDir << "'\n" - << "t.float = 200\n" - << "app = Athena()\n" - << "app.athena_compile = True\n" - << "app.option_file = 'runjob.sh'\n" - << "app.atlas_exetype = 'EXE'\n" - << "app.useRootCore = True\n" - << "app.append_to_user_area = ['.root','.cxx','.C','.h']\n" - << "app.user_area_path = '" << jobELGDir << "'\n" - << "app.atlas_release='None'\n" - << "app.atlas_cmtconfig = '" << meta.castString (Job::optCmtConfig, cmtConfig) << "'\n" - << "app.prepare()\n" - << "app.atlas_release=''\n" - << "app.atlas_dbrelease=''\n" - << "config.Athena.EXE_MAXFILESIZE=2097152\n" - << "config.Configuration.autoGenerateJobWorkspace = False\n" - << "print\n" - << "print \"TaskID: %d\" % t.id\n" - << "print \"TaskContainer: %s\" % t.getContainerName()\n" - << gangaCmd.str() - << "t.run()\n" - << "import time\n" - << "time.sleep(60)\n"; - - { - std::ofstream of((jobELGDir + "/submit.py").c_str()); - of << submitCmd.str(); - of.close(); - } - - std::string curDir = gSystem->pwd(); - gSystem->cd(sandboxDir.c_str()); - std::string gangaMsg; - bool ok = sendGangaCmd(submitCmd.str(), gangaMsg); - gSystem->cd(curDir.c_str()); - - if (not ok) { - ANA_MSG_ERROR ("Could not start ganga service - job submission failed"); - return ::StatusCode::FAILURE; - } - - ANA_MSG_INFO ("Done. Call EL::GridDriver::status(\"" << data.submitDir - << "\") to follow the progress of your jobs."); - - int taskId = -1; - std::string container = getStrValues(gangaMsg, "TaskContainer: "); - std::istringstream(getStrValues(gangaMsg, "TaskID: ")) >> taskId; - RCU_ASSERT(taskId >= 0); - writeTaskID(data.submitDir, taskId); - - { - std::ofstream of(dsContFile.c_str()); - of << container; - of.close(); - } - - std::stringstream queryOutDsCmd; - queryOutDsCmd << "for tr in tasks(" << taskId << ").transforms: " - << "print \"%(in)s: %(out)s \" % " - << "{\"in\": tr.application.options, " - << "\"out\": tr.getContainerName()}\n"; - sendGangaCmd(queryOutDsCmd.str(), gangaMsg); - - for (SH::SampleHandler::iterator sample = data.job->sampleHandler().begin(); - sample != data.job->sampleHandler().end(); ++sample) { - std::string sampleOutDs = getStrValues(gangaMsg, (*sample)->name() + ": "); - if (*sampleOutDs.rbegin() == '\n') - sampleOutDs = sampleOutDs.substr(0, sampleOutDs.size()-1); - if (*sampleOutDs.rbegin() == ' ') - sampleOutDs = sampleOutDs.substr(0, sampleOutDs.size()-1); - - //Create a sample for each output and add it to that output's handler - for (EL::Job::outputIter out=data.job->outputBegin(); - out != data.job->outputEnd(); ++out) { - SH::SampleGrid * mysample = new SH::SampleGrid((*sample)->name()); - mysample->meta()->setString("nc_grid", sampleOutDs); - mysample->meta()->setString("nc_grid_filter", "*" + out->label() + ".root*"); - outMap[out->label()].add(mysample); - } - SH::SampleGrid * mysample=new SH::SampleGrid((*sample)->name()); - mysample->meta()->setString("nc_grid", sampleOutDs); - mysample->meta()->setString("nc_grid_filter", "hist-output.root"); - //mysample->setMetaDouble("norigfiles", nFilesInInputDS); - outMap["hist"].add(mysample); - } - - //Print the created dq2 datasets - //This is broken as Tasks appends extra fluff to the ds names - //outDSs.sort(); - //outDSs.unique(); - //cout << "The following dq2 output datasets will be created:" << endl; - //for (list<std::string>::iterator it=outDSs.begin(); it!=outDSs.end(); ++it) { - // cout << *it << endl; - //} - - // //Print link to Analysis Task Monitor - // std::stringstream url; - // url << "https://dashb-atlas-prodsys-prototype.cern.ch/templates/task-analysis/#user=default"; - // url << "&table=Mains"; - // url << "&p=1"; - // url << "&records=" << outDSs.size() + 1; - // url << "&activemenu=1"; - // url << "&timerange=lastMonth"; - // //url << "&from="; - // //url << "&till="; - // //url << "&refresh=0"; - // url << "&pattern="; - // url << container << "/+%7C%7C+"; - // //for (list<std::string>::iterator it=outDSs.begin(); it!=outDSs.end(); ++it) { - // // url << *it << "/+%7C%7C+"; - // //} - // cout << "You can follow the job progress using the Analysis Task Monitor:"; - // cout << endl << url.str() << endl; - // cout << "(Please note: this service is still under development!)\n"; - - //Save the output Sample Handlers - for (EL::Job::outputIter output=data.job->outputBegin(), - end=data.job->outputEnd(); output != end; ++output) - { - outMap[output->label()].fetch(data.job->sampleHandler()); - outMap[output->label()].save(data.submitDir + "/output-" + output->label()); - }; - outMap["hist"].fetch(data.job->sampleHandler()); - outMap["hist"].save(data.submitDir + "/output-hist"); - } - data.submitted = true; - break; - - case Detail::ManagerStep::doRetrieve: - { - ANA_CHECK (doRetrieve (data)); - } - break; - - default: - (void) true; // safe to do nothing - } - return ::StatusCode::SUCCESS; -} - - -::StatusCode EL::GridDriver::doRetrieve(Detail::ManagerData& data) const { - RCU_READ_INVARIANT(this); - - const std::string hist_sh_dir = data.submitDir + "/output-hist"; - const std::string jobDownloadDir = data.submitDir + "/elg/download"; - const std::string dsContFile = data.submitDir + "/elg/outputDQ2container"; - - EnvReqs env; - env.needGanga = true; - env.needProxy = true; - if (not checkEnvironment(env)) { - //In case of failure, errors are already reported by checkEnvironment() - return false; - } - - SH::SampleHandler sh; - sh.load(hist_sh_dir); - - int taskId = readTaskID(data.submitDir); - if (taskId == dummyIdCompleted) { - ANA_MSG_INFO ("Job has already been completed and output downloaded"); - return true; - } - if (taskId == dummyIdFailed) { - ANA_MSG_INFO ("The job has failed permanently and will not be tried again"); - return true; - } - RCU_ASSERT(taskId >= 0); //At this point, taskId must be that of a real task - - std::stringstream cmd; - cmd << "for [x,y] in [[u,t] for t in tasks(" << taskId << ").transforms " - << "for u in t.units]: print " - << "\"elgunit: %s %s %s\" % (y.application.options, x.name, x.status)"; - - std::string out; - bool ok = sendGangaCmd(cmd.str(), out); - - if (not ok) { - ANA_MSG_ERROR ("Could not start ganga server"); - return false; - } - - const std::string unitList = getStrValues(out, "elgunit: "); - - if (unitList.empty()) { - ANA_MSG_ERROR ("Could not get the list of task partitions from ganga"); - ANA_MSG_ERROR ("Possibly the task registry has been lost"); - ANA_MSG_ERROR ("or the job submission failed"); - return false; - } - - bool isIncomplete = false; //At least one unit is not completed - bool isRunning = false; //At least one unit is still running - bool isFailed = false; //At least one unit is failed - int nTotalUnits = 0; - int nTotalUnitsCompleted = 0; - int nTotalTransformsCompleted = 0; - - for (SH::SampleHandler::iterator sample = sh.begin(); - sample != sh.end(); ++sample) { - - const std::string mergedHistFile - = data.submitDir + "/hist-" + (*sample)->name() + ".root"; - - bool transformCompleted = true; - std::string filesToMerge; - - std::istringstream units(unitList); - int nUnits = 0; - std::string entry; - while (getline(units, entry)) { - std::string sampleName, unitName, unitStatus; - std::istringstream s(entry); - s >> sampleName; - s >> unitName; - s >> unitStatus; - - if ((*sample)->name() != sampleName) continue; - nUnits++; - bool unitCompleted = (unitStatus == "completed"); - bool unitFailed = (unitStatus == "failed"); - bool unitBad = (unitStatus == "bad"); - nTotalUnits++; - if (unitCompleted) nTotalUnitsCompleted++; - if (unitFailed || unitBad) isFailed = true; - if (not unitCompleted && not unitFailed && not unitBad) isRunning=true; - if (not unitCompleted) { - isIncomplete = true; - transformCompleted = false; - continue; - } - - const std::string unitDir = - jobDownloadDir + "/" + (*sample)->name() + "/" + unitName; - const std::string findCmd - = "find " + unitDir + " -name \"*.hist-output.root*\" | tr '\n' ' '"; - filesToMerge += gSystem->GetFromPipe(findCmd.c_str()).Data(); - } - if (transformCompleted) nTotalTransformsCompleted++; - if (nUnits == 0) transformCompleted = false; - if (transformCompleted) { - if (gSystem->Exec(Form("[ -f %s ]", mergedHistFile.c_str()))) { - if (filesToMerge.empty()) { //Debug - ANA_MSG_ERROR ("Did not find files to merge"); - ANA_MSG_ERROR ("ganga response was " << out); - ANA_MSG_ERROR ("Stopping here"); - return false; - } - - std::istringstream iss(filesToMerge); - std::set<std::string> input((std::istream_iterator<std::string>(iss)), - std::istream_iterator<std::string>()); - RCU::hadd(mergedHistFile.c_str(), std::vector<std::string>(input.begin(), input.end())); - - gSystem->Exec(Form("rm %s", filesToMerge.c_str())); - } - } - } - - ANA_MSG_INFO (nTotalTransformsCompleted << "/" << sh.size() - << " samples processed (" << nTotalUnitsCompleted << "/" - << nTotalUnits << " jobs completed)"); - - if (isRunning) return false; - - //At this point, task should be either completed or failed - if (isFailed != isIncomplete) { - std::cerr << "At this point, task should be either completed or failed\n" - << "somehow it's not" << std::endl; - } - - if (isFailed) { - std::cerr << "The job has failed and has reached maximum number of retries."; - std::cerr << std::endl << "It will not be tried again. Sorry." << std::endl; - } - - std::stringstream removeCmd; - removeCmd << "tasks(" << taskId << ").remove(remove_jobs=True)"; - if (sendGangaCmd(removeCmd.str())) { - writeTaskID(data.submitDir, isFailed ? dummyIdFailed : dummyIdCompleted); - } else { - std::cerr << "Warning: The job has stopped running but could not be removed "; - std::cerr << "from ganga. Please try calling retrieve() again later to "; - std::cerr << "clean up the job records." << std::endl; - } - - env.needProxy = true; - env.needDQ2 = true; - if (checkEnvironment(env)) { - std::string container; - std::ifstream f(dsContFile.c_str()); - f >> container; - f.close(); - std::cout << "The following DQ2 containers have been created:" << std::endl; - gSystem->Exec(("dq2-ls \"" + - container.substr(0,container.length()-1) + - ".*/\"").c_str()); - } - - data.retrieved = true; - data.completed = true; - return ::StatusCode::SUCCESS; -} - -void EL::GridDriver::status(const std::string& location) { - const std::string hist_sh_dir = location + "/output-hist"; - const std::string dsContFile = location + "/elg/outputDQ2container"; - SH::SampleHandler sh; - sh.load(hist_sh_dir); - - EnvReqs env; - env.needGanga = true; - env.needProxy = true; - if (not checkEnvironment(env)) return; - - int taskId = readTaskID(location); - if (taskId == dummyIdCompleted) { - ANA_MSG_INFO ("Job is completed completed and output downloaded"); - return; - } else if (taskId == dummyIdFailed) { - ANA_MSG_INFO ("The job has failed permanently and will not be tried again"); - return; - } - RCU_ASSERT(taskId >= 0); - - std::stringstream cmd; - cmd << "for [x,y] in [[u,t] for t in tasks(" << taskId << ").transforms " - << "for u in t.units]: print " - << "\"unit: %s %s\" % (x.name, x.status)"; - std::string out; - bool ok = sendGangaCmd(cmd.str(), out); - if (not ok) { - ANA_MSG_ERROR ("Could not start ganga server"); - return; - } - const std::string unitList = getStrValues(out, "unit: "); - if (unitList.empty()) { - ANA_MSG_ERROR ("Could not get the list of task partitions from ganga"); - ANA_MSG_ERROR ("Possibly the task registry has been lost"); - return; - } - ANA_MSG_INFO (unitList); -} - -void EL::GridDriver::status() { - std::istringstream tasks(listActive()); - std::string location; - while (getline(tasks, location)) { - ANA_MSG_INFO (location); - status(location); - } -} - -void EL::GridDriver::kill(const std::string& location) { - EnvReqs env; - env.needGanga = true; - env.needProxy = true; - if (not checkEnvironment(env)) return; - - std::stringstream removeCmd; - int taskId = readTaskID(location); - if (taskId > 0) { - removeCmd << "tasks(" << taskId << ").pause()"; - removeCmd << "tasks(" << taskId << ").remove(remove_jobs=True)"; - if (sendGangaCmd(removeCmd.str())) { - writeTaskID(location, dummyIdFailed); - } else { - std::cerr << "Error: Could not remove the job at " << location << std::endl; - } - } else if (taskId == dummyIdCompleted) { - ANA_MSG_INFO ("The job is already completed!"); - } -} - -void EL::GridDriver::killAll() { - std::istringstream tasks(listActive()); - std::string location; - ANA_MSG_INFO ("Killing jobs:"); - while (getline(tasks, location)) { - ANA_MSG_INFO (location); - kill(location); - } -} - -std::string EL::GridDriver::listActive() { - std::string gangaMsg; - sendGangaCmd("for t in tasks: print t.comment", gangaMsg); - return getStrValues(gangaMsg, "location:"); -} - -bool EL::GridDriver::startServer() { - return sendGangaCmd(""); -} - -bool EL::GridDriver::stopServer() { - EnvReqs env; - env.needGanga = true; - if (not checkEnvironment(env)) { - ANA_MSG_INFO ("Failed to set up ganga"); - return false; - } - const std::string gangaBin = gSystem->GetFromPipe("command -v ganga").Data(); - const std::string gangaInstall = gangaBin.substr(0, gangaBin.find("/bin/ganga")); - const std::string gangaPythonDir = gangaInstall + "/python"; - std::stringstream cmd; - cmd << "export PYTHONPATH=$PYTHONPATH:" << gangaPythonDir << "\n" - << "python << EOL\n" - << "from GangaService.Lib.ServiceAPI.ServiceAPI import GangaService\n" - << "gs = GangaService()\n" - << "gs.gangadir = \"" << gangaDir << "\"\n" - << "gs.killServer()\n" - << "EOL\n"; - return (gSystem->Exec(cmd.str().c_str()) == 0); -} - -void EL::GridDriver::reset() { - gSystem->Exec(Form("rm -rf %s", gangaDir)); -} - -namespace EL { - namespace { - - //The functions in this namespace could be split into a separate "utilities" - //module. In the interest of one day merging this package with the main EL - //package, they are kept here to keep the number of EventLoopGrid related - //files to a minimum. - - /* - //TODO: This function is currently not used as the sample meta data - //are not filled correctly during submission - bool readJobInfo(const std::string location, - Int_t &nEventsTotal, - Int_t &nFilesTotal, - Float_t &realTimeTotal, - Float_t &cpuTimeTotal, - Float_t &eventsPerRealSecondTotal, - Float_t &eventsPerCpuSecondTotal, - bool printInfo) { - - TFile f(location.c_str()); - - if (f.IsZombie() || not f.IsOpen()) { - cerr << "Failed to oen file " << location << endl; - return false; - } - - TTree *tree = (TTree*)f.Get("EventLoopGridJobInfo"); - - if (tree == 0) { - cerr << "Could not read tree 'EventLoopGridJobInfo' "; - cerr << "in file " << location << ". "; - cerr << "File is corrupt or is not an EventLoopGrid histogram collection"; - cerr << endl; - return false; - } - - nEventsTotal = 0; - nFilesTotal = 0; - realTimeTotal = 0; - cpuTimeTotal = 0; - eventsPerRealSecondTotal = 0; - eventsPerCpuSecondTotal = 0; - - Int_t nEvents = 0; - Int_t nFiles = 0; - Float_t realTime = 0; - Float_t cpuTime = 0; - Float_t eventsPerRealSecond = 0; - Float_t eventsPerCpuSecond = 0; - - TBranch *bnEvents = tree->GetBranch("eventsRead"); - TBranch *bnFiles = tree->GetBranch("filesRead"); - TBranch *brealTime = tree->GetBranch("loopWallTime"); - TBranch *bcpuTime = tree->GetBranch("loopCpuTime"); - TBranch *beventsPerRealSecond = tree->GetBranch("eventsPerWallSec"); - TBranch *beventsPerCpuSecond = tree->GetBranch("eventsPerCpuSec"); - - bnEvents->SetAddress(&nEvents); - bnFiles->SetAddress(&nFiles); - brealTime->SetAddress(&realTime); - bcpuTime->SetAddress(&cpuTime); - beventsPerRealSecond->SetAddress(&eventsPerRealSecond); - beventsPerCpuSecond->SetAddress(&eventsPerCpuSecond); - - if (printInfo) { - cout << "Job information for file " << location << ":" << endl; - } - - for (Int_t i = 0; i < tree->GetEntriesFast(); i++) { - bnEvents->GetEvent(i); - bnFiles->GetEvent(i); - brealTime->GetEvent(i); - bcpuTime->GetEvent(i); - beventsPerRealSecond->GetEvent(i); - beventsPerCpuSecond->GetEvent(i); - - if (printInfo) { - cout << " Job " << setw(3) << setfill('0') << i; - cout << " Input files read: " << nFiles; - cout << " Events read: " << nEvents; - cout << " Loop real time: " << realTime; - cout << " Loop CPU time: " << cpuTime; - cout << " events per real second: " << eventsPerRealSecond; - cout << " events per cpu second: " << eventsPerCpuSecond; - cout << endl; - } - - nEventsTotal += nEvents; - nFilesTotal += nFiles; - realTimeTotal += realTime; - cpuTimeTotal += cpuTimeTotal; - } - - eventsPerRealSecondTotal - = nEventsTotal / (realTimeTotal != 0 ? realTimeTotal : 1.); - eventsPerCpuSecondTotal - = nEventsTotal / (cpuTimeTotal != 0 ? cpuTimeTotal : 1.); - - f.Close(); - - return true; - } - */ - - /* - bool readJobInfo(const std::string location, bool printInfo) { - Int_t nEventsTotal = 0, nFilesTotal = 0; - Float_t realTimeTotal = 0, cpuTimeTotal = 0; - Float_t eventsPerRealSecondTotal = 0, eventsPerCpuSecondTotal = 0; - return readJobInfo(location, nEventsTotal, nFilesTotal, realTimeTotal, - cpuTimeTotal, eventsPerRealSecondTotal, - eventsPerCpuSecondTotal, printInfo); - } - */ - - /* - bool readJobInfo(const std::string location, - Int_t &nEventsTotal, - Int_t &nFilesTotal, - bool printInfo) { - Float_t realTimeTotal = 0, cpuTimeTotal = 0; - Float_t eventsPerRealSecondTotal = 0, eventsPerCpuSecondTotal = 0; - return readJobInfo(location, nEventsTotal, nFilesTotal, realTimeTotal, - cpuTimeTotal, eventsPerRealSecondTotal, - eventsPerCpuSecondTotal, printInfo); - } - */ - - //This function extracts substrings from a text, - //It is used to parse the output from GangaService. - //Returns: "value" from every substring matching "key: value" - std::string getStrValues(const std::string &inLines, const std::string &key) { - std::string out; - TString res(inLines); - TObjArray *lines = res.Tokenize("\n"); - TIter itr(lines); - TObject *obj = 0; - while ((obj = itr())) { - TString l = ((TObjString*)obj)->String(); - if (l.Index(key.c_str()) != -1) { - TString val = l(l.Index(key.c_str())+key.length(), l.Length()); - out += val.Data(); - out += "\n"; - } - } - delete lines; - return out; - } - - //This function takes a string like "var1 var2 var3" and formats it as a - //python list of strings. It is used in the creation of python commands. - //Returns: A string of format "['var1','var2','var3']". - const std::string pyList(const std::string &list) { - if (list[0] == '\0') return ""; - TString s(("['" + list + "']").c_str()); - s.ReplaceAll(" ", ""); - s.ReplaceAll(",", "','"); - return s.Data(); - } - - //This function performs a number of checks on the environment we are - //running in. If necessary, it tries to set up the requested components. - bool checkEnvironment(EnvReqs env) { - - if (env.needProxy) env.needPanda = true; - - // if (env.needRootCore && not gSystem->Getenv("ROOTCOREBIN")) { - // cerr << "Error: Must set up RootCore first" << endl; - // return false; - // } - - env.needPanda &= bool(gSystem->Exec("command -v prun >/dev/null")); - env.needGanga &= bool(gSystem->Exec("command -v ganga >/dev/null")); - - bool needALRB = (env.needPanda || env.needGanga); - needALRB &= ( ! gSystem->Getenv("ATLAS_LOCAL_ROOT_BASE")); - - if (needALRB) { - if (gSystem->Exec(Form("[ -d %s ]", cvmfsATLASLocalRootBase)) == 0) - gSystem->Setenv("ATLAS_LOCAL_ROOT_BASE", cvmfsATLASLocalRootBase); - else { - ANA_MSG_ERROR ("variable ATLASLocalRootBase is not set"); - ANA_MSG_ERROR ("and CVMFS ATLASLocalRootBase is not available -"); - ANA_MSG_ERROR ("can't set up ATLAS software on this computer."); - return false; - } - } - - if (env.needPanda || env.needGanga) { - try { - std::stringstream cmd; - cmd << "set > __env1" << std::endl; - cmd << shAtlasLocalSetup << " > /dev/null 2>&1" << std::endl; - if (env.needPanda) - cmd << "localSetupPandaClient --noAthenaCheck >/dev/null 2>&1"<<std::endl; - if (env.needGanga) - cmd << "localSetupGanga > /dev/null 2>&1" << std::endl; - if (env.needDQ2) - cmd << "localSetupDQ2Client --quiet" << std::endl; - cmd << "set > __env2" << std::endl; - cmd << "grep -v -Fx -f __env1 __env2" << std::endl; - cmd << "rm -f __env1 __env2" << std::endl; - std::string env = gSystem->GetFromPipe(cmd.str().c_str()).Data(); - std::istringstream ssEnv(env); - std::string line; - while(getline(ssEnv, line)) { - TString s(line.c_str()); - gSystem->Setenv(TString(s(0,s.Index("="))).Data(), - TString(s(s.Index("=")+1, s.Length())).Data()); - } - } - catch (...) { - ANA_MSG_ERROR ("Error setting up ATLAS software, see log for details"); - gSystem->Exec("rm -f __env1 __env2"); - return false; - } - } - - if (env.needPanda && bool(gSystem->Exec("command -v prun>/dev/null"))) { - ANA_MSG_ERROR ("Error setting up panda!!"); - return false; - } - - for (int retry = 0; env.needProxy && retry < 3; retry++) { - const char pyCheckProxy[] = - "from pandatools import PsubUtils;" - "PsubUtils.checkGridProxy('',False,False,None);"; - env.needProxy &= - bool(gSystem->Exec(Form("python -c \"%s\"", pyCheckProxy))); - } - - if (env.needProxy) { - ANA_MSG_ERROR ("Failed to set up grid proxy"); - return false; - } - - return true; - } - - //This function returns the users grid nickname. - //It is the callers responsibility to ensure that panda tools - //and voms proxy are available. - const std::string getNickname() { - const char pyGetNickName[] = - "from pandatools import PsubUtils;" - "print (PsubUtils.getNickname());"; - return gSystem->GetFromPipe(Form("python -c \"%s\" 2>/dev/null", - pyGetNickName)).Data(); - } - - //This function sends a command to the ganga service. - //Returns: true if the command was sent succesfully, otherwise false. - bool sendGangaCmd(const std::string &cmd, std::string &out) { - - EnvReqs env; - env.needGanga = true; - env.needProxy = true; - if (not checkEnvironment(env)) return false; - - const std::string gangaCfg = "RUNTIME_PATH=GangaAtlas:GangaPanda"; - const std::string gangaArg = "-o[Configuration]" + gangaCfg; - const std::string gangaBin = gSystem->GetFromPipe("command -v ganga").Data(); - const std::string gangaInstall = gangaBin.substr(0, gangaBin.find("/bin/ganga")); - const std::string gangaPythonDir = gangaInstall + "/python"; - - std::stringstream sendCmd; - sendCmd - << "export PYTHONPATH=$PYTHONPATH:" << gangaPythonDir << "\n" - << "python << EOL\n" - << "from GangaService.Lib.ServiceAPI.ServiceAPI import GangaService\n" - << "import time\n" - << "gs = GangaService()\n" - << "gs.gangadir = \"" << gangaDir << "\"\n" - << "gs.gangacmd = \"" << gangaBin << " " << gangaArg << "\"\n" - << "gs.timeout = 24*60\n" - << "if not gs.startServer():\n" - << " gs.killServer()\n" - << " if not gs.startServer():\n" - << " time.sleep(30)\n" - << " if not gs.startServer():\n" - << " gs.killServer()\n" - << " time.sleep(60)\n" - << " if not gs.startServer():\n" - << " print (\"Failed to start server\")\n" - << " exit(0)\n" - << "print (gs.sendCmd(\"\"\"\n" - << cmd - << "\"\"\"))\n" - << "EOL\n"; - - out = gSystem->GetFromPipe(sendCmd.str().c_str()).Data(); - - if (out.find("Failed to start server") != std::string::npos) return false; - - if (not GridDriver::gangaLogFile.empty()) { - std::ofstream f(GridDriver::gangaLogFile.c_str(), std::ios::out | std::ios::app); - f << "COMMAND:" << std::endl << cmd << std::endl; - f << "RESPONSE:" << std::endl << out << std::endl; - f.close(); - } - - std::istringstream stream(out); - std::string line; - while (std::getline(stream, line)) { - if (line.find(": ERROR") != std::string::npos) { - std::cout << line << std::endl; - } - } - - return true; - } - - bool sendGangaCmd(const std::string &cmd) { - std::string out; - return sendGangaCmd(cmd, out); - } - - //This function generates python code to create a Ganga Transform - const std::string gangaTrfCmd(const GangaTrfDef &args, - const GridDriver &driver) { - RCU_REQUIRE("" != args.sampleName); - RCU_REQUIRE("" != args.inDS); - RCU_REQUIRE("" != args.outDS); - RCU_REQUIRE("" != args.inputSdBox); - RCU_REQUIRE("" != args.outputFiles); - RCU_REQUIRE("" != args.downloadDir); - - std::stringstream gangaCmd; - gangaCmd << "trf = AtlasTransform()" << std::endl; - gangaCmd << "trf.name = '" - << args.outDS.substr(args.outDS.find('.', 5) + 1) << "'" << std::endl; - gangaCmd << "t.appendTransform(trf)" << std::endl; - if (*args.inDS.rbegin() == '/') { - gangaCmd << "trf.initializeFromContainer('" - << args.inDS << "')" << std::endl; - } else { - gangaCmd << "trf.initializeFromDatasets(" - << pyList(args.inDS) << ")" << std::endl; - } - gangaCmd << "for u in trf.units: u.inputdata.names_pattern = " - << pyList(args.inputFiles) << std::endl; - gangaCmd << "trf.setRunLimit(" - << driver.nMinorRetries + driver.nMajorRetries << ")" << std::endl; - gangaCmd << "trf.setMinorRunLimit(" << driver.nMinorRetries << ")" << std::endl; - gangaCmd << "trf.setMajorRunLimit(" << driver.nMajorRetries << ")" << std::endl; - gangaCmd << "trf.application = app" << std::endl; - gangaCmd << "trf.application.options = '" << args.sampleName << "'" << std::endl; - gangaCmd << "trf.outputdata = DQ2OutputDataset()" << std::endl; - gangaCmd << "trf.outputdata.datasetname = '" << args.outDS << "'" << std::endl; - gangaCmd << "trf.outputdata.outputdata = " - << pyList(args.outputFiles) << std::endl; - gangaCmd << "trf.copy_output = DQ2OutputDataset()" << std::endl; - gangaCmd << "trf.copy_output.datasetname = '" << args.outDS << "'" << std::endl; - gangaCmd << "trf.copy_output.outputdata = " - << pyList(args.outputFiles) << std::endl; - gangaCmd << "trf.backend = Jedi()" << std::endl; - gangaCmd << "trf.backend.requirements.rootver = '" - << args.rootVer << "'" << std::endl; - gangaCmd << "trf.local_location = '" - << args.downloadDir + "/" + args.sampleName << "'" << std::endl; - gangaCmd << "trf.include_file_mask = " - << pyList(args.downloadMask) << std::endl; - if (0 < driver.nFiles) - gangaCmd << "for u in trf.units: u.inputdata.number_of_files = " - << driver.nFiles << std::endl; - if (0 < driver.nFilesPerJob) - gangaCmd << "trf.files_per_job = " << driver.nFilesPerJob << std::endl; - if (0 < driver.nGBPerJob) - gangaCmd << "trf.MB_per_job = " << (int)(driver.nGBPerJob*1000) << std::endl; - if (0 < driver.nJobs) - gangaCmd << "trf.subjobs_per_unit = " << driver.nJobs << std::endl; - if (not driver.destSE.empty()) - gangaCmd << "trf.outputdata.location = '" << driver.destSE << "'" << std::endl; - if (not driver.site.empty()) - gangaCmd << "trf.backend.site = '" << driver.site << "'" << std::endl; - if (0 < driver.memory) - gangaCmd << "trf.backend.requirements.memory = " << driver.memory << std::endl; - if (0 < driver.maxCpuCount) - gangaCmd << "trf.backend.requirements.cputime = " - << driver.maxCpuCount << std::endl; - if (driver.express) - gangaCmd << "trf.backend.requirements.express = True" << std::endl; - if (driver.mergeOutput) - gangaCmd << "trf.backend.requirements.enableMerge = True" << std::endl; - gangaCmd << "trf.backend.requirements.configMerge['exec'] = " - << "'elg_merge jobdef.root %OUT %IN'" << std::endl; - if (not driver.excludedSite.empty()) - gangaCmd << "trf.backend.requirements.excluded_sites = " - << pyList(driver.excludedSite) << std::endl; - if (not driver.cloud.empty()) - gangaCmd << "trf.backend.requirements.cloud = " << driver.cloud << std::endl - << "trf.backend.requirements.anyCloud = False" << std::endl; - return gangaCmd.str(); - } - - int readTaskID(const std::string& location) { - RCU_REQUIRE(not location.empty()); - const std::string taskIdFile = location + "/elg/taskID"; - int taskId = -1; - std::ifstream f; - try { - f.open(taskIdFile.c_str()); - f >> taskId; - } catch (...) { - std::cerr << "Could not read taskID from " << taskIdFile << std::endl; - } - f.close(); - return taskId; - } - - bool writeTaskID(const std::string& location, const int taskId) { - RCU_REQUIRE(not location.empty()); - const std::string taskIdFile = location + "/elg/taskID"; - try { - std::ofstream f(taskIdFile.c_str()); - f << taskId; - f.close(); - } catch (...) { - return false; - } - return true; - } - - } //namespace -} //namespace EL - - - - -//Method stubs for obsolete functions - -void EL::GridDriver::gather(const std::string location) const { - RCU_READ_INVARIANT(this); - std::cerr << "GridDriver::gather(): This function is obsolete and "; - std::cerr << "will be removed in a future version. "; - std::cerr << "Please use retrieve() or wait() instead." << std::endl; - retrieve(location); -} - -SH::SampleGrid* EL::GridDriver::createSampleFromDQ2(const std::string& dataset) - const { - RCU_READ_INVARIANT(this); - std::cerr << "GridDriver::createSampleFromDQ2(): This function is obsolete and "; - std::cerr << "will be removed in a future version. "; - std::cerr << "Please use SH::scanDQ2() instead." << std::endl; - std::string name=dataset; - size_t pos=0; - while((pos=name.find("/", pos)) != std::string::npos) - name.replace(pos++, 1, "_"); - SH::SampleGrid * mysample=new SH::SampleGrid(name); - mysample->meta()->setString("nc_grid", dataset); - mysample->meta()->setString("nc_grid_filter", "*"); - return mysample; -} diff --git a/PhysicsAnalysis/D3PDTools/EventLoopGrid/Root/LinkDef.h b/PhysicsAnalysis/D3PDTools/EventLoopGrid/Root/LinkDef.h index 50230abf5b74cf7f75543e04fe1aa235a7a0515d..6b3f7ccfe970c4baca6a792f71c0ea3256db1238 100644 --- a/PhysicsAnalysis/D3PDTools/EventLoopGrid/Root/LinkDef.h +++ b/PhysicsAnalysis/D3PDTools/EventLoopGrid/Root/LinkDef.h @@ -2,9 +2,6 @@ Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration */ -#include <EventLoopGrid/GridDriver.h> -#include <EventLoopGrid/PrunDriver.h> - #ifdef __CINT__ #pragma link off all globals; @@ -14,7 +11,6 @@ #pragma link C++ namespace EL; -#pragma link C++ class EL::GridDriver+; #pragma link C++ class EL::PrunDriver+; #pragma link C++ function EL::getRootCoreConfig (); diff --git a/PhysicsAnalysis/D3PDTools/EventLoopGrid/Root/PrunDriver.cxx b/PhysicsAnalysis/D3PDTools/EventLoopGrid/Root/PrunDriver.cxx index 31957fbaced9873f7f467b07308cb9fe3b133185..2dddffaafe02baef8201d357020a57bbf1212598 100644 --- a/PhysicsAnalysis/D3PDTools/EventLoopGrid/Root/PrunDriver.cxx +++ b/PhysicsAnalysis/D3PDTools/EventLoopGrid/Root/PrunDriver.cxx @@ -1,5 +1,5 @@ /* - Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration + Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration */ /// @author Alexander Madsen @@ -8,7 +8,6 @@ #include <EventLoopGrid/PrunDriver.h> -#include <EventLoopGrid/GridDriver.h> #include <EventLoop/Algorithm.h> #include <EventLoop/ManagerData.h> #include <EventLoop/ManagerStep.h> @@ -39,6 +38,7 @@ #include <sstream> #include <string> #include <vector> +#include <stdexcept> #include <boost/algorithm/string.hpp> @@ -61,10 +61,13 @@ namespace { if (what == name[i]) { return static_cast<Enum>(i); } } RCU_ASSERT0("Failed to parse job state string"); - throw; //compiler dummy + throw std::runtime_error("PrunDriver.cxx: Failed to parse job state string"); //compiler dummy } } + // When changing the values in the enum make sure + // corresponding values in `data/ELG_jediState.py` script + // are changed accordingly namespace Status { //static const int NSTATES = 3; enum Enum { DONE=0, PENDING=1, FAIL=2 }; @@ -86,7 +89,7 @@ namespace { struct TmpCd { const std::string origDir; - TmpCd(std::string dir) + TmpCd(const std::string & dir) : origDir(gSystem->pwd()) { gSystem->cd(dir.c_str()); @@ -131,8 +134,8 @@ static JobState::Enum nextState(JobState::Enum state, Status::Enum status) return TABLE[i].toState; } } - RCU_ASSERT0("Missing state transtion rule"); - throw; //compiler dummy + RCU_ASSERT0("Missing state transition rule"); + throw std::logic_error("PrunDriver.cxx: Missing state transition rule"); } static SH::MetaObject defaultOpts() @@ -214,26 +217,24 @@ static Status::Enum checkPandaTask(SH::Sample* const sample) static bool loaded = false; if (not loaded) { - // TString path = "$ROOTCOREBIN/python/EventLoopGrid/ELG_jediState.py"; - // gSystem->ExpandPathName(path); - // TPython::LoadMacro(path.Data()); std::string path = PathResolverFindCalibFile("EventLoopGrid/ELG_jediState.py"); TPython::LoadMacro(path.c_str()); loaded = true; } - TPython::Bind(dynamic_cast<TObject*>(sample), "ELG_SAMPLE"); - std::string ret = (const char*) TPython::Eval("ELG_jediState(ELG_SAMPLE)"); - TPython::Bind(0, "ELG_SAMPLE"); + TPython::Bind(dynamic_cast<TObject*>(sample), "ELG_SAMPLE"); + int ret = TPython::Eval("ELG_jediState(ELG_SAMPLE)"); + TPython::Bind(0, "ELG_SAMPLE"); - if (ret == "done") return Status::DONE; - if (ret == "failed") return Status::FAIL; - if (ret == "finished") return Status::FAIL; + if (ret == Status::DONE) return Status::DONE; + if (ret == Status::FAIL) return Status::FAIL; - if (ret != "running") { sample->meta()->setString("nc_ELG_state_details", ret); } - if (ret.empty()) { - sample->meta()->setString("nc_ELG_state_details", - "problem checking jedi task status"); + // Value 90 corresponds to `running` state of the job + if (ret != 90) { sample->meta()->setString("nc_ELG_state_details", "task status other than done/finished/failed/running"); } + // Value 99 is returned if there is error in the script (import, missing ID) + if (ret == 99) { + sample->meta()->setString("nc_ELG_state_details", + "problem checking jedi task status"); } return Status::PENDING; @@ -380,7 +381,7 @@ static void processAllInState(const SH::SampleHandler& sh, JobState::Enum state, } static std::string formatOutputName(const SH::MetaObject& sampleMeta, - const std::string pattern) + const std::string & pattern) { const std::string sampleName = sampleMeta.castString("sample_name"); RCU_REQUIRE(not pattern.empty()); @@ -445,9 +446,15 @@ static void saveJobDef(const std::string& fileName, outputs.Add(o->Clone()); file.WriteTObject(&job.jobConfig(), "jobConfig", "SingleKey"); file.WriteTObject(&outputs, "outputs", "SingleKey"); + bool haveDefault = false; for (SH::SampleHandler::iterator s = sh.begin(); s != sh.end(); ++s) { const SH::MetaObject& meta = *((*s)->meta()); file.WriteObject(&meta, meta.castString("sample_name").c_str()); + if (!haveDefault) + { + file.WriteObject (&meta, "defaultMetaObject"); + haveDefault = true; + } } } diff --git a/PhysicsAnalysis/D3PDTools/EventLoopGrid/data/ELG_jediState.py b/PhysicsAnalysis/D3PDTools/EventLoopGrid/data/ELG_jediState.py index 11e63092842f9e440dcf7c6dab082e92d107937c..5a0bdaaf7fc09645473f1a975142cf7767075a44 100644 --- a/PhysicsAnalysis/D3PDTools/EventLoopGrid/data/ELG_jediState.py +++ b/PhysicsAnalysis/D3PDTools/EventLoopGrid/data/ELG_jediState.py @@ -3,27 +3,54 @@ from __future__ import print_function def ELG_jediState(sample) : + """ Returns state of the jobs. + + State returned as an int based on an enum in the PrunDriver.cxx : + `enum Enum { DONE=0, PENDING=1, FAIL=2 };` + Make sure these values are kept the same in both files. + Extra values are added to capture extra states, those start at 90. + + """ + from enum import IntEnum + class Status(IntEnum): + DONE = 0 + PENDING = 1 + FAIL = 2 + RUNNING = 90 + OTHER = 91 + SCRIPT_FAIL = 99 + try: from pandatools import PandaToolsPkgInfo # noqa: F401 except ImportError: print ("prun needs additional setup, try:") print (" lsetup panda") - return 99 + return Status.SCRIPT_FAIL jediTaskID = int(sample.meta().castDouble("nc_jediTaskID", 0)) if jediTaskID < 100 : print ("Sample " + sample.name() + " does not have a jediTaskID") - return '' + return Status.SCRIPT_FAIL from pandatools import Client taskDict = {} taskDict['jediTaskID'] = jediTaskID - ret = Client.getJediTaskDetails(taskDict, False, True) - if ret[0] != 0 : + detail = Client.getJediTaskDetails(taskDict, False, True) + if detail[0] != 0 : print ("Problem checking status of task %s with id %s" % (sample.name(), jediTaskID)) - return '' + return Status.SCRIPT_FAIL + + status = detail[1]['status'] + + if status == "done": return Status.DONE + elif status == "failed": return Status.FAIL + # Finished returning FAIL status is original + # behavior from the PrunDriver.cxx + elif status == "finished": return Status.FAIL + elif status == "running": return Status.RUNNING - return ret[1]['status'].encode("ascii") + # Value for states not considered by PrunDriver.cxx + return Status.OTHER diff --git a/PhysicsAnalysis/D3PDTools/EventLoopTest/CMakeLists.txt b/PhysicsAnalysis/D3PDTools/EventLoopTest/CMakeLists.txt index 6897f00c21d5f4cf050ba52a5a155bcb7f9422e5..a2280a5e76c626f4cd50e9e831a5127ef536b968 100644 --- a/PhysicsAnalysis/D3PDTools/EventLoopTest/CMakeLists.txt +++ b/PhysicsAnalysis/D3PDTools/EventLoopTest/CMakeLists.txt @@ -34,7 +34,8 @@ atlas_add_root_dictionary( EventLoopTestLib EXTERNAL_PACKAGES ROOT ) atlas_add_library( EventLoopTestLib - EventLoopTest/*.h EventLoopTest/*.ihh Root/*.cxx ${EventLoopTestDictSource} + Root/UnitTest.cxx Root/UnitTestAlg.cxx Root/UnitTestAlg1.cxx Root/UnitTestAlg2.cxx Root/UnitTestAlg3.cxx Root/UnitTestAlg4.cxx Root/UnitTestAlg5.cxx Root/UnitTestAlg6.cxx Root/UnitTestAlg7.cxx Root/UnitTestAlgXAOD.cxx Root/UnitTestConfig.cxx Root/UnitTestTool.cxx + ${EventLoopTestDictSource} PUBLIC_HEADERS EventLoopTest INCLUDE_DIRS ${ROOT_INCLUDE_DIRS} ${GMOCK_INCLUDE_DIRS} LINK_LIBRARIES ${ROOT_LIBRARIES} ${GMOCK_LIBRARIES} RootCoreUtils @@ -91,5 +92,4 @@ endforeach (source ${util_sources}) # Install files from the package: -atlas_install_scripts( scripts/el_retrieve scripts/el_wait ) -atlas_install_data( data/*.root data/*.yml ) +atlas_install_data( data/*.yml data/*.py ) diff --git a/PhysicsAnalysis/D3PDTools/EventLoopTest/EventLoopTest/EventLoopTestDict.h b/PhysicsAnalysis/D3PDTools/EventLoopTest/EventLoopTest/EventLoopTestDict.h index 9472bf63c650c69d4025c8e2115bd789f0f5bed6..49df960545d84db27623df564568ca7c01854073 100644 --- a/PhysicsAnalysis/D3PDTools/EventLoopTest/EventLoopTest/EventLoopTestDict.h +++ b/PhysicsAnalysis/D3PDTools/EventLoopTest/EventLoopTest/EventLoopTestDict.h @@ -7,5 +7,6 @@ #include "EventLoopTest/UnitTestAlg5.h" #include "EventLoopTest/UnitTestAlg6.h" +#include "EventLoopTest/UnitTestAlg7.h" #endif diff --git a/PhysicsAnalysis/D3PDTools/EventLoopTest/EventLoopTest/UnitTestAlg7.h b/PhysicsAnalysis/D3PDTools/EventLoopTest/EventLoopTest/UnitTestAlg7.h new file mode 100644 index 0000000000000000000000000000000000000000..46dbe36cb5cfaa174a89275adf76ede4437e9275 --- /dev/null +++ b/PhysicsAnalysis/D3PDTools/EventLoopTest/EventLoopTest/UnitTestAlg7.h @@ -0,0 +1,50 @@ +/* + Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration +*/ + +#ifndef EVENT_LOOP_UNIT_TEST_ALG7_H +#define EVENT_LOOP_UNIT_TEST_ALG7_H + +#include <EventLoopTest/Global.h> + +#include <AnaAlgorithm/AnaAlgorithm.h> + +namespace EL +{ + /// \brief a \ref AnaAlgorithm for testing the configuration on the + /// worker node + + class UnitTestAlg7 final : public AnaAlgorithm + { + // + // public interface + // + + public: + UnitTestAlg7 (const std::string& name, + ISvcLocator* pSvcLocator); + + + + // + // interface inherited from Algorithm + // + + private: + virtual ::StatusCode initialize () override; + + private: + virtual ::StatusCode execute () override; + + private: + virtual ::StatusCode finalize () override; + + + + // + // private interface + // + }; +} + +#endif diff --git a/PhysicsAnalysis/D3PDTools/EventLoopTest/EventLoopTest/selection.xml b/PhysicsAnalysis/D3PDTools/EventLoopTest/EventLoopTest/selection.xml index 8cd781929c2ccee9e7baddff063dc79da7cbc5ae..46e0d98a39d80445f70bb97f7919cad8daf02a27 100644 --- a/PhysicsAnalysis/D3PDTools/EventLoopTest/EventLoopTest/selection.xml +++ b/PhysicsAnalysis/D3PDTools/EventLoopTest/EventLoopTest/selection.xml @@ -3,5 +3,6 @@ <!-- Unit test types: --> <class name="EL::UnitTestAlg5" /> <class name="EL::UnitTestAlg6" /> + <class name="EL::UnitTestAlg7" /> </lcgdict> diff --git a/PhysicsAnalysis/D3PDTools/EventLoopTest/Root/UnitTestAlg2.cxx b/PhysicsAnalysis/D3PDTools/EventLoopTest/Root/UnitTestAlg2.cxx index dd94437076fb325bedea0fb2928231c3bdfd0f79..609d8a358e1254daad52716257a0becd0e14d65c 100644 --- a/PhysicsAnalysis/D3PDTools/EventLoopTest/Root/UnitTestAlg2.cxx +++ b/PhysicsAnalysis/D3PDTools/EventLoopTest/Root/UnitTestAlg2.cxx @@ -22,6 +22,7 @@ #include <EventLoop/Worker.h> #include <RootCoreUtils/Assert.h> #include <RootCoreUtils/ThrowMsg.h> +#include <TEfficiency.h> #include <TFile.h> #include <TH1.h> #include <TTree.h> @@ -73,6 +74,12 @@ namespace EL RCU_ASSERT_SOFT (!m_hasInitialize); + // test that we can create and then retrieve a TEfficiency + // histogram + ANA_CHECK (book (TEfficiency ("efficiency", "dummy efficiency hist", 50, 0, 50))); + (void) hist<TEfficiency> ("efficiency"); + (void) histeff ("efficiency"); + ANA_CHECK (book (TH1F ((m_name + "2_2").c_str(), m_name.c_str(), 50, 0, 50))); ANA_CHECK (book (TH1F ("file_executes_2", "file executes", 1, 0, 1))); diff --git a/PhysicsAnalysis/D3PDTools/EventLoopTest/Root/UnitTestAlg7.cxx b/PhysicsAnalysis/D3PDTools/EventLoopTest/Root/UnitTestAlg7.cxx new file mode 100644 index 0000000000000000000000000000000000000000..2146faa47b8670b010f7dd99d403a0ac37d7ecf2 --- /dev/null +++ b/PhysicsAnalysis/D3PDTools/EventLoopTest/Root/UnitTestAlg7.cxx @@ -0,0 +1,51 @@ +/* + Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration +*/ + + +// +// includes +// + +#include <EventLoopTest/UnitTestAlg7.h> + +#include <TH1.h> + +// +// method implementations +// + +namespace EL +{ + UnitTestAlg7 :: + UnitTestAlg7 (const std::string& name, + ISvcLocator* pSvcLocator) + : AnaAlgorithm (name, pSvcLocator) + { + } + + + + ::StatusCode UnitTestAlg7 :: + initialize () + { + ANA_CHECK (book (TH1F ("dummy_hist", "dummy_hist", 50, 0, 50))); + return ::StatusCode::SUCCESS; + } + + + + ::StatusCode UnitTestAlg7 :: + execute () + { + return ::StatusCode::SUCCESS; + } + + + + ::StatusCode UnitTestAlg7 :: + finalize () + { + return ::StatusCode::SUCCESS; + } +} diff --git a/PhysicsAnalysis/D3PDTools/EventLoopTest/data/worker_config.py b/PhysicsAnalysis/D3PDTools/EventLoopTest/data/worker_config.py new file mode 100644 index 0000000000000000000000000000000000000000..f35e8dc2fbb2399e1b82dca579b8e9015eac98dd --- /dev/null +++ b/PhysicsAnalysis/D3PDTools/EventLoopTest/data/worker_config.py @@ -0,0 +1,5 @@ +from AnaAlgorithm.DualUseConfig import createAlgorithm + +def fillWorkerConfig (config) : + alg = createAlgorithm ('EL::UnitTestAlg7', 'myalg') + config.add (alg) diff --git a/PhysicsAnalysis/D3PDTools/EventLoopTest/test/gt_LeakChecks.cxx b/PhysicsAnalysis/D3PDTools/EventLoopTest/test/gt_LeakChecks.cxx index b2e38d43fe1e6d83920bbc7e4efd181240f9cf0e..52e55bb8781284f0aa1d508996146d6e5510da10 100644 --- a/PhysicsAnalysis/D3PDTools/EventLoopTest/test/gt_LeakChecks.cxx +++ b/PhysicsAnalysis/D3PDTools/EventLoopTest/test/gt_LeakChecks.cxx @@ -56,9 +56,9 @@ protected: m_job.algsAdd( algconf ); // Set up the job. - m_job.options()->setBool( EL::Job::optRemoveSubmitDir, true ); m_job.options()->setBool( EL::Job::optMemFailOnLeak, GetParam().enableChecks ); + m_job.options()->setString (EL::Job::optSubmitDirMode, "unique"); } /// EventLoop job to be run @@ -100,8 +100,8 @@ TEST_P( LeakCheckTests, batch ) { EL::LocalDriver driver; try { - driver.submit( m_job, outputDir.str() ); - driver.retrieve( outputDir.str() ); + std::string submitDir = driver.submit( m_job, outputDir.str() ); + driver.retrieve( submitDir ); if (shouldSucceed == false) FAIL() << "job succeeded when it should have failed"; } catch (...) diff --git a/PhysicsAnalysis/D3PDTools/EventLoopTest/test/gt_worker_config.cxx b/PhysicsAnalysis/D3PDTools/EventLoopTest/test/gt_worker_config.cxx new file mode 100644 index 0000000000000000000000000000000000000000..fcd492825c1ffd11e167d9b1392d94a38669edc0 --- /dev/null +++ b/PhysicsAnalysis/D3PDTools/EventLoopTest/test/gt_worker_config.cxx @@ -0,0 +1,38 @@ +// +// Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration +// + +#include <AsgTesting/UnitTest.h> +#include <EventLoop/DirectDriver.h> +#include <EventLoop/Job.h> +#include <EventLoop/OutputStream.h> +#include <SampleHandler/SampleHandler.h> +#include <SampleHandler/SampleLocal.h> +#include <TFile.h> + +using namespace EL; + +TEST (WorkerConfigTest, DISABLED_baseTest) +{ + Job job; + job.useXAOD(); + job.outputAdd (OutputStream ("out")); + job.options()->setString (Job::optWorkerConfigFile, "EventLoopTest/worker_config.py"); + job.options()->setString (Job::optSubmitDirMode, "unique"); + + SH::SampleHandler sh; + sh.setMetaString ("nc_tree", "CollectionTree"); + auto sample = std::make_unique<SH::SampleLocal> ("mc"); + sample->add( "file://${ASG_TEST_FILE_MC}" ); + sh.add (std::move (sample)); + job.sampleHandler (sh); + + DirectDriver driver; + std::string submitDir = driver.submit (job, "submitDir"); + std::unique_ptr<TFile> file (TFile::Open ((submitDir + "/hist-mc.root").c_str(), "READ")); + ASSERT_NE (file.get(), nullptr); + ASSERT_NE (file->Get ("dummy_hist"), nullptr); +} + +// Declare the main() function. +ATLAS_GOOGLE_TEST_MAIN diff --git a/PhysicsAnalysis/D3PDTools/MultiDraw/Root/AlgCFlow.cxx b/PhysicsAnalysis/D3PDTools/MultiDraw/Root/AlgCFlow.cxx index 6980923ea6995b4386d634fbbb41a1a6ba0ed916..8ed331c7944451dd2d2d87e6c56a52a3da5c35a6 100644 --- a/PhysicsAnalysis/D3PDTools/MultiDraw/Root/AlgCFlow.cxx +++ b/PhysicsAnalysis/D3PDTools/MultiDraw/Root/AlgCFlow.cxx @@ -1,5 +1,5 @@ /* - Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration + Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration */ // Copyright Nils Krumnack 2011. @@ -107,12 +107,11 @@ namespace MD } } - m_hist = hist.get(); + m_hist = hist.release(); m_hist->SetDirectory (0); m_values.resize (m_formulas.size()); - hist.release (); RCU_NEW_INVARIANT (this); } diff --git a/PhysicsAnalysis/D3PDTools/MultiDraw/Root/AlgHist.cxx b/PhysicsAnalysis/D3PDTools/MultiDraw/Root/AlgHist.cxx index 1b953e6eae1bd044d85a78d8364589ae41c70a98..e96f38982dea08704b02dae41b88b2ec89aa497e 100644 --- a/PhysicsAnalysis/D3PDTools/MultiDraw/Root/AlgHist.cxx +++ b/PhysicsAnalysis/D3PDTools/MultiDraw/Root/AlgHist.cxx @@ -1,5 +1,5 @@ /* - Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration + Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration */ // Copyright Nils Krumnack 2011. @@ -112,7 +112,7 @@ namespace MD if (!val_value3.empty()) m_formulas.push_back (val_value3); - m_hist = hist.get(); + m_hist = hist.release(); m_hist->SetDirectory (0); if (dynamic_cast<TProfile*>(m_hist) != 0) { @@ -139,7 +139,6 @@ namespace MD else RCU_THROW_MSG ("invalid number of formulas"); - hist.release (); RCU_NEW_INVARIANT (this); } diff --git a/PhysicsAnalysis/D3PDTools/MultiDraw/Root/Formula.cxx b/PhysicsAnalysis/D3PDTools/MultiDraw/Root/Formula.cxx index 093ed41f66d86f9e0223be519e4745e9224d9b1b..f52d63211fe8aea2cd8e5a357932032af656f166 100644 --- a/PhysicsAnalysis/D3PDTools/MultiDraw/Root/Formula.cxx +++ b/PhysicsAnalysis/D3PDTools/MultiDraw/Root/Formula.cxx @@ -1,5 +1,5 @@ /* - Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration + Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration */ // Copyright Nils Krumnack 2011 - 2012. @@ -114,8 +114,7 @@ namespace MD m_tree = 0; m_tree = tree; - std::unique_ptr<TTreeFormula> myform - (m_form = new TTreeFormula (m_name.c_str(), m_formula.c_str(), tree)); + m_form = new TTreeFormula (m_name.c_str(), m_formula.c_str(), tree); m_form->SetQuickLoad (kTRUE); m_manager = new TTreeFormulaManager; @@ -132,7 +131,7 @@ namespace MD else if (m_manager->GetMultiplicity() == -1 && m_form->GetMultiplicity() == 1) m_ndim = 0; - myform.release(); + } diff --git a/PhysicsAnalysis/D3PDTools/RootCoreUtils/CMakeLists.txt b/PhysicsAnalysis/D3PDTools/RootCoreUtils/CMakeLists.txt index c6d303315f1986bdeaf93006ae4e2693e57fffa5..303147b7aed331215f991b6684050c257b8a564f 100644 --- a/PhysicsAnalysis/D3PDTools/RootCoreUtils/CMakeLists.txt +++ b/PhysicsAnalysis/D3PDTools/RootCoreUtils/CMakeLists.txt @@ -1,7 +1,4 @@ -# $Id: CMakeLists.txt 739245 2016-04-11 08:05:59Z krasznaa $ -################################################################################ -# Package: RootCoreUtils -################################################################################ +# Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration # Declare the package name: atlas_subdir( RootCoreUtils ) diff --git a/PhysicsAnalysis/D3PDTools/RootCoreUtils/Root/Assert.cxx b/PhysicsAnalysis/D3PDTools/RootCoreUtils/Root/Assert.cxx index 5e4f62c0ec1b344a41a61061cc5349d8440a2cc6..76d055acd47213726ba2114f4ed54d0eecd94801 100644 --- a/PhysicsAnalysis/D3PDTools/RootCoreUtils/Root/Assert.cxx +++ b/PhysicsAnalysis/D3PDTools/RootCoreUtils/Root/Assert.cxx @@ -26,7 +26,7 @@ namespace RCU { namespace Check { - const char *typeLiteral [typeNum] = + const char * const typeLiteral [typeNum] = { "assertion failed", "assertion failed", @@ -45,7 +45,7 @@ namespace RCU - bool typeAbort [typeNum] = + const bool typeAbort [typeNum] = { false, true, diff --git a/PhysicsAnalysis/D3PDTools/RootCoreUtils/Root/CheckRootVersion.cxx b/PhysicsAnalysis/D3PDTools/RootCoreUtils/Root/CheckRootVersion.cxx index af0992ca9f0186dacd224136067fe875c2bc06a1..f38d638443676b42a03acd6c12be8164cf77ded3 100644 --- a/PhysicsAnalysis/D3PDTools/RootCoreUtils/Root/CheckRootVersion.cxx +++ b/PhysicsAnalysis/D3PDTools/RootCoreUtils/Root/CheckRootVersion.cxx @@ -1,5 +1,5 @@ /* - Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration + Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration */ /// @author Nils Krumnack @@ -14,6 +14,7 @@ #include <cstdlib> #include <iostream> +#include <atomic> #include <TROOT.h> #include <RootCoreUtils/Assert.h> @@ -25,7 +26,7 @@ namespace RCU { namespace { - bool checked = false; + std::atomic<bool> checked {false}; } void check_root_version () diff --git a/PhysicsAnalysis/D3PDTools/RootCoreUtils/Root/Message.cxx b/PhysicsAnalysis/D3PDTools/RootCoreUtils/Root/Message.cxx index 6b84627837249d0a7d24424eb578a61004aa9f20..5805c697d8d814dbc8e21d06b4006cf29e01a661 100644 --- a/PhysicsAnalysis/D3PDTools/RootCoreUtils/Root/Message.cxx +++ b/PhysicsAnalysis/D3PDTools/RootCoreUtils/Root/Message.cxx @@ -61,7 +61,7 @@ namespace RCU if (mytype != MESSAGE_UNSPECIFIED) { - static const char *type_names[MESSAGE_UNSPECIFIED] = + static const char * const type_names[MESSAGE_UNSPECIFIED] = {"message", "warning", "error", "exception", "abort"}; str << type_names[mytype] << ":"; } diff --git a/PhysicsAnalysis/D3PDTools/RootCoreUtils/Root/StringUtil.cxx b/PhysicsAnalysis/D3PDTools/RootCoreUtils/Root/StringUtil.cxx index 07c1ea8d0927aa54715a24335dd2c45806261098..2e4a255fc234d019f780d17f947719042d547b54 100644 --- a/PhysicsAnalysis/D3PDTools/RootCoreUtils/Root/StringUtil.cxx +++ b/PhysicsAnalysis/D3PDTools/RootCoreUtils/Root/StringUtil.cxx @@ -28,8 +28,10 @@ namespace RCU std::string result = str; std::string::size_type pos; - while ((pos = result.find (pattern)) != std::string::npos) + while ((pos = result.find (pattern)) != std::string::npos) { + // cppcheck-suppress uselessCallsSubstr result = result.substr (0, pos) + with + result.substr (pos + pattern.size()); + } return result; } diff --git a/PhysicsAnalysis/D3PDTools/RootCoreUtils/RootCoreUtils/ATLAS_CHECK_THREAD_SAFETY b/PhysicsAnalysis/D3PDTools/RootCoreUtils/RootCoreUtils/ATLAS_CHECK_THREAD_SAFETY new file mode 100644 index 0000000000000000000000000000000000000000..ebf7dd1d3901449205e3d2bd884078e9dcaba6f3 --- /dev/null +++ b/PhysicsAnalysis/D3PDTools/RootCoreUtils/RootCoreUtils/ATLAS_CHECK_THREAD_SAFETY @@ -0,0 +1 @@ +PhysicsAnalysis/D3PDTools/RootCoreUtils diff --git a/PhysicsAnalysis/D3PDTools/RootCoreUtils/RootCoreUtils/Assert.h b/PhysicsAnalysis/D3PDTools/RootCoreUtils/RootCoreUtils/Assert.h index 39595ec546dedeac41ab96bcc3b0c0ca4cf14f98..069103c71ab66a2f7fa6942fb996e1e289a2f3cf 100644 --- a/PhysicsAnalysis/D3PDTools/RootCoreUtils/RootCoreUtils/Assert.h +++ b/PhysicsAnalysis/D3PDTools/RootCoreUtils/RootCoreUtils/Assert.h @@ -73,8 +73,8 @@ namespace RCU invariant }; const int typeNum = invariant + 1; - extern const char *typeLiteral [typeNum]; - extern bool typeAbort [typeNum]; + extern const char * const typeLiteral [typeNum]; + extern const bool typeAbort [typeNum]; /// effects: report the error and abort either by exception or diff --git a/PhysicsAnalysis/D3PDTools/RootCoreUtils/share/ut_expression.ref b/PhysicsAnalysis/D3PDTools/RootCoreUtils/share/ut_expression.ref deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/PhysicsAnalysis/D3PDTools/RootCoreUtils/share/ut_ran_packages_false.ref b/PhysicsAnalysis/D3PDTools/RootCoreUtils/share/ut_ran_packages_false.ref deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/PhysicsAnalysis/D3PDTools/SampleHandler/Root/GridTools.cxx b/PhysicsAnalysis/D3PDTools/SampleHandler/Root/GridTools.cxx index ee5b4e47de471ef3b5f99e42ab2807c57c386318..a7868edaf376eae57237b719e7cd379592b2be9e 100644 --- a/PhysicsAnalysis/D3PDTools/SampleHandler/Root/GridTools.cxx +++ b/PhysicsAnalysis/D3PDTools/SampleHandler/Root/GridTools.cxx @@ -143,7 +143,7 @@ namespace SH while (isspace (subresult.front())) subresult = subresult.substr (1); while (isspace (subresult.back())) - subresult = subresult.substr (0, subresult.size()-1); + subresult.pop_back(); result.push_back (std::move (subresult)); } } diff --git a/PhysicsAnalysis/D3PDTools/SampleHandler/Root/Sample.cxx b/PhysicsAnalysis/D3PDTools/SampleHandler/Root/Sample.cxx index fbf7b6c42fa895d7246040e70105240fc0295455..69ce9582be1196bd469ad2d705ad61ae23b8247d 100644 --- a/PhysicsAnalysis/D3PDTools/SampleHandler/Root/Sample.cxx +++ b/PhysicsAnalysis/D3PDTools/SampleHandler/Root/Sample.cxx @@ -1,5 +1,5 @@ /* - Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration + Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration */ // Copyright Nils Krumnack 2011. @@ -456,6 +456,7 @@ namespace SH std::unique_ptr<TFile> file (TFile::Open (fileList[0].c_str(), "READ")); if (file == nullptr) RCU_THROW_MSG ("could not open file " + fileList[0]); + //cppcheck-suppress nullPointerRedundantCheck TObject *object = file->Get (name.c_str()); if (object != nullptr) RCU::SetDirectory (object, nullptr); diff --git a/PhysicsAnalysis/D3PDTools/SampleHandler/Root/ScanDir.cxx b/PhysicsAnalysis/D3PDTools/SampleHandler/Root/ScanDir.cxx index cfa7d64185ac42e975b9436396a968b20216813c..5914d32fb9a17cde4a7a5165ca99491bb7713b54 100644 --- a/PhysicsAnalysis/D3PDTools/SampleHandler/Root/ScanDir.cxx +++ b/PhysicsAnalysis/D3PDTools/SampleHandler/Root/ScanDir.cxx @@ -279,7 +279,7 @@ namespace SH { if (iter == 0) RCU_THROW_MSG ("sample name matches entire postfix pattern: \"" + sampleName + "\""); - sampleName = sampleName.substr (0, iter); + sampleName.resize (iter); done = true; } } @@ -349,7 +349,7 @@ namespace SH myindex < 0) { while (!sampleName.empty() && sampleName[sampleName.size()-1] == '/') - sampleName = sampleName.substr (0, sampleName.size() - 1); + sampleName.pop_back(); if (sampleName.empty()) return sampleName; if (myindex < 0) @@ -360,7 +360,7 @@ namespace SH sampleName.clear (); return sampleName; } - sampleName = sampleName.substr (0, split); + sampleName.resize (split); ++ myindex; } if (sampleName.empty()) diff --git a/PhysicsAnalysis/D3PDTools/SampleHandler/Root/ToolsDiscovery.cxx b/PhysicsAnalysis/D3PDTools/SampleHandler/Root/ToolsDiscovery.cxx index 448eba6553a39434f98ae27d0d597da60a0b5c0d..96e6a761fefadd16fbcb7410a989cca658a7b1da 100644 --- a/PhysicsAnalysis/D3PDTools/SampleHandler/Root/ToolsDiscovery.cxx +++ b/PhysicsAnalysis/D3PDTools/SampleHandler/Root/ToolsDiscovery.cxx @@ -1,5 +1,5 @@ /* - Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration + Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration */ // Copyright Nils Krumnack 2011. @@ -282,7 +282,7 @@ namespace SH std::string url = entry.replica; const auto split = url.find (from); if (split != std::string::npos) - url = url.substr (0, split) + to + url.substr (split + from.size()); + url.replace(split, from.size(), to); usedFiles[entry.name] = url; } } diff --git a/PhysicsAnalysis/D3PDTools/SampleHandler/Root/ToolsOther.cxx b/PhysicsAnalysis/D3PDTools/SampleHandler/Root/ToolsOther.cxx index 6527045e9be1f7c1cb373936fc21965c3b67ffd3..9703cde31c062e167c3352d70cf3178a1029cf2e 100644 --- a/PhysicsAnalysis/D3PDTools/SampleHandler/Root/ToolsOther.cxx +++ b/PhysicsAnalysis/D3PDTools/SampleHandler/Root/ToolsOther.cxx @@ -60,6 +60,7 @@ namespace SH return result; } RCU_THROW_MSG ("failed to open file: " + name); + //cppcheck-suppress rethrowNoCurrentException throw; //compiler dummy } diff --git a/PhysicsAnalysis/D3PDTools/SampleHandler/SampleHandler/SampleGrid.h b/PhysicsAnalysis/D3PDTools/SampleHandler/SampleHandler/SampleGrid.h index 3199dd0a349a7a85c1ed1d7f700b7185a8af9090..a7825b1f826af3c029afebc9ad2858339b83d969 100644 --- a/PhysicsAnalysis/D3PDTools/SampleHandler/SampleHandler/SampleGrid.h +++ b/PhysicsAnalysis/D3PDTools/SampleHandler/SampleHandler/SampleGrid.h @@ -31,7 +31,7 @@ namespace SH /// the files to use within. /// /// The main purpose as such is to pass it as an input sample into - /// the EL::GridDriver. However, in practice one can still use this + /// the @ref EL::PrunDriver. However, in practice one can still use this /// like a regular sample (using direct access via FAX, or /// pre-loading via rucio). ///