diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithmsDict.h b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithmsDict.h
new file mode 100644
index 0000000000000000000000000000000000000000..4a4318995cb2168c360ec4e132266ac6f86326d3
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithmsDict.h
@@ -0,0 +1,33 @@
+/*
+  Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef ASG_ANALYSIS_ALGORITHMS__ASG_ANALYSIS_ALGORITHMS_DICT_H
+#define ASG_ANALYSIS_ALGORITHMS__ASG_ANALYSIS_ALGORITHMS_DICT_H
+
+#include <AsgAnalysisAlgorithms/AsgFlagSelectionTool.h>
+#include <AsgAnalysisAlgorithms/AsgPtEtaSelectionTool.h>
+#include <AsgAnalysisAlgorithms/AsgCutBookkeeperAlg.h>
+#include <AsgAnalysisAlgorithms/AsgEventScaleFactorAlg.h>
+#include <AsgAnalysisAlgorithms/AsgLeptonTrackSelectionAlg.h>
+#include <AsgAnalysisAlgorithms/AsgOriginalObjectLinkAlg.h>
+#include <AsgAnalysisAlgorithms/AsgSelectionAlg.h>
+#include <AsgAnalysisAlgorithms/AsgViewFromSelectionAlg.h>
+#include <AsgAnalysisAlgorithms/AsgxAODNTupleMakerAlg.h>
+#include <AsgAnalysisAlgorithms/EventFlagSelectionAlg.h>
+#include <AsgAnalysisAlgorithms/EventSelectionByObjectFlagAlg.h>
+#include <AsgAnalysisAlgorithms/IsolationCloseByCorrectionAlg.h>
+#include <AsgAnalysisAlgorithms/KinematicHistAlg.h>
+#include <AsgAnalysisAlgorithms/ObjectCutFlowHistAlg.h>
+#include <AsgAnalysisAlgorithms/OverlapRemovalAlg.h>
+#include <AsgAnalysisAlgorithms/PileupReweightingAlg.h>
+#include <AsgAnalysisAlgorithms/PMGTruthWeightAlg.h>
+#include <AsgAnalysisAlgorithms/SysListDumperAlg.h>
+#include <AsgAnalysisAlgorithms/SysListLoaderAlg.h>
+#include <AsgAnalysisAlgorithms/TreeFillerAlg.h>
+#include <AsgAnalysisAlgorithms/TreeMakerAlg.h>
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/AsgCutBookkeeperAlg.h b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/AsgCutBookkeeperAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..037aa61a2846f7ef7bcdbb03bc4596d3282fa9a4
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/AsgCutBookkeeperAlg.h
@@ -0,0 +1,73 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Tadej Novak
+
+
+#ifndef ASG_ANALYSIS_ALGORITHMS__ASG_CUT_BOOKKEEPER_ALG_H
+#define ASG_ANALYSIS_ALGORITHMS__ASG_CUT_BOOKKEEPER_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <PMGAnalysisInterfaces/IPMGTruthWeightTool.h>
+
+namespace CP
+{
+  /// \brief an algorithm for dumping the CutBookkeeper metadata
+  /// into a histogram
+
+  class AsgCutBookkeeperAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    AsgCutBookkeeperAlg (const std::string& name, 
+                         ISvcLocator* pSvcLocator);
+
+  public:
+    /// \brief initialize
+    StatusCode initialize () override;
+
+  public:
+    /// \brief run once on each file
+    StatusCode fileExecute () override;
+
+  public:
+    /// \brief finalize
+    StatusCode finalize () override;
+
+    /// \brief flag to enable systematics
+  private:
+    bool m_enableSystematics{false};
+
+    /// \brief the truth weight tool
+  private:
+    ToolHandle<PMGTools::IPMGTruthWeightTool> m_truthWeightTool;
+
+    /// \brief run number we are processing
+  private:
+    uint32_t m_runNumber {};
+
+    /// \brief MC channel number we are processing
+  private:
+    uint32_t m_mcChannelNumber {};
+
+    /// \brief weights helper struct
+  private:
+    struct WeightsGroup
+    {
+      float nEventsProcessed {};
+      float sumOfWeights {};
+      float sumOfWeightsSquared {};
+    };
+
+    /// \brief weights map
+  private:
+    std::unordered_map<size_t, WeightsGroup> m_weights;
+
+    /// \brief the pattern for histogram names
+  private:
+    std::string m_histPattern {"CutBookkeeper_%DSID%_%RUN%_%SYS%"};
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/AsgEventScaleFactorAlg.h b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/AsgEventScaleFactorAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..9d856927f482199795c8075226b73ca85a5aebdc
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/AsgEventScaleFactorAlg.h
@@ -0,0 +1,70 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Tadej Novak
+
+
+#ifndef ASG_ANALYSIS_ALGORITHMS__ASG_EVENT_SCALE_FACTOR_ALG_H
+#define ASG_ANALYSIS_ALGORITHMS__ASG_EVENT_SCALE_FACTOR_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <SelectionHelpers/ISelectionAccessor.h>
+#include <SelectionHelpers/SelectionReadHandle.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <SystematicsHandles/SysDecorationHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+#include <xAODBase/IParticleContainer.h>
+#include <xAODEventInfo/EventInfo.h>
+
+namespace CP
+{
+  /// \brief an algorithm for calculating per-event scale factors
+
+  class AsgEventScaleFactorAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    AsgEventScaleFactorAlg (const std::string& name, 
+                            ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the event info we run on (empty by default)
+  private:
+    SysCopyHandle<xAOD::EventInfo> m_eventInfoHandle {
+      this, "eventInfo", "EventInfo", "the event info object to run on"};
+
+    /// \brief the jet collection we run on
+  private:
+    SysCopyHandle<xAOD::IParticleContainer> m_particleHandle {
+      this, "particles", "", "the particle collection to run on"};
+
+    /// \brief the preselection we apply to our input
+  private:
+    SelectionReadHandle m_preselection {
+      this, "preselection", "", "the preselection to apply"};
+
+    /// \brief the decoration for reading the scale factor
+  private:
+    SysDecorationHandle<float> m_scaleFactorInputDecoration {
+      this, "scaleFactorInputDecoration", "", "the decoration for the input efficiency scale factor"};
+
+    /// \brief the decoration for writing the scale factor
+  private:
+    SysDecorationHandle<float> m_scaleFactorOutputDecoration {
+      this, "scaleFactorOutputDecoration", "", "the decoration for the output efficiency scale factor"};
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/AsgFlagSelectionTool.h b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/AsgFlagSelectionTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..77eb764d24a38526627dc00dd82a5ff8cc050ba6
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/AsgFlagSelectionTool.h
@@ -0,0 +1,89 @@
+/*
+ Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+ */
+
+/// @author Teng Jian Khoo
+
+
+
+#ifndef ASG_ANALYSIS_ALGORITHMS__ASG_FLAG_SELECTION_TOOL_H
+#define ASG_ANALYSIS_ALGORITHMS__ASG_FLAG_SELECTION_TOOL_H
+
+#include <AsgTools/AsgTool.h>
+#include <PATCore/IAsgSelectionTool.h>
+#include <SelectionHelpers/ISelectionAccessor.h>
+#include <xAODBase/IParticle.h>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace CP
+{
+    /// \brief an \ref IAsgSelectionTool that cuts on char decorations
+    ///
+    /// For ROOT I/O reasons, chars are commonly used to store boolean
+    /// properties, such as flags indicating whether particular criteria
+    /// have been passed. This tool provides a conversion of a list
+    /// of such decorations, interpreted as bools, into the SelectionType
+    /// encoding of TAccept, for use in AnaAlgSequence.
+    ///
+    /// A list of flags may be provided to the tool, and the output will
+    /// simply concatenate them into the TAccept. Each selection criterion
+    /// must be passed (no OR behaviour), but the flag logic may be
+    /// inverted, such that flags may be encoded as "passX" or !"failX".
+
+    class AsgFlagSelectionTool final
+    : public asg::AsgTool, virtual public IAsgSelectionTool
+  {
+    //
+    // public interface
+    //
+
+    // Create a proper constructor for Athena
+    ASG_TOOL_CLASS( AsgFlagSelectionTool, IAsgSelectionTool )
+
+
+    /// \brief standard constructor
+    /// \par Guarantee
+    ///   strong
+    /// \par Failures
+    ///   out of memory II
+  public:
+    AsgFlagSelectionTool (const std::string& name);
+
+
+
+
+    //
+    // inherited interface
+    //
+
+    virtual StatusCode initialize () override;
+
+    virtual const Root::TAccept& getTAccept( ) const override;
+
+    virtual const Root::TAccept& accept( const xAOD::IParticle* /*part*/ ) const override;
+
+
+
+    //
+    // private interface
+    //
+
+    /// tool properties
+    /// \{
+
+    std::vector<std::string> m_selFlags;
+    std::vector<bool> m_invertFlags;
+    std::vector<std::unique_ptr<ISelectionAccessor> > m_acc_selFlags;
+
+    /// \}
+
+
+    /// \brief the \ref TAccept we are using
+  private:
+    mutable Root::TAccept m_accept;
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/AsgLeptonTrackSelectionAlg.h b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/AsgLeptonTrackSelectionAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..db25eee5813c6371294fc41a4518c4ceedc4f22d
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/AsgLeptonTrackSelectionAlg.h
@@ -0,0 +1,91 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+#ifndef ASG_ANALYSIS_ALGORITHMS__ASG_LEPTON_TRACK_SELECTION_ALG_H
+#define ASG_ANALYSIS_ALGORITHMS__ASG_LEPTON_TRACK_SELECTION_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <PATCore/IAsgSelectionTool.h>
+#include <SelectionHelpers/ISelectionAccessor.h>
+#include <SelectionHelpers/SelectionReadHandle.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+
+namespace CP
+{
+  /// \brief an algorithm for performing track-vertex selection on
+  /// leptons
+  ///
+  /// Originally I meant to implement this as an \ref
+  /// IAsgSelectionTool, but since this needs other objects besides
+  /// the lepton itself, I made it into an algorithm.  Technically
+  /// this could also be addressed by retrieving those extra objects
+  /// in the seleciton tool, but this seemed like potential overkill,
+  /// given that the selection tools may be called very frequently and
+  /// are not really doing any form of heavy lifting at all.  Still,
+  /// at some point we may decide to change this into a selection tool
+  /// instead (06 Aug 18).
+
+  class AsgLeptonTrackSelectionAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    AsgLeptonTrackSelectionAlg (const std::string& name, 
+                                   ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+
+
+    /// algorithm properties
+    /// \{
+
+  private:
+    float m_maxD0Significance {0};
+    float m_maxDeltaZ0SinTheta {0};
+    int m_nMinPixelHits{-1};
+    int m_nMaxPixelHits{-1};
+    int m_nMinSCTHits{-1};
+    int m_nMaxSCTHits{-1};
+    std::string m_selectionDecoration {"trackSelection"};
+    std::string m_eventInfo {"EventInfo"};
+    std::string m_primaryVertices {"PrimaryVertices"};
+
+    /// \}
+
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the particle continer we run on
+  private:
+    SysCopyHandle<xAOD::IParticleContainer> m_particlesHandle {
+      this, "particles", "", "the asg collection to run on"};
+
+    /// \brief the preselection we apply to our input
+  private:
+    SelectionReadHandle m_preselection {
+      this, "preselection", "", "the preselection to apply"};
+
+    /// \brief the accessor for \ref m_selectionDecoration
+  private:
+    std::unique_ptr<ISelectionAccessor> m_selectionAccessor;
+
+
+    /// \brief the \ref TAccept we are using
+  private:
+    Root::TAccept m_accept;
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/AsgOriginalObjectLinkAlg.h b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/AsgOriginalObjectLinkAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..a69f19ca7f2ac8f91fae573ef566a19fdf8ace69
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/AsgOriginalObjectLinkAlg.h
@@ -0,0 +1,55 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Tadej Novak
+
+
+#ifndef ASG_ANALYSIS_ALGORITHMS__ASG_ORIGINAL_OBJECT_LINK_ALG_H
+#define ASG_ANALYSIS_ALGORITHMS__ASG_ORIGINAL_OBJECT_LINK_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+#include <xAODBase/IParticleContainer.h>
+
+
+namespace CP
+{
+  /// \brief an algorithm for relinking a shallow copy with it's base container
+  /// when this was not done originally
+  ///
+  /// In some cases we work with shallow copy containers that originally did
+  /// not have proper original object linking done when created.
+  /// Currently the client are b-tagging calibration shallow copies.
+
+  class AsgOriginalObjectLinkAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    AsgOriginalObjectLinkAlg (const std::string& name, 
+                              ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+
+    /// \brief base container name
+  private:
+    std::string m_baseContainerName {""};
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the particle container we run on
+  private:
+    SysCopyHandle<xAOD::IParticleContainer> m_particleHandle {
+      this, "particles", "", "the particle container to run on"};
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/AsgPtEtaSelectionTool.h b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/AsgPtEtaSelectionTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..f1cd65e237c9b906dc8dc06e0c3419fb5a926445
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/AsgPtEtaSelectionTool.h
@@ -0,0 +1,130 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+#ifndef ASG_ANALYSIS_ALGORITHMS__ASG_PT_ETA_SELECTION_TOOL_H
+#define ASG_ANALYSIS_ALGORITHMS__ASG_PT_ETA_SELECTION_TOOL_H
+
+#include <AsgTools/AsgTool.h>
+#include <PATCore/IAsgSelectionTool.h>
+#include <atomic>
+
+namespace CP
+{
+  /// \brief an \ref IAsgSelectionTool that performs basic pt and eta
+  /// cut (with an optional eta gap)
+  ///
+  /// This is a very basic selection that needs to happen on all
+  /// object types to some degree.  Instead of doing this separately
+  /// for each type, it is just one basic tool for all \ref IParticle
+  /// implementations.  Also, this is a tool, not an algorithm,
+  /// because we already have an algorithm wrapping generic selection
+  /// tools and there is no benefit to making it an algorithm.
+  ///
+  /// There may be some overlap with the individual selectors for the
+  /// given object types, but having a tool for this allows to apply
+  /// it at any point in the algorithm sequence.
+
+  class AsgPtEtaSelectionTool final
+    : public asg::AsgTool, virtual public IAsgSelectionTool
+  {
+    //
+    // public interface
+    //
+
+    // Create a proper constructor for Athena
+    ASG_TOOL_CLASS( AsgPtEtaSelectionTool, IAsgSelectionTool )
+
+
+    /// \brief standard constructor
+    /// \par Guarantee
+    ///   strong
+    /// \par Failures
+    ///   out of memory II
+  public:
+    AsgPtEtaSelectionTool (const std::string& name);
+
+
+
+
+    //
+    // inherited interface
+    //
+
+    virtual StatusCode initialize () override;
+
+    virtual const Root::TAccept& getTAccept( ) const override;
+
+    virtual const Root::TAccept& accept( const xAOD::IParticle* /*part*/ ) const override;
+
+
+
+    //
+    // private interface
+    //
+
+    /// tool properties
+    /// \{
+
+  private:
+    float m_minPt {0};
+    float m_maxPt {0};
+    float m_maxEta {0};
+    float m_etaGapLow {0};
+    float m_etaGapHigh {0};
+    bool m_useClusterEta {false};
+    bool m_printCastWarning {true};
+    bool m_printClusterWarning {true};
+
+    /// \}
+
+    /// Index for the minimum pT selection
+    int m_minPtCutIndex{ -1 };
+    /// Index for the maximum pT selection
+    int m_maxPtCutIndex{ -1 };
+    /// Index for the maximum eta selection
+    int m_maxEtaCutIndex{ -1 };
+    /// Index for the eta gap selection
+    int m_etaGapCutIndex{ -1 };
+    /// Index for the e/gamma casting
+    int m_egammaCastCutIndex{ -1 };
+    /// Index for the e/gamma calo-cluster
+    int m_egammaClusterCutIndex{ -1 };
+
+    /// \brief a version of \ref m_printCastWarning that we modify
+    /// once we printed the warning
+    ///
+    /// I don't like modifying property values in the tool itself, so
+    /// I copy it over here and then modify once I print out.
+    ///
+    /// Technically this tool isn't thread-safe due to the use of
+    /// TAccept, but once we move to master this will be fixed, so
+    /// this member is already made thread-safe so that we don't trip
+    /// up on that later.
+  private:
+    mutable std::atomic<bool> m_shouldPrintCastWarning {true};
+
+    /// \brief a version of \ref m_printClusterWarning that we modify
+    /// once we printed the warning
+    ///
+    /// I don't like modifying property values in the tool itself, so
+    /// I copy it over here and then modify once I print out.
+    ///
+    /// Technically this tool isn't thread-safe due to the use of
+    /// TAccept, but once we move to master this will be fixed, so
+    /// this member is already made thread-safe so that we don't trip
+    /// up on that later.
+  private:
+    mutable std::atomic<bool> m_shouldPrintClusterWarning {true};
+
+    /// \brief the \ref TAccept we are using
+  private:
+    mutable Root::TAccept m_accept;
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/AsgSelectionAlg.h b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/AsgSelectionAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..5af9efe89ae17fd821b103f74e9357444af8b3b2
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/AsgSelectionAlg.h
@@ -0,0 +1,98 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+#ifndef ASG_ANALYSIS_ALGORITHMS__ASG_SELECTION_ALG_H
+#define ASG_ANALYSIS_ALGORITHMS__ASG_SELECTION_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <PATCore/IAsgSelectionTool.h>
+#include <SelectionHelpers/ISelectionAccessor.h>
+#include <SelectionHelpers/SelectionReadHandle.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+
+namespace CP
+{
+  /// \brief an algorithm for calling \ref IAsgSelectionTool
+  ///
+  /// There are two ways to implement this: The way I chose is to call
+  /// the IAsgSelectionTool on the members of an IParticleContainer.
+  /// Among several issues this has some inefficiencies, because it
+  /// relies on dynamic_cast inside the IAsgSelectionTool.  Also,
+  /// currently \ref CopyHandle only supports specific xAOD types for
+  /// IParticleContainer (though in practice this only matters if
+  /// systematics are applied in this algorithm).
+  ///
+  /// The alternative would be to implement this as a template, with
+  /// one template argument for the tool interface type and a second
+  /// template argument for the container type to run on.  Besides
+  /// improving some of the efficiences, it would also allow using
+  /// selector tools that don't implement the \ref IAsgSelectionTool
+  /// interface (though arguably they should support the
+  /// IAsgSelectionTool interface).  The main downside of that
+  /// approach would be the need to support a templated algorithm,
+  /// which I'm not even sure we have dual-use support for at the
+  /// moment (19 Mar 18).
+
+  class AsgSelectionAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    AsgSelectionAlg (const std::string& name, 
+                                   ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+    
+
+
+    /// \brief the smearing tool
+  private:
+    ToolHandle<IAsgSelectionTool> m_selectionTool;
+
+    /// \brief the smearing tool cast to an ISystematicsTool
+    ///
+    /// Normally selection tools don't have systematics, but I believe
+    /// I have seen one or two with systematics, so I added that
+    /// possibility in.
+  private:
+    ISystematicsTool *m_systematicsTool {nullptr};
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the particle continer we run on
+  private:
+    SysCopyHandle<xAOD::IParticleContainer> m_particlesHandle {
+      this, "particles", "", "the asg collection to run on"};
+
+    /// \brief the preselection we apply to our input
+  private:
+    SelectionReadHandle m_preselection {
+      this, "preselection", "", "the preselection to apply"};
+
+    /// \brief the decoration for the asg selection
+  private:
+    std::string m_selectionDecoration;
+
+    /// \brief the accessor for \ref m_selectionDecoration
+  private:
+    std::unique_ptr<ISelectionAccessor> m_selectionAccessor;
+
+    /// \brief the bits to set for an object failing the preselection
+  private:
+    SelectionType m_setOnFail;
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/AsgViewFromSelectionAlg.h b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/AsgViewFromSelectionAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..117b6223351c8447381623b87b9e643fd72ebde2
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/AsgViewFromSelectionAlg.h
@@ -0,0 +1,102 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+#ifndef ASG_ANALYSIS_ALGORITHMS__ASG_VIEW_FROM_SELECTION_ALG_H
+#define ASG_ANALYSIS_ALGORITHMS__ASG_VIEW_FROM_SELECTION_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <xAODBase/IParticleContainer.h>
+#include <SelectionHelpers/ISelectionAccessor.h>
+#include <SystematicsHandles/SysReadHandle.h>
+#include <SystematicsHandles/SysWriteHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+#include <limits>
+
+namespace CP
+{
+  /// \brief create a view container based on selection decorations
+  ///
+  /// This is a generic algorithm that works for all object types to
+  /// read our standard selection decorations and create a view
+  /// container.  This avoids the need for the algorithm/tool to read
+  /// the selection decoration on the input, instead it can just
+  /// assume that all its input objects are meant to be processed.
+
+  class AsgViewFromSelectionAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    AsgViewFromSelectionAlg (const std::string& name, 
+                             ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the input collection we run on
+  private:
+    SysReadHandle<xAOD::IParticleContainer> m_inputHandle {
+      this, "input", "", "the input collection to run on"};
+
+    /// \brief the output view container we produce
+  private:
+    SysWriteHandle<xAOD::IParticleContainer> m_outputHandle {
+      this, "output", "", "the output view container to produce"};
+
+  private:
+    std::vector<std::string> m_selection;
+
+  private:
+    std::vector<SelectionType> m_ignore;
+
+    /// \brief Sort the output (view) container by pT
+  private:
+    bool m_sortPt {false};
+
+    /// \brief Allow the input container to be missing
+  private:
+    bool m_allowMissing {false};
+
+    /// \brief Perform a deep copy for creating the output container
+  private:
+    bool m_deepCopy {false};
+
+  private:
+    std::size_t m_sizeLimit {std::numeric_limits<std::size_t>::max()};
+
+    /// the list of accessors and cut ignore list
+  private:
+    std::vector<std::pair<std::unique_ptr<ISelectionAccessor>,SelectionType> > m_accessors;
+
+    /// \brief the templated version of execute for a single systematic
+  private:
+    template<typename Type> StatusCode
+    executeTemplate (const CP::SystematicSet& sys);
+
+    /// \brief the version of execute to find the type
+  private:
+    StatusCode executeFindType (const CP::SystematicSet& sys);
+
+    /// \brief The version of execute for missing input containers
+  private:
+    StatusCode executeMissing (const CP::SystematicSet& sys);
+
+  private:
+    StatusCode (AsgViewFromSelectionAlg::* m_function) (const CP::SystematicSet& sys) {&AsgViewFromSelectionAlg::executeFindType};
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/AsgxAODNTupleMakerAlg.h b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/AsgxAODNTupleMakerAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..50df447893fa03b765a021bac14a4d08ee35fba9
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/AsgxAODNTupleMakerAlg.h
@@ -0,0 +1,368 @@
+// Dear emacs, this is -*- c++ -*-
+//
+// Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+//
+#ifndef ASGANALYSISALGORITHMS_ASGXAODNTUPLEMAKERALG_H
+#define ASGANALYSISALGORITHMS_ASGXAODNTUPLEMAKERALG_H
+
+// System include(s):
+#include <unordered_map>
+#include <string>
+#include <vector>
+#include <memory>
+#include <list>
+
+// Framework include(s):
+#include "AsgTools/AsgMessaging.h"
+#include "AnaAlgorithm/AnaAlgorithm.h"
+#include "SystematicsHandles/SysListHandle.h"
+
+// EDM include(s):
+#include "AthContainersInterfaces/IAuxTypeVector.h"
+#include "AthContainers/AuxElement.h"
+
+// Forward declaration(s):
+class TClass;
+class TTree;
+class TVirtualCollectionProxy;
+namespace SG {
+   class AuxVectorBase;
+   class IAuxTypeVectorFactory;
+}
+
+namespace CP {
+
+   /// Algorithm that can write a simple ntuple from xAOD objects/variables
+   ///
+   /// This is meant as a simple tool for creating small ntuples in analyses,
+   /// using a simple job configuration. It can create branches from any xAOD
+   /// variables that are possible to write as an "xAOD variable" to begin with.
+   ///
+   /// It is *not* meant as a general purpose DAOD -> NTuple dumper however.
+   /// It should only be used to create small ntuples, with highly processed
+   /// variables.
+   ///
+   /// It's "main" property ("Branches") can be filled with entries of the form:
+   ///
+   /// <code>
+   ///    writer = ...<br/>
+   ///    writer.Branches = [ "<SG key>.<aux variable> -> <branch name>", ... ]
+   /// </code>
+   ///
+   /// , where each entry sets up one branch for the output tree.
+   ///
+   /// @author Attila Krasznahorkay <Attila.Krasznahorkay@cern.ch>
+   ///
+   class AsgxAODNTupleMakerAlg : public EL::AnaAlgorithm {
+
+   public:
+      /// Algorithm constructor
+      AsgxAODNTupleMakerAlg( const std::string& name, ISvcLocator* svcLoc );
+
+      /// @name Functions inherited from @c EL::AnaAlgorithm
+      /// @{
+
+      /// Function executed as part of the job initialisation
+      StatusCode initialize() override;
+
+      /// Function executed once per event
+      StatusCode execute() override;
+
+      /// Function executed as part of the job finalisation
+      StatusCode finalize() override;
+
+      /// @}
+
+   private:
+      /// Function setting up the internal data structures on the first event
+      StatusCode setupTree();
+
+      /// Function setting up an individual branch on the first event
+      StatusCode setupBranch( const std::string &branchDecl,
+                              const CP::SystematicSet &sys );
+
+      /// @name Algorithm properties
+      /// @{
+
+      /// The name of the output tree to write
+      std::string m_treeName;
+      /// The branches to write into this output tree
+      std::vector< std::string > m_branches;
+
+      /// @}
+
+      /// Class writing all variables from one standalone object
+      ///
+      /// It is designed to work with any type inheriting from
+      /// @c SG::AuxElement. Like @c xAOD::EventInfo. Which is its main user
+      /// at the moment...
+      ///
+      class ElementProcessor : public asg::AsgMessaging {
+
+      public:
+         /// Default constructor
+         ///
+         /// We have to have a default constructor to initialise the
+         /// @c asg::AsgMessaging base class correctly. Members of this class
+         /// would not need an explicit constructor themselves.
+         ///
+         ElementProcessor();
+
+         /// Process the object
+         ///
+         /// This function is called during the event processing to extract
+         /// all configured variables from a standalone xAOD object into the
+         /// output variables set up using @c ElementProcessor::addBranch.
+         ///
+         /// @param element The xAOD (interface) object to process
+         /// @return The usual @c StatusCode values
+         ///
+         StatusCode process( const SG::AuxElement& element );
+
+         /// Add one branch to the output tree
+         ///
+         /// This function is used during the setup of the output tree to create
+         /// one branch in it, from one specific auxiliary variable. The type of
+         /// the variable is figured out at runtime using the auxiliary store
+         /// infrastructure.
+         ///
+         /// @param tree The tree to create the branch in
+         /// @param auxName Name of the auxiliary variable to create the branch
+         ///                from
+         /// @param branchName The name of the branch to create in the tree
+         /// @param allowMissing Set to @c true to print an error message in case
+         ///                     of a failure
+         /// @param created Used to store if the branch was actually created
+         /// @return The usual @c StatusCode values
+         ///
+         StatusCode addBranch( TTree& tree, const std::string& auxName,
+                               const std::string& branchName,
+                               bool allowMissing,
+                               bool &created );
+
+      private:
+         /// Class writing one variable from an xAOD object into a branch
+         ///
+         /// It is used for both setting up the branch in the outut @c TTree
+         /// during the setup of the tree, and then to fill the "output
+         /// variable" with the right payload during the event processing.
+         ///
+         /// Note that since we may have a *lot* of such objects, I didn't want
+         /// to make it inherit from @c asg::AsgMessaging. Which means that all
+         /// of the class's functions need to receive its parent's message
+         /// stream object to be able to log error messages "nicely".
+         ///
+         /// Also note that since this is very much an internal class, all of
+         /// its members are public. Since the owner of such objects should know
+         /// perfectly well how they behave.
+         ///
+         class BranchProcessor {
+
+         public:
+            /// Function setting up the object, and the branch
+            ///
+            /// This is pretty much the constructor of the class. I just decided
+            /// to implement it as a regular function and not a "real"
+            /// constructor, to be able to return a @c StatusCode value from the
+            /// call. Since the setup of the object may very well fail.
+            ///
+            /// @param tree The tree to set up the new branch in
+            /// @param auxName The name of the auxiliary variable to create
+            ///                a branch from
+            /// @param branchName Name of the branch to create in the tree
+            /// @param msg Reference to the parent's @c MsgStream object
+            /// @return The usual @c StatusCode values
+            ///
+            StatusCode setup( TTree& tree, const std::string& auxName,
+                              const std::string& branchName,
+                              MsgStream& msg );
+
+            /// Function processing the object, filling the variable
+            ///
+            /// This function is called by @c ElementProcessor, to extract one
+            /// variable from the standalone object, and move its payload into
+            /// the memory address from which the output tree is writing its
+            /// branch.
+            ///
+            /// @param element The standalone object to get the auxiliary
+            ///                variable from
+            /// @param msg Reference to the parent's @c MsgStream object
+            /// @return The usual @c StatusCode values
+            ///
+            StatusCode process( const SG::AuxElement& element,
+                                MsgStream& msg );
+
+            /// Name of the branch being written
+            std::string m_branchName;
+            /// Object accessing the variable in question
+            std::unique_ptr< SG::AuxElement::TypelessConstAccessor > m_acc;
+            /// Pointer to the helper object that handles this variable
+            const SG::IAuxTypeVectorFactory* m_factory = nullptr;
+            /// The object managing the memory of the written variable
+            std::unique_ptr< SG::IAuxTypeVector > m_data;
+            /// Helper variable, pointing at the object to be written
+            void* m_dataPtr = nullptr;
+
+         }; // class BranchProcessor
+
+         /// List of branch processors set up for this xAOD object
+         ///
+         /// Note that when we set up a branch, we tell @c TTree to remember a
+         /// physical address in memory. To make sure that the address of the
+         /// object held by the branch processors are not moved in memory after
+         /// their construction, we have to use an @c std::list container here.
+         /// @c std::vector would not work. (As it can relocate objects when
+         /// increasing the size of the container.)
+         ///
+         std::list< BranchProcessor > m_branches;
+
+      }; // class ElementProcessor
+
+      /// Class writing all variables from one @c DataVector container
+      ///
+      /// It is designed to work with *any* @c DataVector<SG::AuxElement> type,
+      /// it doesn't have to be an @c xAOD::IParticleContainer. But of course
+      /// that is the main use case for it...
+      ///
+      /// It expects an @c SG::AuxVectorBase object from the caller, iterates
+      /// over the elements of that container using the ROOT dictionary of the
+      /// type, and writes individual variables from the elements of the
+      /// container using the same machinery that @c ElementProcessor employs.
+      ///
+      class ContainerProcessor : public asg::AsgMessaging {
+
+      public:
+         /// Default constructor
+         ///
+         /// We have to have a default constructor to initialise the
+         /// @c asg::AsgMessaging base class correctly. Members of this class
+         /// would not need an explicit constructor themselves.
+         ///
+         ContainerProcessor();
+
+         /// Process the container
+         ///
+         /// This function is called during the event processing to extract
+         /// all configured variables from an xAOD container into the
+         /// output variables set up using @c ContainerProcessor::addBranch.
+         ///
+         /// @param container The xAOD (interface) container to process
+         /// @return The usual @c StatusCode values
+         ///
+         StatusCode process( const SG::AuxVectorBase& container );
+
+         /// Add one branch to the output tree
+         ///
+         /// This function is used during the setup of the output tree to create
+         /// one branch in it, from one specific auxiliary variable. The type of
+         /// the variable is figured out at runtime using the auxiliary store
+         /// infrastructure.
+         ///
+         /// @param tree The tree to create the branch in
+         /// @param auxName Name of the auxiliary variable to create the branch
+         ///                from
+         /// @param branchName The name of the branch to create in the tree
+         /// @param allowMissing Set to @c true to print an error message in case
+         ///                     of a failure
+         /// @param created Used to store if the branch was actually created
+         /// @return The usual @c StatusCode values
+         ///
+         StatusCode addBranch( TTree& tree, const std::string& auxName,
+                               const std::string& branchName,
+                               bool allowMissing,
+                               bool &created );
+
+      private:
+         /// Class writing one variable from an xAOD object into a branch
+         ///
+         /// It is used for both setting up the branch in the outut @c TTree
+         /// during the setup of the tree, and then to fill the "output
+         /// variable" with the right payload during the event processing.
+         ///
+         /// Note that since we may have a *lot* of such objects, I didn't want
+         /// to make it inherit from @c asg::AsgMessaging. Which means that all
+         /// of the class's functions need to receive its parent's message
+         /// stream object to be able to log error messages "nicely".
+         ///
+         /// Also note that since this is very much an internal class, all of
+         /// its members are public. Since the owner of such objects should know
+         /// perfectly well how they behave.
+         ///
+         /// Finally, note that it is more complicated than the
+         /// @c ElementProcessor::BranchProcessor class. Since in this case we
+         /// need to explicitly deal with @c std::vector types, which we need to
+         /// fill explicitly when extracting the variables from the xAOD
+         /// objects.
+         ///
+         class BranchProcessor {
+
+         public:
+            /// Function setting up the object, and the branch
+            StatusCode setup( TTree& tree, const std::string& auxName,
+                              const std::string& branchName,
+                              MsgStream& msg );
+            /// Function (re)sizing the variable for a new event
+            StatusCode resize( size_t size, MsgStream& msg );
+            /// Function processing the object, filling the variable
+            StatusCode process( const SG::AuxElement& element, size_t index,
+                                MsgStream& msg );
+
+            /// Name of the branch being written
+            std::string m_branchName;
+            /// Object accessing the variable in question
+            std::unique_ptr< SG::AuxElement::TypelessConstAccessor > m_acc;
+            /// Pointer to the helper object that handles this variable
+            const SG::IAuxTypeVectorFactory* m_factory = nullptr;
+            /// The object managing the memory of the written variable
+            std::unique_ptr< SG::IAuxTypeVector > m_data;
+            /// Helper variable, pointing at the object to be written
+            void* m_dataPtr = nullptr;
+
+         }; // class BranchProcessor
+
+         /// List of branch processors set up for this xAOD object
+         ///
+         /// Note that when we set up a branch, we tell @c TTree to remember a
+         /// physical address in memory. To make sure that the address of the
+         /// object held by the branch processors are not moved in memory after
+         /// their construction, we have to use an @c std::list container here.
+         /// @c std::vector would not work. (As it can relocate objects when
+         /// increasing the size of the container.)
+         ///
+         std::list< BranchProcessor > m_branches;
+         /// Collection proxy used for iterating over the container
+         TVirtualCollectionProxy* m_collProxy = nullptr;
+         /// Offset of the element type to @c SG::AuxElement
+         int m_auxElementOffset = -1;
+
+      }; // class ContainerProcessor
+
+      /// @name Variables used for the TTree filling
+      /// @{
+
+      /// The tree being written
+      TTree* m_tree = nullptr;
+
+      /// Objects to write branches from
+      std::unordered_map< std::string, ElementProcessor > m_elements;
+      /// Containers to write branches from
+      std::unordered_map< std::string, ContainerProcessor > m_containers;
+
+      /// Internal status flag, showing whether the algorithm is initialised
+      ///
+      /// This is necessary because we can only set up the output @c TTree while
+      /// processing the first event, we can't do it in
+      /// @c CP::AsgxAODNTupleMakerAlg::initialize.
+      ///
+      bool m_isInitialized = false;
+
+      /// The systematic list to consider during execution
+      SysListHandle m_systematicsList{ this };
+
+      /// @}
+
+   }; // class AsgxAODNTupleMakerAlg
+
+} // namespace CP
+
+#endif // ASGANALYSISALGORITHMS_ASGXAODNTUPLEMAKERALG_H
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/EventFlagSelectionAlg.h b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/EventFlagSelectionAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..3d75ba62d7c485f872c4a87596283344ef846d80
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/EventFlagSelectionAlg.h
@@ -0,0 +1,42 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Tadej Novak
+
+#ifndef ASG_ANALYSIS_ALGORITHMS__EVENT_FLAG_SELECTION_ALG_H
+#define ASG_ANALYSIS_ALGORITHMS__EVENT_FLAG_SELECTION_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <AnaAlgorithm/FilterReporterParams.h>
+#include <SelectionHelpers/ISelectionAccessor.h>
+
+namespace CP
+{
+  /// \brief an algorithm for selecting events by flag
+  class EventFlagSelectionAlg final : public EL::AnaAlgorithm
+  {
+  public:
+    EventFlagSelectionAlg(const std::string &name,
+                          ISvcLocator *svcLoc = nullptr);
+
+    virtual StatusCode initialize() final;
+    virtual StatusCode execute() final;
+    virtual StatusCode finalize() final;
+
+  private:
+    /// \brief flags that we want to select events with
+    std::vector<std::string> m_selFlags;
+    
+    /// \brief invert flags
+    std::vector<bool> m_invertFlags;
+    
+    /// \brief a vector of accessors to read the flags
+    std::vector<std::unique_ptr<ISelectionAccessor>> m_accessors;
+
+    /// \brief the filter reporter parameters
+    EL::FilterReporterParams m_filterParams {this, "event flag selection"};
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/EventSelectionByObjectFlagAlg.h b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/EventSelectionByObjectFlagAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..671020270bc1d123dcb59fd9a3c672ed05b9dc34
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/EventSelectionByObjectFlagAlg.h
@@ -0,0 +1,61 @@
+/*
+  Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Miha Muskinja
+
+#ifndef ASG_ANALYSIS_ALGORITHMS__EVENT_SELECTION_BY_OBJECT_FLAG_ALG_H
+#define ASG_ANALYSIS_ALGORITHMS__EVENT_SELECTION_BY_OBJECT_FLAG_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <SelectionHelpers/SelectionReadHandle.h>
+#include <SystematicsHandles/SysDecorationHandle.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <SystematicsHandles/SysFilterReporterParams.h>
+#include <SystematicsHandles/SysListHandle.h>
+#include <SystematicsHandles/SysReadHandle.h>
+#include <xAODBase/IParticleContainer.h>
+#include <xAODEventInfo/EventInfo.h>
+
+namespace CP {
+/// \brief an algorithm for selecting events based on object flags
+/// (e.g. bad muon veto or b-jet veto)
+class EventSelectionByObjectFlagAlg final : public EL::AnaAlgorithm {
+  public:
+    EventSelectionByObjectFlagAlg(const std::string &name,
+                                   ISvcLocator *svcLoc = nullptr);
+
+  public:
+    StatusCode initialize() override;
+
+  public:
+    StatusCode execute() override;
+
+  public:
+    StatusCode finalize() override;
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList{this};
+
+    /// \brief the filter reporter
+  private:
+    SysFilterReporterParams m_filterParams {this, "object flag selection"};
+
+    /// \brief the particle collection we run on
+  private:
+    SysReadHandle<xAOD::IParticleContainer> m_particleHandle{
+        this, "particles", "", "the particle collection to run on"};
+
+    /// \brief the preselection we apply to our input
+  private:
+    SelectionReadHandle m_preselection{this, "preselection", "", "the preselection to apply"};
+
+    /// \brief the preselection we apply to our input
+  private:
+    SelectionReadHandle m_veto{this, "veto", "", "selection upon which events are vetoed"};
+};
+
+} // namespace CP
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/IsolationCloseByCorrectionAlg.h b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/IsolationCloseByCorrectionAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..670aeaa975af9cbbd0959833bfbd816af29bb316
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/IsolationCloseByCorrectionAlg.h
@@ -0,0 +1,63 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef ASG_ANALYSIS_ALGORITHMS__ISOLATION_CLOSE_BY_CORRECTION_ALG_H
+#define ASG_ANALYSIS_ALGORITHMS__ISOLATION_CLOSE_BY_CORRECTION_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <IsolationSelection/IIsolationCloseByCorrectionTool.h>
+#include <SelectionHelpers/OutOfValidityEventHelper.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+
+namespace CP
+{
+  /// \brief an algorithm for calling \ref IIsolationCloseByCorrectionTool
+
+  class IsolationCloseByCorrectionAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    IsolationCloseByCorrectionAlg (const std::string& name, 
+                                   ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+    
+
+
+    /// The OR toolbox
+  private:
+    ToolHandle<IIsolationCloseByCorrectionTool> m_isolationCorrectionTool {
+      "CP::IsolationCloseByCorrectionTool", this};
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+  private:
+    SysCopyHandle<xAOD::ElectronContainer> m_electronsHandle {
+      this, "electrons", "", "the electrons container to use"};
+    SysCopyHandle<xAOD::MuonContainer> m_muonsHandle {
+      this, "muons", "", "the muons container to use"};
+    SysCopyHandle<xAOD::PhotonContainer> m_photonsHandle {
+      this, "photons", "", "the photons container to use"};
+
+  private:
+    int m_topoEtConeModel {IIsolationCloseByCorrectionTool::TopoConeCorrectionModel::DirectCaloClusters};
+
+    /// \brief the helper for OutOfValidity results
+  private:
+    OutOfValidityEventHelper m_outOfValidity {this};
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/KinematicHistAlg.h b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/KinematicHistAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..c9c1598f7027050c5e0eb67c34f32a1886f61c8a
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/KinematicHistAlg.h
@@ -0,0 +1,83 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef ASG_ANALYSIS_ALGORITHMS__KINEMATIC_HIST_ALG_H
+#define ASG_ANALYSIS_ALGORITHMS__KINEMATIC_HIST_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <SelectionHelpers/SelectionReadHandle.h>
+#include <SystematicsHandles/SysReadHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+#include <xAODBase/IParticleContainer.h>
+
+namespace CP
+{
+  /// \brief an algorithm for dumping the kinematics of an IParticle
+  /// container into histograms
+  ///
+  /// This is mostly meant as a temporary helper algorithm to debug
+  /// the common CP algorithms as they get developed.
+
+  class KinematicHistAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    KinematicHistAlg (const std::string& name, 
+                            ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the jet collection we run on
+  private:
+    SysReadHandle<xAOD::IParticleContainer> m_inputHandle {
+      this, "input", "", "the input collection to run on"};
+
+    /// \brief the preselection we apply to our input
+  private:
+    SelectionReadHandle m_preselection {
+      this, "preselection", "", "the preselection to apply"};
+
+    /// \brief the pattern for histogram names
+  private:
+    std::string m_histPattern {"%VAR%_%SYS%"};
+
+
+    /// \brief the histograms we fill per systematic and object
+  private:
+    struct HistSubgroup
+    {
+      TH1 *pt = nullptr;
+      TH1 *eta = nullptr;
+      TH1 *phi = nullptr;
+    };
+
+
+    /// \brief the histograms we fill per systematic
+  private:
+    struct HistGroup
+    {
+      TH1 *multiplicity = nullptr;
+      std::vector<HistSubgroup> perObject;
+    };
+
+    /// \brief the created histograms
+  private:
+    std::unordered_map<CP::SystematicSet,HistGroup> m_hist;
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/ObjectCutFlowHistAlg.h b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/ObjectCutFlowHistAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..426f79cd003e470e914e4b647d62359b1a5e7b21
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/ObjectCutFlowHistAlg.h
@@ -0,0 +1,85 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef ASG_ANALYSIS_ALGORITHMS__OBJECT_CUT_FLOW_HIST_ALG_H
+#define ASG_ANALYSIS_ALGORITHMS__OBJECT_CUT_FLOW_HIST_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <SelectionHelpers/ISelectionAccessor.h>
+#include <SelectionHelpers/SelectionReadHandle.h>
+#include <SystematicsHandles/SysReadHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+#include <xAODBase/IParticleContainer.h>
+
+namespace CP
+{
+  /// \brief an algorithm for dumping the kinematics of an IParticle
+  /// container into histograms
+  ///
+  /// This is mostly meant as a temporary helper algorithm to debug
+  /// the common CP algorithms as they get developed.
+
+  class ObjectCutFlowHistAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    ObjectCutFlowHistAlg (const std::string& name, 
+                            ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the jet collection we run on
+  private:
+    SysReadHandle<xAOD::IParticleContainer> m_inputHandle {
+      this, "input", "", "the input collection to run on"};
+
+    /// \brief the preselection we apply to our input
+  private:
+    SelectionReadHandle m_preselection {
+      this, "preselection", "", "the preselection to apply"};
+
+    /// \brief the pattern for histogram names
+  private:
+    std::string m_histPattern {"cutflow_%SYS%"};
+
+
+  private:
+    std::vector<std::string> m_selection;
+
+  private:
+    std::vector<unsigned> m_selectionNCuts;
+
+    /// the list of accessors and cut ignore list
+  private:
+    std::vector<std::pair<std::unique_ptr<ISelectionAccessor>,unsigned> > m_accessors;
+
+    /// \brief the total number of cuts configured (needed to
+    /// configure histograms)
+  private:
+    unsigned m_allCutsNum = 0;
+
+    /// \brief the created histograms
+  private:
+    std::unordered_map<CP::SystematicSet,TH1*> m_hist;
+
+    /// \brief histogram bin labels
+  private:
+    std::vector<std::string> m_labels;
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/OverlapRemovalAlg.h b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/OverlapRemovalAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..10274653e53253077d4fa66027a33fceaa3c8fd5
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/OverlapRemovalAlg.h
@@ -0,0 +1,61 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef ASG_ANALYSIS_ALGORITHMS__OVERLAP_REMOVAL_ALG_H
+#define ASG_ANALYSIS_ALGORITHMS__OVERLAP_REMOVAL_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <AssociationUtils/ToolBox.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+
+namespace CP
+{
+  /// \brief an algorithm for calling \ref IPileupReweightingTool
+
+  class OverlapRemovalAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    OverlapRemovalAlg (const std::string& name, 
+                       ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+    
+
+
+    /// The OR toolbox
+  private:
+    ToolHandle<ORUtils::IOverlapRemovalTool> m_overlapTool {
+      "ORUtils::OverlapRemovalTool", this};
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+  private:
+    SysCopyHandle<xAOD::ElectronContainer> m_electronsHandle {
+      this, "electrons", "", "the electrons container to use"};
+    SysCopyHandle<xAOD::MuonContainer> m_muonsHandle {
+      this, "muons", "", "the muons container to use"};
+    SysCopyHandle<xAOD::JetContainer> m_jetsHandle {
+      this, "jets", "", "the jets container to use"};
+    SysCopyHandle<xAOD::TauJetContainer> m_tausHandle {
+      this, "taus", "", "the taus container to use"};
+    SysCopyHandle<xAOD::PhotonContainer> m_photonsHandle {
+      this, "photons", "", "the photons container to use"};
+    SysCopyHandle<xAOD::JetContainer> m_fatJetsHandle {
+      this, "fatJets", "", "the fat jets container to use"};
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/PMGTruthWeightAlg.h b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/PMGTruthWeightAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..d053643e9aca303af920e20f79d12ca759ab08c0
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/PMGTruthWeightAlg.h
@@ -0,0 +1,55 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Tadej Novak
+
+
+#ifndef ASG_ANALYSIS_ALGORITHMS__PMG_TRUTH_WEIGHT_ALG_H
+#define ASG_ANALYSIS_ALGORITHMS__PMG_TRUTH_WEIGHT_ALG_H
+
+#include <xAODEventInfo/EventInfo.h>
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <PMGAnalysisInterfaces/IPMGTruthWeightTool.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <SystematicsHandles/SysDecorationHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+
+namespace CP
+{
+  /// \brief an algorithm for calling \ref IPMGTruthWeightTool
+  class PMGTruthWeightAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    PMGTruthWeightAlg (const std::string& name, 
+                       ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+
+    /// \brief the tool
+  private:
+    ToolHandle<PMGTools::IPMGTruthWeightTool> m_truthWeightTool;
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the event info we decorate
+  private:
+    SysCopyHandle<xAOD::EventInfo> m_eventInfoHandle {
+      this, "eventInfo", "EventInfo", "the event info object to run on"};
+
+    /// \brief the decoration for the truth weights
+  private:
+    SysDecorationHandle<float> m_decoration {
+      this, "decoration", "", "the decoration for the truth weights"};
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/PileupReweightingAlg.h b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/PileupReweightingAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..57846bf217a5c4014b044c68c54fa8f0ca911cca
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/PileupReweightingAlg.h
@@ -0,0 +1,81 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef ASG_ANALYSIS_ALGORITHMS__PILEUP_REWEIGHTING_ALG_H
+#define ASG_ANALYSIS_ALGORITHMS__PILEUP_REWEIGHTING_ALG_H
+
+#include <xAODEventInfo/EventInfo.h>
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <AsgAnalysisInterfaces/IPileupReweightingTool.h>
+#include <SelectionHelpers/OutOfValidityHelper.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+
+namespace CP
+{
+  /// \brief an algorithm for calling \ref IPileupReweightingTool
+
+  class PileupReweightingAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    PileupReweightingAlg (const std::string& name, 
+                          ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+    
+
+
+    /// \brief the smearing tool
+  private:
+    ToolHandle<IPileupReweightingTool> m_pileupReweightingTool;
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the jet collection we run on
+  private:
+    SysCopyHandle<xAOD::EventInfo> m_eventInfoHandle {
+      this, "eventInfo", "EventInfo", "the event info object to run on"};
+
+    /// \brief the decoration for the corrected and scaled average interactions per crossing
+  private:
+    std::string m_correctedScaledAverageMuDecoration;
+
+    /// \brief the decoration for the corrected actual interactions per crossing
+  private:
+    std::string m_correctedActualMuDecoration;
+
+    /// \brief the decoration for the corrected and scaled actual interactions per crossing
+  private:
+    std::string m_correctedScaledActualMuDecoration;
+
+    /// \brief the accessor for \ref m_correctedScaledAverageMuDecoration
+  private:
+    std::unique_ptr<const SG::AuxElement::Accessor<float> > m_correctedScaledAverageMuAccessor;
+
+    /// \brief the accessor for \ref m_correctedActualMuDecoration
+  private:
+    std::unique_ptr<const SG::AuxElement::Accessor<float> > m_correctedActualMuAccessor;
+
+    /// \brief the accessor for \ref m_correctedScaledAverageMuDecoration
+  private:
+    std::unique_ptr<const SG::AuxElement::Accessor<float> > m_correctedScaledActualMuAccessor;
+
+    /// \brief the helper for OutOfValidity results
+  private:
+    OutOfValidityHelper m_outOfValidity {this};
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/SysListDumperAlg.h b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/SysListDumperAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..6b0f3d775ac5beda2ddff8213a69963ca549a301
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/SysListDumperAlg.h
@@ -0,0 +1,50 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Tadej Novak
+
+
+#ifndef ASG_ANALYSIS_ALGORITHMS__SYS_LIST_DUMPER_ALG_H
+#define ASG_ANALYSIS_ALGORITHMS__SYS_LIST_DUMPER_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <SystematicsHandles/SysListHandle.h>
+
+namespace CP
+{
+  /// \brief Dump systematics names into a histogram
+
+  class SysListDumperAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief standard constructor
+    /// \par Guarantee
+    ///   strong
+    /// \par Failures
+    ///   out of memory II
+  public:
+    SysListDumperAlg (const std::string& name, 
+                      ISvcLocator* pSvcLocator);
+
+  public:
+    virtual ::StatusCode initialize () override;
+
+  public:
+    virtual ::StatusCode execute () override;
+
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the name of the histogram to use
+  private:
+    std::string m_histogramName {sysListDefaultName()};
+
+    /// \brief whether the next event will be the first event
+  private:
+    bool m_firstEvent = true;
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/SysListLoaderAlg.h b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/SysListLoaderAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..aa6beb74e7dc94eeb4c9180d44704e4b04c446f7
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/SysListLoaderAlg.h
@@ -0,0 +1,84 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef ASG_ANALYSIS_ALGORITHMS__SYS_LIST_LOADER_ALG_H
+#define ASG_ANALYSIS_ALGORITHMS__SYS_LIST_LOADER_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <PATInterfaces/SystematicSet.h>
+#include <SystematicsHandles/SysListType.h>
+
+namespace CP
+{
+  /// \todo add documentation
+
+  class SysListLoaderAlg final : public EL::AnaAlgorithm
+  {
+    //
+    // public interface
+    //
+
+    /// \brief standard constructor
+    /// \par Guarantee
+    ///   strong
+    /// \par Failures
+    ///   out of memory II
+  public:
+    SysListLoaderAlg (const std::string& name, 
+                      ISvcLocator* pSvcLocator);
+
+
+
+    //
+    // inherited interface
+    //
+
+  public:
+    virtual ::StatusCode initialize () override;
+
+  public:
+    virtual ::StatusCode execute () override;
+
+
+
+    //
+    // private interface
+    //
+
+    /// \brief the name under which to store the systematics in the
+    /// event store
+  private:
+    std::string m_systematicsName {sysListDefaultName()};
+
+    /// \brief the names of the systematics to request
+  private:
+    std::vector<std::string> m_systematicsList;
+
+    /// \brief the regular expression for filterinf systematics
+  private:
+    std::string m_systematicsRegex {"(.*)"};
+
+    /// \brief load all recommended systematics at the given number of
+    /// sigmas
+    ///
+    /// The idea here is that this allows to run a simple analysis by
+    /// itself without having to generate the list of systematics
+    /// manually.
+  private:
+    float m_sigmaRecommended = 0;
+
+    /// \brief the list of actual systematics
+  private:
+    std::vector<CP::SystematicSet> m_systematicsVector;
+
+    /// \brief whether the next event will be the first event
+  private:
+    bool m_firstEvent = true;
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/TreeFillerAlg.h b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/TreeFillerAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..d845bedb3bf74b782b01b4e77b790ade1ddf70b6
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/TreeFillerAlg.h
@@ -0,0 +1,84 @@
+// Dear emacs, this is -*- c++ -*-
+//
+// Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+//
+#ifndef ASGANALYSISALGORITHMS_TREEFILLERALG_H
+#define ASGANALYSISALGORITHMS_TREEFILLERALG_H
+
+// System include(s):
+#include <unordered_map>
+#include <string>
+#include <vector>
+#include <memory>
+#include <list>
+
+// Framework include(s):
+#include "AsgTools/AsgMessaging.h"
+#include "AnaAlgorithm/AnaAlgorithm.h"
+#include "SystematicsHandles/SysListHandle.h"
+
+// EDM include(s):
+#include "AthContainersInterfaces/IAuxTypeVector.h"
+#include "AthContainers/AuxElement.h"
+
+// Forward declaration(s):
+class TClass;
+class TTree;
+class TVirtualCollectionProxy;
+namespace SG {
+   class AuxVectorBase;
+   class IAuxTypeVectorFactory;
+}
+
+namespace CP {
+
+   /// Algorithm that writes events to a tree filled by preceding
+   /// algorithms to fill
+   ///
+   /// This is meant in conjunction with \ref TreeMakerAlg and one or
+   /// more of tree-variable filler algorithms in-between.  The idea
+   /// behind this specific design is that it allows multiple
+   /// implementations of tree-variable filler algorithms to work
+   /// together in filling different variables in the same tree, as
+   /// well as making the configuration for each tree-variable filler
+   /// algorithm simpler.
+   ///
+   /// @author Nils Krumnack <Nils.Erik.Krumnack@cern.ch>
+   /// @author Attila Krasznahorkay <Attila.Krasznahorkay@cern.ch>
+
+   class TreeFillerAlg : public EL::AnaAlgorithm {
+
+   public:
+      /// Algorithm constructor
+      TreeFillerAlg( const std::string& name, ISvcLocator* svcLoc );
+
+      /// @name Functions inherited from @c EL::AnaAlgorithm
+      /// @{
+
+      /// Function executed once per event
+      StatusCode execute() override;
+
+      /// @}
+
+   private:
+      /// @name Algorithm properties
+      /// @{
+
+      /// The name of the output tree to write
+      std::string m_treeName;
+
+      /// @}
+
+      /// @name Variables used for the TTree filling
+      /// @{
+
+      /// The tree being written
+      TTree* m_tree = nullptr;
+
+      /// @}
+
+   }; // class TreeFillerAlg
+
+} // namespace CP
+
+#endif // ASGANALYSISALGORITHMS_TREEFILLERALG_H
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/TreeMakerAlg.h b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/TreeMakerAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..a5b4a671d52e43c7ccf621b135c69a6601f8ecae
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/TreeMakerAlg.h
@@ -0,0 +1,68 @@
+// Dear emacs, this is -*- c++ -*-
+//
+// Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+//
+#ifndef ASGANALYSISALGORITHMS_TREEMAKERALG_H
+#define ASGANALYSISALGORITHMS_TREEMAKERALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+
+class TTree;
+
+namespace CP {
+
+   /// Algorithm that creates an empty tree for subsequent algorithms
+   /// to fill
+   ///
+   /// This is meant in conjunction with \ref TreeFillerAlg and one or
+   /// more of tree-variable filler algorithms in-between.  The idea
+   /// behind this specific design is that it allows multiple
+   /// implementations of tree-variable filler algorithms to work
+   /// together in filling different variables in the same tree, as
+   /// well as making the configuration for each tree-variable filler
+   /// algorithm simpler.
+   ///
+   /// @author Nils Krumnack <Nils.Erik.Krumnack@cern.ch>
+   /// @author Attila Krasznahorkay <Attila.Krasznahorkay@cern.ch>
+   ///
+   class TreeMakerAlg : public EL::AnaAlgorithm {
+
+   public:
+      /// Algorithm constructor
+      TreeMakerAlg( const std::string& name, ISvcLocator* svcLoc );
+
+      /// @name Functions inherited from @c EL::AnaAlgorithm
+      /// @{
+
+      /// Function executed once per event
+      StatusCode execute() override;
+
+      /// @}
+
+   private:
+      /// Function setting up the internal data structures on the first event
+      StatusCode setupTree();
+
+      /// @name Algorithm properties
+      /// @{
+
+      /// The name of the output tree to write
+      std::string m_treeName;
+      /// Flust setting for the output tree
+      int m_treeAutoFlush;
+
+      /// @}
+
+      /// @name Helper variables
+      /// @{
+
+      /// Configured tree status
+      bool m_treeConfigured{false};
+
+      /// @}
+
+   }; // class TreeMakerAlg
+
+} // namespace CP
+
+#endif //  ASGANALYSISALGORITHMS_TREEMAKERALG_H
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/selection.xml b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/selection.xml
new file mode 100644
index 0000000000000000000000000000000000000000..bd28958853419777730a150fa06ad7a694ca698d
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/selection.xml
@@ -0,0 +1,26 @@
+<lcgdict>
+
+   <class name="CP::AsgFlagSelectionTool" />
+   <class name="CP::AsgPtEtaSelectionTool" />
+
+   <class name="CP::AsgCutBookkeeperAlg" />
+   <class name="CP::AsgEventScaleFactorAlg" />
+   <class name="CP::AsgLeptonTrackSelectionAlg" />
+   <class name="CP::AsgOriginalObjectLinkAlg" />
+   <class name="CP::AsgSelectionAlg" />
+   <class name="CP::AsgViewSelectionAlg" />
+   <class name="CP::AsgxAODNTupleMakerAlg" />
+   <class name="CP::EventFlagSelectionAlg" />
+   <class name="CP::EventSelectionByObjectFlagAlg" />
+   <class name="CP::IsolationCloseByCorrectionAlg" />
+   <class name="CP::KinematicHistAlg" />
+   <class name="CP::ObjectCutFlowHistAlg" />
+   <class name="CP::OverlapRemovalAlg" />
+   <class name="CP::PileupReweightingAlg" />
+   <class name="CP::PMGTruthWeightAlg" />
+   <class name="CP::SysListDumperAlg" />
+   <class name="CP::SysListLoaderAlg" />
+   <class name="CP::TreeFillerAlg" />
+   <class name="CP::TreeMakerAlg" />
+
+</lcgdict>
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/CMakeLists.txt b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..7a14cc76e80c0a01fc95a4c66032042d4e071b02
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/CMakeLists.txt
@@ -0,0 +1,113 @@
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+#
+# @author Nils Krumnack
+
+atlas_subdir( AsgAnalysisAlgorithms )
+
+atlas_depends_on_subdirs(
+   PUBLIC
+   Control/AthContainers
+   Control/AthContainersInterfaces
+   Control/AthToolSupport/AsgTools
+   Event/xAOD/xAODBase
+   Event/xAOD/xAODCutFlow
+   Event/xAOD/xAODEventInfo
+   Event/xAOD/xAODMetaData
+   PhysicsAnalysis/Algorithms/SelectionHelpers
+   PhysicsAnalysis/Algorithms/SystematicsHandles
+   PhysicsAnalysis/AnalysisCommon/PATCore
+   PhysicsAnalysis/AnalysisCommon/PATInterfaces
+   PhysicsAnalysis/AnalysisCommon/IsolationSelection
+   PhysicsAnalysis/D3PDTools/AnaAlgorithm
+   PhysicsAnalysis/Interfaces/AsgAnalysisInterfaces
+   PhysicsAnalysis/Interfaces/PMGAnalysisInterfaces
+   PRIVATE
+   Event/xAOD/xAODCore
+   Event/xAOD/xAODJet
+   Event/xAOD/xAODMuon
+   Event/xAOD/xAODTau
+   Event/xAOD/xAODEgamma
+   Event/xAOD/xAODTracking
+   Event/xAOD/xAODTruth
+   PhysicsAnalysis/D3PDTools/RootCoreUtils )
+
+atlas_add_library( AsgAnalysisAlgorithmsLib
+   AsgAnalysisAlgorithms/*.h AsgAnalysisAlgorithms/*.icc Root/*.cxx
+   PUBLIC_HEADERS AsgAnalysisAlgorithms
+   LINK_LIBRARIES AthContainers AthContainersInterfaces AsgTools xAODBase
+     xAODEventInfo SelectionHelpersLib SystematicsHandlesLib PATCoreLib
+     PATInterfaces AnaAlgorithmLib AsgAnalysisInterfaces AssociationUtilsLib
+     IsolationSelectionLib
+   PRIVATE_LINK_LIBRARIES xAODCore xAODCutFlow xAODMetaData xAODJet xAODMuon xAODTau xAODEgamma xAODTracking xAODTruth
+     RootCoreUtils )
+
+atlas_add_dictionary( AsgAnalysisAlgorithmsDict
+   AsgAnalysisAlgorithms/AsgAnalysisAlgorithmsDict.h
+   AsgAnalysisAlgorithms/selection.xml
+   LINK_LIBRARIES AsgAnalysisAlgorithmsLib )
+
+if( NOT XAOD_STANDALONE )
+   atlas_add_component( AsgAnalysisAlgorithms
+      src/*.h src/*.cxx src/components/*.cxx
+      LINK_LIBRARIES GaudiKernel AsgAnalysisAlgorithmsLib )
+endif()
+
+atlas_install_python_modules( python/*.py )
+atlas_install_joboptions( share/*_jobOptions.py )
+atlas_install_scripts( share/*_eljob.py )
+
+if( XAOD_STANDALONE )
+  atlas_add_test( EventAlgsTestJobData
+     SCRIPT EventAlgorithmsTest_eljob.py --data-type data --unit-test
+     PROPERTIES TIMEOUT 600 )
+  atlas_add_test( EventAlgsTestJobFullSim
+     SCRIPT EventAlgorithmsTest_eljob.py --data-type mc --unit-test
+     PROPERTIES TIMEOUT 600 )
+  atlas_add_test( EventAlgsTestJobFastSim
+     SCRIPT EventAlgorithmsTest_eljob.py --data-type afii --unit-test
+     PROPERTIES TIMEOUT 600 )
+  atlas_add_test( OverlapRemovalTestJobData
+     SCRIPT OverlapAlgorithmsTest_eljob.py --data-type data --unit-test
+     PROPERTIES TIMEOUT 600 )
+  atlas_add_test( OverlapRemovalTestJobFullSim
+     SCRIPT OverlapAlgorithmsTest_eljob.py --data-type mc --unit-test
+     PROPERTIES TIMEOUT 600 )
+  atlas_add_test( OverlapRemovalTestJobFastSim
+     SCRIPT OverlapAlgorithmsTest_eljob.py --data-type afii --unit-test
+     PROPERTIES TIMEOUT 600 )
+  atlas_add_test( GeneratorAlgsTestJobFullSim
+     SCRIPT GeneratorAlgorithmsTest_eljob.py --data-type mc --unit-test
+     PROPERTIES TIMEOUT 600 )
+else()
+   atlas_add_test( EventAlgsTestJobData
+      SCRIPT athena.py
+             AsgAnalysisAlgorithms/EventAlgorithmsTest_jobOptions.py - --data-type data
+      PROPERTIES TIMEOUT 600 )
+   atlas_add_test( EventAlgsTestJobFullSim
+      SCRIPT athena.py
+             AsgAnalysisAlgorithms/EventAlgorithmsTest_jobOptions.py - --data-type mc
+      PROPERTIES TIMEOUT 600 )
+   atlas_add_test( EventAlgsTestJobFastSim
+      SCRIPT athena.py
+             AsgAnalysisAlgorithms/EventAlgorithmsTest_jobOptions.py - --data-type afii
+      PROPERTIES TIMEOUT 600 )
+   atlas_add_test( GeneratorAlgsTestJob
+      SCRIPT athena.py
+               AsgAnalysisAlgorithms/GeneratorAlgorithmsTest_jobOptions.py - --data-type mc
+      PROPERTIES TIMEOUT 600 )
+
+   if( NOT "${CMAKE_PROJECT_NAME}" STREQUAL "AthDerivation" )
+      atlas_add_test( OverlapRemovalTestJobData
+         SCRIPT athena.py
+                AsgAnalysisAlgorithms/OverlapAlgorithmsTest_jobOptions.py - --data-type data
+         PROPERTIES TIMEOUT 600 )
+      atlas_add_test( OverlapRemovalTestJobFullSim
+         SCRIPT athena.py
+                AsgAnalysisAlgorithms/OverlapAlgorithmsTest_jobOptions.py - --data-type mc
+         PROPERTIES TIMEOUT 600 )
+      atlas_add_test( OverlapRemovalTestJobFastSim
+         SCRIPT athena.py
+                AsgAnalysisAlgorithms/OverlapAlgorithmsTest_jobOptions.py - --data-type afii
+         PROPERTIES TIMEOUT 600 )
+   endif()
+endif()
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/AsgCutBookkeeperAlg.cxx b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/AsgCutBookkeeperAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..129c116eed7e4985ac8cade819cab5f88f427cfd
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/AsgCutBookkeeperAlg.cxx
@@ -0,0 +1,218 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Tadej Novak
+
+//
+// includes
+//
+
+#include <AsgAnalysisAlgorithms/AsgCutBookkeeperAlg.h>
+#include <SystematicsHandles/Helpers.h>
+
+#include <RootCoreUtils/StringUtil.h>
+#include <TH1.h>
+
+#include <xAODCutFlow/CutBookkeeper.h>
+#include <xAODCutFlow/CutBookkeeperContainer.h>
+#include <xAODMetaData/FileMetaData.h>
+#include <xAODTruth/TruthMetaData.h>
+#include <xAODTruth/TruthMetaDataContainer.h>
+
+#include <regex>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  AsgCutBookkeeperAlg ::
+  AsgCutBookkeeperAlg (const std::string &name,
+                      ISvcLocator *pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+    , m_truthWeightTool ("PMGTools::PMGTruthWeightTool", this)
+  {
+    declareProperty ("runNumber", m_runNumber, "the run number we are processing");
+    declareProperty ("histPattern", m_histPattern, "the pattern for histogram names");
+    declareProperty ("truthWeightTool", m_truthWeightTool, "the truth weight tool");
+    declareProperty ("enableSystematics", m_enableSystematics, "enable systematics");
+  }
+
+
+
+  StatusCode AsgCutBookkeeperAlg ::
+  initialize ()
+  {
+    if (m_runNumber == 0)
+    {
+      ANA_MSG_ERROR ("Run number should be set");
+      return StatusCode::FAILURE;
+    }
+
+    ANA_CHECK (requestFileExecute ());
+
+    // Read the channel number from FileMetaData
+    const xAOD::FileMetaData *fmd{};
+    ANA_CHECK (inputMetaStore()->retrieve(fmd, "FileMetaData"));
+
+    float _flt_channel_number{};
+    if (fmd->value(xAOD::FileMetaData::mcProcID, _flt_channel_number))
+    {
+      m_mcChannelNumber = static_cast<uint32_t>(_flt_channel_number);
+    }
+    if (m_mcChannelNumber == 0)
+    {
+      ANA_MSG_WARNING ("MC channel number could not be read from the FileMetaData.");
+
+      // Try also TruthMetaData
+      const xAOD::TruthMetaDataContainer *tmd{};
+      if (inputMetaStore()->contains<xAOD::TruthMetaDataContainer>("TruthMetaData"))
+      {
+        ATH_CHECK (inputMetaStore()->retrieve(tmd, "TruthMetaData"));
+        ATH_MSG_DEBUG("Loaded xAOD::TruthMetaDataContainer");
+
+        // If we only have one metadata item take MC channel from there if needed
+        if (tmd->size() == 1) {
+          m_mcChannelNumber = tmd->at(0)->mcChannelNumber();
+          ATH_MSG_WARNING("... MC channel number taken from the metadata as " << m_mcChannelNumber);
+        }
+      }
+    }
+
+    // Prepare for systematics
+    ANA_CHECK (m_truthWeightTool.retrieve());
+
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode AsgCutBookkeeperAlg ::
+  fileExecute ()
+  {
+    ANA_MSG_DEBUG ("Updating CutBookkeeper information");
+
+    // Update MC channel number if needed
+    if (m_mcChannelNumber == 0)
+    {
+      const xAOD::FileMetaData *fmd{};
+      ANA_CHECK (inputMetaStore()->retrieve(fmd, "FileMetaData"));
+
+      float _flt_channel_number{};
+      if (fmd->value(xAOD::FileMetaData::mcProcID, _flt_channel_number)) {
+        m_mcChannelNumber = static_cast<uint32_t>(_flt_channel_number);
+      }
+    }
+
+    // Retrieve complete CutBookkeeperContainer
+    const xAOD::CutBookkeeperContainer *completeCBC{};
+    ANA_CHECK (inputMetaStore()->retrieve(completeCBC, "CutBookkeepers"));
+
+    // Find the max cycle
+    int maxCycle{-1};
+    const xAOD::CutBookkeeper *allEvents{};
+    for (const xAOD::CutBookkeeper *cbk : *completeCBC)
+    {
+      ANA_MSG_DEBUG ("Complete cbk name: " << cbk->name() << " - stream: " << cbk->inputStream());
+  
+      if (cbk->cycle() > maxCycle && cbk->name() == "AllExecutedEvents" && cbk->inputStream() == "StreamAOD")
+      {
+        allEvents = cbk;
+        maxCycle = cbk->cycle();
+      }
+    }
+
+    if (allEvents == nullptr)
+    {
+      ANA_MSG_ERROR ("Could not find AllExecutedEvents CutBookkeeper information.");
+      return StatusCode::FAILURE;
+    }
+
+    size_t counter{};
+    for (const xAOD::CutBookkeeper *cbk : *completeCBC)
+    {
+      if (cbk->cycle() == maxCycle && cbk->name().find("AllExecutedEvents") == 0 && cbk->inputStream() == "StreamAOD")
+      {
+        static const std::regex re ("AllExecutedEvents.*_([0-9]+)");
+        // Get the CBK index
+        size_t index{0};
+        std::smatch match;
+        if (std::regex_match(cbk->name(), match, re))
+        {
+          index = std::stoi(match[1]);
+        }
+
+        uint64_t nEventsProcessed  = cbk->nAcceptedEvents();
+        double sumOfWeights        = cbk->sumOfEventWeights();
+        double sumOfWeightsSquared = cbk->sumOfEventWeightsSquared();
+
+        // Write CutBookkeeper information to the info
+        ANA_MSG_VERBOSE ("CutBookkeeper information from the current file for index " << index << ":");
+        ANA_MSG_VERBOSE ("Initial events                 = " << nEventsProcessed);
+        ANA_MSG_VERBOSE ("Initial sum of weights         = " << sumOfWeights);
+        ANA_MSG_VERBOSE ("Initial sum of weights squared = " << sumOfWeightsSquared);
+
+        auto it = m_weights.emplace(index, WeightsGroup()).first;
+        it->second.nEventsProcessed    += nEventsProcessed;
+        it->second.sumOfWeights        += sumOfWeights;
+        it->second.sumOfWeightsSquared += sumOfWeightsSquared;
+
+        counter++;
+      }
+    }
+
+    if (counter == 1 && m_enableSystematics) {
+      ANA_MSG_WARNING ("This sample does not support CutBookkeeper systematics. Disabling...");
+      m_enableSystematics = false;
+    }
+
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode AsgCutBookkeeperAlg ::
+  finalize ()
+  {
+    // Temporarily handle systematics directly here
+    std::vector<CP::SystematicSet> systematics;
+    systematics.emplace_back();
+    if (m_enableSystematics)
+    {
+      for (const CP::SystematicVariation &variation : m_truthWeightTool->affectingSystematics())
+      {
+        systematics.emplace_back(CP::SystematicSet({variation}));
+      }
+    }
+
+    for (const CP::SystematicSet &sys : systematics)
+    {
+      std::string name = RCU::substitute(m_histPattern, "%DSID%", std::to_string(m_mcChannelNumber));
+      name = RCU::substitute(name, "%RUN%", std::to_string(m_runNumber));
+      name = makeSystematicsName (name, sys);
+
+      ANA_CHECK (book(TH1F(name.c_str(), "CutBookkeeper Information", 3, 0.5, 3.5)));
+      TH1 *h = hist(name);
+      assert(h != nullptr);
+
+      h->GetXaxis()->SetBinLabel (1, "Initial events");
+      h->GetXaxis()->SetBinLabel (2, "Initial sum of weights");
+      h->GetXaxis()->SetBinLabel (3, "Initial sum of weights squared");
+
+      ANA_MSG_INFO ("CutBookkeeper information will be stored in " << name);
+
+      ANA_CHECK (m_truthWeightTool->applySystematicVariation (sys));
+
+      ANA_MSG_VERBOSE ("Running systematics " << sys.name() << " with index " << m_truthWeightTool->getSysWeightIndex());
+
+      const WeightsGroup &weights = m_weights.at (m_truthWeightTool->getSysWeightIndex());
+      h->SetBinContent (1, weights.nEventsProcessed);
+      h->SetBinContent (2, weights.sumOfWeights);
+      h->SetBinContent (3, weights.sumOfWeightsSquared);
+    }
+
+    return StatusCode::SUCCESS;
+  }
+} // namespace CP
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/AsgEventScaleFactorAlg.cxx b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/AsgEventScaleFactorAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..58a9cb24f8d710f83a1d062146f940b6777dd1a8
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/AsgEventScaleFactorAlg.cxx
@@ -0,0 +1,75 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Tadej Novak
+
+
+//
+// includes
+//
+
+#include <AsgAnalysisAlgorithms/AsgEventScaleFactorAlg.h>
+
+// #include <SelectionHelpers/SelectionHelpers.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  AsgEventScaleFactorAlg ::
+  AsgEventScaleFactorAlg (const std::string& name, 
+                          ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+  {
+  }
+
+
+
+  StatusCode AsgEventScaleFactorAlg ::
+  initialize ()
+  {
+    if (m_scaleFactorInputDecoration.empty() || m_scaleFactorOutputDecoration.empty())
+    {
+      ANA_MSG_ERROR ("no scale factor decoration name set");
+      return StatusCode::FAILURE;
+    }
+
+    m_systematicsList.addHandle (m_particleHandle);
+    ANA_CHECK (m_systematicsList.initialize());
+    ANA_CHECK (m_preselection.initialize());
+
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode AsgEventScaleFactorAlg ::
+  execute ()
+  {
+    ANA_CHECK (m_scaleFactorOutputDecoration.preExecute (m_systematicsList));
+
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode {
+      xAOD::EventInfo *eventInfo = nullptr;
+      ANA_CHECK (m_eventInfoHandle.getCopy (eventInfo, sys));
+
+      xAOD::IParticleContainer *particles = nullptr;
+      ANA_CHECK (m_particleHandle.getCopy (particles, sys));
+
+      float scaleFactor = 1;
+      for (xAOD::IParticle *particle : *particles)
+      {
+        if (m_preselection.getBool (*particle))
+        {
+          scaleFactor *= m_scaleFactorInputDecoration.get (*particle, sys);
+        }
+      }
+
+      m_scaleFactorOutputDecoration.set (*eventInfo, scaleFactor, sys);
+
+      return StatusCode::SUCCESS;
+    });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/AsgFlagSelectionTool.cxx b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/AsgFlagSelectionTool.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..ac9c2aab0f408f2becebc509a276dc2d23ee861a
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/AsgFlagSelectionTool.cxx
@@ -0,0 +1,88 @@
+/*
+ Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+ */
+
+/// @author Teng Jian Khoo
+
+
+
+//
+// includes
+//
+
+#include <AsgAnalysisAlgorithms/AsgFlagSelectionTool.h>
+
+#include <cmath>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+    AsgFlagSelectionTool ::
+    AsgFlagSelectionTool (const std::string& name)
+    : AsgTool (name)
+    {
+      declareProperty ("selectionFlags", m_selFlags, "list of flags to use as selection criteria");
+      declareProperty ("invertFlags",    m_invertFlags, "toggles for inverting the selection (index-parallel to selectionFlags)");
+    }
+
+
+
+    StatusCode AsgFlagSelectionTool ::
+    initialize ()
+    {
+      if(m_invertFlags.size()!=m_selFlags.size() && !m_invertFlags.empty()) {
+          ATH_MSG_ERROR("Property invertFlags has different size to selectionFlags. Please check your configuration");
+          return StatusCode::FAILURE;
+      }
+      // Could also warn if there are fewer values, but we don't have to force users to set where irrelevant.
+      // Maybe warn unless the size is 0, in which case assume all default?
+
+      for(size_t index=0; index<m_selFlags.size(); ++index) {
+          const std::string& thisflag = m_selFlags[index];
+          if (thisflag.empty()) {
+              ATH_MSG_ERROR("Empty string passed as selection flag!");
+              return StatusCode::FAILURE;
+          } else {
+              // Extend m_invertFlags until the size matches m_selectionFlags
+              // Only done in the case that m_invert was empty
+              if(m_invertFlags.size()<index+1) {m_invertFlags.push_back(false);}
+              std::string doInvertStr = m_invertFlags[index] ? "!" : "";
+              m_accept.addCut (doInvertStr + thisflag, doInvertStr + thisflag);
+              std::unique_ptr<ISelectionAccessor> accessor;
+              ATH_CHECK (makeSelectionAccessor (thisflag, accessor, true));
+              m_acc_selFlags.push_back (std::move (accessor));
+          }
+      }
+
+      return StatusCode::SUCCESS;
+    }
+
+
+
+    const Root::TAccept& AsgFlagSelectionTool ::
+    getTAccept () const
+    {
+      return m_accept;
+    }
+
+
+
+    const Root::TAccept& AsgFlagSelectionTool ::
+    accept (const xAOD::IParticle *particle) const
+    {
+      m_accept.clear();
+      for(std::size_t cutIndex=0; cutIndex<m_accept.getNCuts(); ++cutIndex) {
+          // Test against the opposite of the invert value
+          bool testval = !m_invertFlags[cutIndex];
+          ATH_MSG_VERBOSE("Now testing flag \"" << m_selFlags[cutIndex] << "\" requiring value " << testval);
+          m_accept.setCutResult (cutIndex, m_acc_selFlags[cutIndex]->getBool (*particle)==testval);
+      }
+      ATH_MSG_VERBOSE("  Result: " << m_accept);
+
+      return m_accept;
+    }
+}
+
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/AsgLeptonTrackSelectionAlg.cxx b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/AsgLeptonTrackSelectionAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..57f4fed9f244d93ae04b458c80891a1592ca927f
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/AsgLeptonTrackSelectionAlg.cxx
@@ -0,0 +1,194 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+//
+// includes
+//
+
+#include <AsgAnalysisAlgorithms/AsgLeptonTrackSelectionAlg.h>
+
+#include <xAODEgamma/Electron.h>
+#include <xAODEventInfo/EventInfo.h>
+#include <xAODMuon/Muon.h>
+#include <xAODTracking/TrackParticlexAODHelpers.h>
+#include <xAODTracking/VertexContainer.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  AsgLeptonTrackSelectionAlg ::
+  AsgLeptonTrackSelectionAlg (const std::string& name, 
+                     ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+  {
+    declareProperty ("maxD0Significance", m_maxD0Significance, "maximum d0 significance (or 0 for no cut)");
+    declareProperty ("maxDeltaZ0SinTheta", m_maxDeltaZ0SinTheta, "maximum Delta z0 sin theta (or 0 for no cut)");
+    declareProperty ("nMinPixelHits", m_nMinPixelHits, "minimum number of required Pixel hits (or -1 for no cut)");
+    declareProperty ("nMaxPixelHits", m_nMaxPixelHits, "minimum number of required Pixel hits (or -1 for no cut)");
+    declareProperty ("nMinSCTHits", m_nMinSCTHits, "minimum number of required SCT hits (or -1 for no cut)");
+    declareProperty ("nMaxSCTHits", m_nMaxSCTHits, "minimum number of required SCT hits (or -1 for no cut)");
+    declareProperty ("selectionDecoration", m_selectionDecoration, "the decoration for the asg selection");
+    declareProperty ("eventInfo", m_eventInfo, "the name of the EventInfo object to retrieve");
+    declareProperty ("primaryVertices", m_primaryVertices, "the name of the PrimaryVertex container to retrieve");
+  }
+
+
+
+  StatusCode AsgLeptonTrackSelectionAlg ::
+  initialize ()
+  {
+    if (m_selectionDecoration.empty())
+    {
+      ANA_MSG_ERROR ("no selection decoration name set");
+      return StatusCode::FAILURE;
+    }
+    ANA_CHECK (makeSelectionAccessor (m_selectionDecoration, m_selectionAccessor));
+
+    if (m_maxD0Significance < 0 || !std::isfinite (m_maxD0Significance))
+    {
+      ATH_MSG_ERROR ("invalid value of maxD0Significance: " << m_maxD0Significance);
+      return StatusCode::FAILURE;
+    }
+    if (m_maxDeltaZ0SinTheta < 0 || !std::isfinite (m_maxDeltaZ0SinTheta))
+    {
+      ATH_MSG_ERROR ("invalid value of maxDeltaZ0SinTheta: " << m_maxDeltaZ0SinTheta);
+      return StatusCode::FAILURE;
+    }
+
+    m_accept.addCut ("trackRetrieval", "whether the track retrieval failed");
+    if (m_maxD0Significance > 0)
+      m_accept.addCut ("maxD0Significance", "maximum D0 significance cut");
+    if (m_maxDeltaZ0SinTheta > 0)
+      m_accept.addCut ("maxDeltaZ0SinTheta", "maximum Delta z0 sin theta cut");
+    if (m_nMinPixelHits != -1 || m_nMaxPixelHits != -1)
+      m_accept.addCut ("numPixelHits", "Minimum and/or maxiumum Pixel hits");
+    if (m_nMinSCTHits != -1 || m_nMaxSCTHits != -1)
+      m_accept.addCut ("numSCTHits", "Minimum and/or maxiumum SCT hits");
+
+    m_systematicsList.addHandle (m_particlesHandle);
+    ANA_CHECK (m_systematicsList.initialize());
+    ANA_CHECK (m_preselection.initialize());
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode AsgLeptonTrackSelectionAlg ::
+  execute ()
+  {
+    const xAOD::EventInfo *eventInfo {nullptr};
+    if (m_maxD0Significance > 0)
+      ANA_CHECK (evtStore()->retrieve (eventInfo, m_eventInfo));
+
+    const xAOD::Vertex *primaryVertex {nullptr};
+    if (m_maxDeltaZ0SinTheta > 0)
+    {
+      const xAOD::VertexContainer *vertices {nullptr};
+      ANA_CHECK (evtStore()->retrieve (vertices, m_primaryVertices));
+      for (const xAOD::Vertex *vertex : *vertices)
+      {
+        if (vertex->vertexType() == xAOD::VxType::PriVtx)
+        {
+          // The default "PrimaryVertex" container is ordered in
+          // sum-pt, and the tracking group recommends to pick the one
+          // with the maximum sum-pt, so this will do the right thing.
+          // If the user needs a different primary vertex, he needs to
+          // provide a reordered primary vertex container and point
+          // this algorithm to it.  Currently there is no central
+          // algorithm to do that, so users will have to write their
+          // own (15 Aug 18).
+          if (primaryVertex == nullptr)
+          {
+            primaryVertex = vertex;
+            break;
+          }
+        }
+      }
+    }
+
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode {
+        xAOD::IParticleContainer *particles = nullptr;
+        ANA_CHECK (m_particlesHandle.getCopy (particles, sys));
+        for (xAOD::IParticle *particle : *particles)
+        {
+          m_accept.clear();
+
+          if (m_preselection.getBool (*particle))
+          {
+            std::size_t cutIndex {0};
+
+            const xAOD::TrackParticle *track {nullptr};
+            if (const xAOD::Muon *muon = dynamic_cast<xAOD::Muon*>(particle))
+              track = muon->primaryTrackParticle();
+            else if (const xAOD::Electron *electron = dynamic_cast<xAOD::Electron*>(particle))
+              track = electron->trackParticle();
+            else
+            {
+              ANA_MSG_ERROR ("failed to cast input to electron or muon");
+              return StatusCode::FAILURE;
+            }
+
+            m_accept.setCutResult (cutIndex ++, track != nullptr);
+            if (track != nullptr)
+            {
+              if (m_maxD0Significance > 0)
+              {
+                try
+                {
+                  const float d0sig = xAOD::TrackingHelpers::d0significance
+                    (track, eventInfo->beamPosSigmaX(), eventInfo->beamPosSigmaY(),
+                    eventInfo->beamPosSigmaXY());
+                  m_accept.setCutResult (cutIndex ++, fabs( d0sig ) < m_maxD0Significance);
+                } catch (const std::runtime_error &) {
+                  m_accept.setCutResult (cutIndex ++, false);
+                }
+              }
+              if (m_maxDeltaZ0SinTheta > 0)
+              {
+                const double vertex_z = primaryVertex ? primaryVertex->z() : 0;
+                const float deltaZ0SinTheta
+                  = (track->z0() + track->vz() - vertex_z) * sin (particle->p4().Theta());
+                m_accept.setCutResult (cutIndex ++, fabs (deltaZ0SinTheta) < m_maxDeltaZ0SinTheta);
+              }
+              if (m_nMinPixelHits != -1 || m_nMaxPixelHits != -1) {
+                uint8_t nPixelHits;
+                track->summaryValue(nPixelHits, xAOD::numberOfPixelHits);
+                bool accept = true;
+                if(m_nMinPixelHits != -1) {
+                  accept &= nPixelHits >= m_nMinPixelHits;
+                }
+                if(m_nMaxPixelHits != -1) {
+                  accept &= nPixelHits <= m_nMaxPixelHits;
+                }
+                m_accept.setCutResult (cutIndex++, accept);
+              }
+              if (m_nMinSCTHits != -1 || m_nMaxSCTHits != -1) {
+                uint8_t nSCTHits;
+                track->summaryValue(nSCTHits, xAOD::numberOfSCTHits);
+                bool accept = true;
+                if(m_nMinSCTHits != -1) {
+                  accept &= nSCTHits >= m_nMinSCTHits;
+                }
+                if(m_nMaxSCTHits != -1) {
+                  accept &= nSCTHits <= m_nMaxSCTHits;
+                }
+                m_accept.setCutResult (cutIndex++, accept);
+              }
+            }
+          }
+
+          m_selectionAccessor->setBits
+              (*particle, selectionFromAccept (m_accept));
+        }
+        return StatusCode::SUCCESS;
+      });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/AsgOriginalObjectLinkAlg.cxx b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/AsgOriginalObjectLinkAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..fa93b7f58c4d500225a7f5b97724da559143f904
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/AsgOriginalObjectLinkAlg.cxx
@@ -0,0 +1,65 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Tadej Novak
+
+
+//
+// includes
+//
+
+#include <AsgAnalysisAlgorithms/AsgOriginalObjectLinkAlg.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  AsgOriginalObjectLinkAlg ::
+  AsgOriginalObjectLinkAlg (const std::string& name, 
+                            ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+  {
+    declareProperty ("baseContainerName", m_baseContainerName, "base particle container name");
+  }
+
+
+
+  StatusCode AsgOriginalObjectLinkAlg ::
+  initialize ()
+  {
+    if (m_baseContainerName.empty())
+    {
+      ANA_MSG_ERROR ("Base particle container name should not be empty.");
+      return StatusCode::FAILURE;
+    }
+
+    m_systematicsList.addHandle (m_particleHandle);
+    ANA_CHECK (m_systematicsList.initialize());
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode AsgOriginalObjectLinkAlg ::
+  execute ()
+  {
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode {
+      xAOD::IParticleContainer *particles = nullptr;
+      ANA_CHECK (m_particleHandle.getCopy (particles, sys));
+
+      const xAOD::IParticleContainer *baseParticles = nullptr;
+      ANA_CHECK (evtStore()->retrieve(baseParticles, m_baseContainerName));
+
+      if (!xAOD::setOriginalObjectLink (*baseParticles, *particles))
+      {
+        ATH_MSG_ERROR ("Cannot set original object links for container named " << m_baseContainerName);
+        return StatusCode::FAILURE;
+      }
+
+      return StatusCode::SUCCESS;
+    });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/AsgPtEtaSelectionTool.cxx b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/AsgPtEtaSelectionTool.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..32f936c0242b08979bdc41bdde7575b6a1a496b2
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/AsgPtEtaSelectionTool.cxx
@@ -0,0 +1,174 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+//
+// includes
+//
+
+#include <AsgAnalysisAlgorithms/AsgPtEtaSelectionTool.h>
+
+#include <xAODEgamma/Egamma.h>
+#include <xAODBase/IParticle.h>
+#include <cmath>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  AsgPtEtaSelectionTool :: 
+  AsgPtEtaSelectionTool (const std::string& name)
+    : AsgTool (name)
+  {
+    declareProperty ("minPt", m_minPt, "minimum pt to require (or 0 for no pt cut)");
+    declareProperty ("maxPt", m_maxPt, "maximum pt to require (or 0 for no pt cut)");
+    declareProperty ("maxEta", m_maxEta, "maximum abs(eta) to allow (or 0 for no eta cut)");
+    declareProperty ("etaGapLow", m_etaGapLow, "low end of the eta gap");
+    declareProperty ("etaGapHigh", m_etaGapHigh, "high end of the eta gap (or 0 for no eta gap)");
+    declareProperty ("useClusterEta", m_useClusterEta, "whether to use the cluster eta (for electrons only)");
+    declareProperty ("printCastWarning", m_printCastWarning, "whether to print a warning/error when the cast fails");
+    declareProperty ("printClusterWarning", m_printClusterWarning, "whether to print a warning/error when the cluster is missing");
+  }
+
+
+
+  StatusCode AsgPtEtaSelectionTool ::
+  initialize ()
+  {
+    if (m_minPt < 0 || !std::isfinite (m_minPt))
+    {
+      ATH_MSG_ERROR ("invalid value of minPt: " << m_minPt);
+      return StatusCode::FAILURE;
+    }
+    if (m_maxPt < 0 || !std::isfinite (m_maxPt))
+    {
+      ATH_MSG_ERROR ("invalid value of m_maxPt: " << m_maxPt);
+      return StatusCode::FAILURE;
+    }
+    if (m_maxEta < 0 || !std::isfinite (m_maxEta))
+    {
+      ATH_MSG_ERROR ("invalid value of maxEta: " << m_maxEta);
+      return StatusCode::FAILURE;
+    }
+    if (m_etaGapLow < 0 || !std::isfinite (m_etaGapLow))
+    {
+      ATH_MSG_ERROR ("invalid value of etaGapLow: " << m_etaGapLow);
+      return StatusCode::FAILURE;
+    }
+    if (m_etaGapHigh < 0 || !std::isfinite (m_etaGapHigh))
+    {
+      ATH_MSG_ERROR ("invalid value of etaGapHigh: " << m_etaGapHigh);
+      return StatusCode::FAILURE;
+    }
+    if (m_etaGapHigh > 0 && m_etaGapLow >= m_etaGapHigh)
+    {
+      ATH_MSG_ERROR ("invalid eta gap: " << m_etaGapLow << " to " << m_etaGapHigh);
+      return StatusCode::FAILURE;
+    }
+    if (m_etaGapHigh > 0 && m_maxEta > 0 && m_etaGapHigh >= m_maxEta)
+    {
+      ATH_MSG_ERROR ("etaGapHigh=" << m_etaGapHigh << " >= maxEta=" << m_maxEta);
+      return StatusCode::FAILURE;
+    }
+
+    if (m_minPt > 0) {
+       ATH_MSG_DEBUG( "Performing pt >= " << m_minPt << " MeV selection" );
+       m_minPtCutIndex = m_accept.addCut ("minPt", "minimum pt cut");
+    }
+    if (m_maxPt > 0) {
+       ATH_MSG_DEBUG( "Performing pt < " << m_maxPt << " MeV selection" );
+       m_maxPtCutIndex = m_accept.addCut ("maxPt", "maximum pt cut");
+    }
+    if (m_useClusterEta) {
+       ATH_MSG_DEBUG( "Performing eta cut on the e/gamma cluster" );
+       m_egammaCastCutIndex = m_accept.addCut ("castEgamma", "cast to egamma");
+       m_egammaClusterCutIndex = m_accept.addCut ("caloCluster", "egamma object has cluster");
+    }
+    if (m_maxEta > 0) {
+       ATH_MSG_DEBUG( "Performing |eta| < " << m_maxEta << " selection" );
+       m_maxEtaCutIndex = m_accept.addCut ("maxEta", "maximum eta cut");
+    }
+    if (m_etaGapHigh > 0) {
+       ATH_MSG_DEBUG( "Performing !( " << m_etaGapLow << " < |eta| < "
+                      << m_etaGapHigh << " ) selection" );
+       m_etaGapCutIndex = m_accept.addCut ("etaGap", "eta gap cut");
+    }
+    m_shouldPrintCastWarning = m_printCastWarning;
+    m_shouldPrintClusterWarning = m_printClusterWarning;
+
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  const Root::TAccept& AsgPtEtaSelectionTool ::
+  getTAccept () const
+  {
+    return m_accept;
+  }
+
+
+
+  const Root::TAccept& AsgPtEtaSelectionTool ::
+  accept (const xAOD::IParticle *particle) const
+  {
+    m_accept.clear();
+
+    // Perform the tranverse momentum cuts.
+    if (m_minPtCutIndex >= 0) {
+       m_accept.setCutResult (m_minPtCutIndex, particle->pt() >= m_minPt);
+    }
+    if (m_maxPtCutIndex >= 0) {
+       m_accept.setCutResult (m_maxPtCutIndex, particle->pt() < m_maxPt);
+    }
+
+    // Perform the eta cut(s).
+    if (m_maxEtaCutIndex >= 0 || m_etaGapCutIndex >= 0)
+    {
+      float absEta = 0;
+
+      if (m_useClusterEta == true)
+      {
+        const xAOD::Egamma *egamma
+          = dynamic_cast<const xAOD::Egamma*>(particle);
+        if (egamma == nullptr)
+        {
+          if (m_shouldPrintCastWarning)
+            ANA_MSG_ERROR ("failed to cast input particle to electron");
+          m_shouldPrintCastWarning = false;
+          return m_accept;
+        }
+        m_accept.setCutResult (m_egammaCastCutIndex, true);
+        const xAOD::CaloCluster *const caloCluster {egamma->caloCluster()};
+        if (!caloCluster)
+        {
+          if (m_shouldPrintClusterWarning)
+            ANA_MSG_ERROR ("no calo-cluster associated with e-gamma object");
+          m_shouldPrintClusterWarning = false;
+          return m_accept;
+        }
+        m_accept.setCutResult (m_egammaClusterCutIndex, true);
+        absEta = std::abs (caloCluster->etaBE(2));
+      } else
+      {
+        absEta = std::abs (particle->eta());
+      }
+
+      if (m_maxEtaCutIndex >= 0) {
+        m_accept.setCutResult (m_maxEtaCutIndex, absEta <= m_maxEta);
+      }
+      if (m_etaGapCutIndex >= 0) {
+        m_accept.setCutResult (m_etaGapCutIndex, (absEta < m_etaGapLow ||
+                                                  absEta > m_etaGapHigh));
+      }
+    }
+
+    return m_accept;
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/AsgSelectionAlg.cxx b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/AsgSelectionAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..80951862530a149f949932585731d0577a9b2e46
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/AsgSelectionAlg.cxx
@@ -0,0 +1,89 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+//
+// includes
+//
+
+#include <AsgAnalysisAlgorithms/AsgSelectionAlg.h>
+
+#include <PATInterfaces/ISystematicsTool.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  AsgSelectionAlg ::
+  AsgSelectionAlg (const std::string& name, 
+                     ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+    , m_selectionTool ("", this)
+  {
+    declareProperty ("selectionTool", m_selectionTool, "the selection tool we apply");
+    declareProperty ("selectionDecoration", m_selectionDecoration, "the decoration for the asg selection");
+  }
+
+
+
+  StatusCode AsgSelectionAlg ::
+  initialize ()
+  {
+    if (m_selectionDecoration.empty())
+    {
+      ANA_MSG_ERROR ("no selection decoration name set");
+      return StatusCode::FAILURE;
+    }
+    ANA_CHECK (makeSelectionAccessor (m_selectionDecoration, m_selectionAccessor));
+
+    ANA_CHECK (m_selectionTool.retrieve());
+    m_systematicsTool = dynamic_cast<ISystematicsTool*>(&*m_selectionTool);
+    if (m_systematicsTool)
+      ANA_CHECK (m_systematicsList.addAffectingSystematics (m_systematicsTool->affectingSystematics()));
+      
+    m_systematicsList.addHandle (m_particlesHandle);
+    ANA_CHECK (m_systematicsList.initialize());
+    ANA_CHECK (m_preselection.initialize());
+
+    Root::TAccept blankAccept = m_selectionTool->getTAccept();
+    // Just in case this isn't initially set up as a failure clear it this one
+    // time. This only calls reset on the bitset
+    blankAccept.clear();
+    m_setOnFail = selectionFromAccept(blankAccept);
+
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode AsgSelectionAlg ::
+  execute ()
+  {
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode {
+        if (m_systematicsTool)
+          ANA_CHECK (m_systematicsTool->applySystematicVariation (sys));
+
+        xAOD::IParticleContainer *particles = nullptr;
+        ANA_CHECK (m_particlesHandle.getCopy (particles, sys));
+        for (xAOD::IParticle *particle : *particles)
+        {
+          if (m_preselection.getBool (*particle))
+          {
+            m_selectionAccessor->setBits
+              (*particle, selectionFromAccept (m_selectionTool->accept (particle)));
+          }
+          else
+          {
+            m_selectionAccessor->setBits(*particle, m_setOnFail);
+          }
+        }
+        return StatusCode::SUCCESS;
+      });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/AsgViewFromSelectionAlg.cxx b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/AsgViewFromSelectionAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..07f99decf502c5e6019aefc62d1f55cbb072048d
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/AsgViewFromSelectionAlg.cxx
@@ -0,0 +1,228 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+//
+// includes
+//
+
+#include <AsgAnalysisAlgorithms/AsgViewFromSelectionAlg.h>
+
+#include <CxxUtils/fpcompare.h>
+#include <xAODCore/AuxContainerBase.h>
+#include <xAODEgamma/PhotonContainer.h>
+#include <xAODEgamma/ElectronContainer.h>
+#include <xAODJet/JetContainer.h>
+#include <xAODMuon/MuonContainer.h>
+#include <xAODTau/TauJetContainer.h>
+#include <xAODTau/DiTauJetContainer.h>
+#include <xAODTracking/TrackParticleContainer.h>
+#include <xAODTruth/TruthParticleContainer.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  template<typename Type> StatusCode AsgViewFromSelectionAlg ::
+  executeTemplate (const CP::SystematicSet& sys)
+  {
+    const Type *input = nullptr;
+    ANA_CHECK (evtStore()->retrieve (input, m_inputHandle.getName (sys)));
+    auto viewCopy = std::make_unique<Type> (SG::VIEW_ELEMENTS);
+    for (const auto particle : *input)
+    {
+      bool keep = true;
+      for (const auto& accessor : m_accessors)
+      {
+        if ((accessor.first->getBits (*particle) | accessor.second) != selectionAccept())
+        {
+          keep = false;
+          break;
+        }
+      }
+      if (keep)
+      {
+        typename Type::value_type particleNC =
+          const_cast<typename Type::value_type>(particle);
+        viewCopy->push_back (particleNC);
+      }
+    }
+
+    if (m_sortPt)
+    {
+      std::sort (viewCopy->begin(), viewCopy->end(), [] (const xAOD::IParticle *a, const xAOD::IParticle *b) {return CxxUtils::fpcompare::greater (a->pt(), b->pt());});
+    }
+
+    // If anyone might be concerned about efficiency here, this will
+    // add/sort a couple more entries than needed only to remove them
+    // from the vector afterwards, so there is a slight efficiency
+    // loss.  However, this option is not expected to be used very
+    // often and the algorithm is still expected to run quickly, so I
+    // decided to keep the code above simpler and just do this as a
+    // separate step, instead of trying to optimize this by
+    // integrating it with the code above.
+    if (viewCopy->size() > m_sizeLimit)
+      viewCopy->resize (m_sizeLimit);
+
+    // In case we want to output a view copy, do that here.
+    if (!m_deepCopy)
+    {
+      ANA_CHECK (evtStore()->record (viewCopy.release(),
+                                     m_outputHandle.getName (sys)));
+      return StatusCode::SUCCESS;
+    }
+
+    // Apparently we want to make a deep copy. So set that one up.
+    auto deepCopy = std::make_unique<Type> ();
+    auto aux = std::make_unique<xAOD::AuxContainerBase> ();
+    deepCopy->setStore (aux.get());
+    deepCopy->reserve (viewCopy->size());
+    for (auto particle : *viewCopy)
+    {
+      typename Type::value_type pcopy = new typename Type::base_value_type();
+      deepCopy->push_back (pcopy);
+      *pcopy = *particle;
+    }
+
+    // Record the deep copy into the event store.
+    ANA_CHECK (evtStore()->record (deepCopy.release(),
+                                   m_outputHandle.getName (sys)));
+    ANA_CHECK (evtStore()->record (aux.release(),
+                                   m_outputHandle.getName (sys) + "Aux."));
+
+    return StatusCode::SUCCESS;
+  }
+
+
+
+
+  StatusCode AsgViewFromSelectionAlg ::
+  executeFindType (const CP::SystematicSet& sys)
+  {
+    if( m_allowMissing ) {
+      const std::string& name = m_inputHandle.getName( sys );
+      if( ! evtStore()->contains< xAOD::IParticleContainer >( name ) ) {
+        m_function = &AsgViewFromSelectionAlg::executeMissing;
+        return StatusCode::SUCCESS;
+      }
+    }
+
+    const xAOD::IParticleContainer *input = nullptr;
+    ANA_CHECK (m_inputHandle.retrieve (input, sys));
+
+    if (dynamic_cast<const xAOD::ElectronContainer*> (input))
+    {
+      m_function =
+        &AsgViewFromSelectionAlg::executeTemplate<xAOD::ElectronContainer>;
+    }
+    else if (dynamic_cast<const xAOD::PhotonContainer*> (input))
+    {
+      m_function =
+        &AsgViewFromSelectionAlg::executeTemplate<xAOD::PhotonContainer>;
+    }
+    else if (dynamic_cast<const xAOD::JetContainer*> (input))
+    {
+      m_function =
+        &AsgViewFromSelectionAlg::executeTemplate<xAOD::JetContainer>;
+    }
+    else if (dynamic_cast<const xAOD::MuonContainer*> (input)) {
+      m_function =
+        &AsgViewFromSelectionAlg::executeTemplate<xAOD::MuonContainer>;
+    }
+    else if (dynamic_cast<const xAOD::TauJetContainer*> (input))
+    {
+      m_function =
+        &AsgViewFromSelectionAlg::executeTemplate<xAOD::TauJetContainer>;
+    }
+    else if (dynamic_cast<const xAOD::DiTauJetContainer*> (input))
+    {
+      m_function =
+        &AsgViewFromSelectionAlg::executeTemplate<xAOD::DiTauJetContainer>;
+    }
+    else if (dynamic_cast<const xAOD::TrackParticleContainer*> (input))
+    {
+      m_function =
+        &AsgViewFromSelectionAlg::executeTemplate<xAOD::TrackParticleContainer>;
+    }
+    else if (dynamic_cast<const xAOD::TruthParticleContainer*> (input))
+    {
+      m_function =
+        &AsgViewFromSelectionAlg::executeTemplate<xAOD::TruthParticleContainer>;
+    }
+    else
+    {
+      ANA_MSG_ERROR ("unknown type contained in AsgViewFromSelectionAlg, please extend it");
+      return StatusCode::FAILURE;
+    }
+
+    return (this->*m_function) (sys);
+  }
+
+
+
+  StatusCode AsgViewFromSelectionAlg ::
+  executeMissing (const CP::SystematicSet&)
+  {
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  AsgViewFromSelectionAlg ::
+  AsgViewFromSelectionAlg (const std::string& name, 
+                           ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+  {
+    /// \todo this would probably better be an std::map, but this
+    /// isn't supported as a property type for AnaAlgorithm right now
+    declareProperty ("selection", m_selection, "the list of selection decorations");
+    declareProperty ("ignore", m_ignore, "the list of cuts to *ignore* for each selection");
+    declareProperty ("sortPt", m_sortPt, "whether to sort objects in pt");
+    declareProperty ("allowMissing", m_allowMissing,
+                     "Allow the input container to be missing");
+    declareProperty ("sizeLimit", m_sizeLimit, "the limit on the size of the output container");
+    declareProperty ("deepCopy", m_deepCopy, "perform a deep copy");
+  }
+
+
+
+  StatusCode AsgViewFromSelectionAlg ::
+  initialize ()
+  {
+    m_systematicsList.addHandle (m_inputHandle);
+    m_systematicsList.addHandle (m_outputHandle);
+    ANA_CHECK (m_systematicsList.initialize());
+
+    if (m_ignore.size() > m_selection.size())
+    {
+      ANA_MSG_ERROR ("ignore property can't have more properties than selection property");
+      return StatusCode::FAILURE;
+    }
+    for (std::size_t iter = 0, end = m_selection.size(); iter != end; ++ iter)
+    {
+      SelectionType ignore = 0;
+      if (iter < m_ignore.size())
+        ignore = m_ignore[iter];
+      std::unique_ptr<ISelectionAccessor> accessor;
+      ANA_CHECK (makeSelectionAccessor (m_selection[iter], accessor));
+      m_accessors.push_back (std::make_pair (std::move (accessor), ignore));
+    }
+
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode AsgViewFromSelectionAlg ::
+  execute ()
+  {
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode {
+        return (this->*m_function) (sys);});
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/AsgxAODNTupleMakerAlg.cxx b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/AsgxAODNTupleMakerAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..8ff21f3eeecd24a876c44798e1f557379efa5026
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/AsgxAODNTupleMakerAlg.cxx
@@ -0,0 +1,953 @@
+// Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+
+// Local include(s):
+#include "AsgAnalysisAlgorithms/AsgxAODNTupleMakerAlg.h"
+
+// EDM include(s):
+#include "AthContainersInterfaces/IAuxTypeVectorFactory.h"
+#include "AthContainers/AuxElement.h"
+#include "AthContainers/AuxVectorBase.h"
+#include "AthContainers/normalizedTypeinfoName.h"
+
+// Framework include(s):
+#include "SystematicsHandles/Helpers.h"
+
+// ROOT include(s):
+#include <TClass.h>
+#include <TTree.h>
+#include <TBranch.h>
+#include <TVirtualCollectionProxy.h>
+
+// System include(s):
+#include <regex>
+#include <algorithm>
+#include <functional>
+#include <sstream>
+
+namespace {
+
+#ifdef XAOD_STANDALONE
+
+   /// Get an xAOD container from the event store
+   ///
+   /// This is the "standalone implementation" of the function.
+   ///
+   /// @param key The name of the container in the event store
+   /// @param evtStore The value of evtStore() from the algorithm
+   /// @param allowMissing Set to @c true to print an error message in case
+   ///                     of a failure
+   /// @param msg Reference to the caller's @c MsgStream object
+   /// @return A pointer to the container if successful, @c nullptr if not
+   ///
+   const SG::AuxVectorBase* getVector( const std::string& key,
+                                       asg::SgTEvent& evtStore,
+                                       bool allowMissing,
+                                       MsgStream& msg ) {
+      if( allowMissing &&
+          ( ! evtStore.contains< const SG::AuxVectorBase >( key ) ) ) {
+         return nullptr;
+      }
+      const SG::AuxVectorBase* c = nullptr;
+      if( ! evtStore.retrieve( c, key ).isSuccess() ) {
+         msg << MSG::ERROR << "Couldn't retrieve container with key \"" << key
+             << "\"" << endmsg;
+         return nullptr;
+      }
+      return c;
+   }
+
+   /// Get a standalone xAOD object from the event store
+   ///
+   /// This is the "standalone implementation" of the function.
+   ///
+   /// @param key The name of the container in the event store
+   /// @param evtStore The value of evtStore() from the algorithm
+   /// @param allowMissing Set to @c true to print an error message in case
+   ///                     of a failure
+   /// @param msg Reference to the caller's @c MsgStream object
+   /// @return A pointer to the container if successful, @c nullptr if not
+   ///
+   const SG::AuxElement* getElement( const std::string& key,
+                                     asg::SgTEvent& evtStore,
+                                     bool allowMissing,
+                                     MsgStream& msg ) {
+      if( allowMissing &&
+          ( ! evtStore.contains< const SG::AuxElement >( key ) ) ) {
+         return nullptr;
+      }
+      const SG::AuxElement* e = nullptr;
+      if( ! evtStore.retrieve( e, key ).isSuccess() ) {
+         msg << MSG::ERROR << "Couldn't retrieve object with key \"" << key
+             << "\"" << endmsg;
+         return nullptr;
+      }
+      return e;
+   }
+
+#else
+
+   /// Predicate for finding a proxy with a given name
+   class ProxyWithName {
+   public:
+      /// Type of the predicate's argument
+      typedef const SG::DataProxy* argument_type;
+      /// Constructor with key/name
+      ProxyWithName( const std::string& name ) : m_name( name ) {}
+      /// Operator evaluating whether this is a good proxy or not
+      bool operator()( argument_type proxy ) const {
+         return ( proxy->name() == m_name );
+      }
+   private:
+      std::string m_name; ///< Name for the proxy to find
+   }; // class ProxyWithName
+
+   /// Get an xAOD container from the event store
+   ///
+   /// This is the "Athena implementation" of the function.
+   ///
+   /// @param key The name of the container in the event store
+   /// @param evtStore The value of evtStore() from the algorithm
+   /// @param allowMissing Set to @c true to print an error message in case
+   ///                     of a failure
+   /// @param msg Reference to the caller's @c MsgStream object
+   /// @return A pointer to the container if successful, @c nullptr if not
+   ///
+   const SG::AuxVectorBase* getVector( const std::string& key,
+                                       IProxyDict& evtStore,
+                                       bool allowMissing,
+                                       MsgStream& msg ) {
+
+      // Find all proxies with this key:
+      auto proxies = evtStore.proxies();
+      proxies.erase( std::remove_if( proxies.begin(), proxies.end(),
+                                     std::not1( ProxyWithName( key ) ) ),
+                     proxies.end() );
+      // Now iterate over them:
+      for( const SG::DataProxy* proxy : proxies ) {
+         // We need a non-const version of it... :-(
+         SG::DataProxy* proxy_nc = const_cast< SG::DataProxy* >( proxy );
+         // Try to get the right object out of it.
+         DataBucketBase* bucket =
+            dynamic_cast< DataBucketBase* >( proxy_nc->accessData() );
+         if( ! bucket ) {
+            // This is a big problem in the job. Return right away.
+            msg << MSG::ERROR
+                << "Couldn't access data object as a data bucket?!?" << endmsg;
+            return nullptr;
+         }
+         // Get the dictionary for the type:
+         TClass* cl = TClass::GetClass( bucket->tinfo() );
+         if( ! cl ) {
+            if( msg.level() <= MSG::VERBOSE ) {
+               msg << MSG::VERBOSE << "No dictionary found for: "
+                   << bucket->tinfo().name() << endmsg;
+            }
+            continue;
+         }
+         // Check whether the object inherits from AuxVectorBase:
+         if( ! cl->InheritsFrom( "SG::AuxVectorBase" ) ) {
+            if( msg.level() <= MSG::VERBOSE ) {
+               msg << MSG::VERBOSE << "Object \"" << key << "/" << cl->GetName()
+                   << "\" does not inherit from SG::AuxVectorBase" << endmsg;
+            }
+            continue;
+         }
+         // If all is well, just assume that the inheritance is direct/simple:
+         const SG::AuxVectorBase* result =
+            reinterpret_cast< const SG::AuxVectorBase* >( bucket->object() );
+         return result;
+      }
+
+      // Apparently we failed...
+      if( ! allowMissing ) {
+         msg << MSG::ERROR << "Couldn't retrieve object \"" << key
+             << "\" as SG::AuxVectorBase" << endmsg;
+      }
+      return nullptr;
+   }
+
+   /// Get a standalone xAOD object from the event store
+   ///
+   /// This is the "Athena implementation" of the function.
+   ///
+   /// @param key The name of the container in the event store
+   /// @param evtStore The value of evtStore() from the algorithm
+   /// @param allowMissing Set to @c true to print an error message in case
+   ///                     of a failure
+   /// @param msg Reference to the caller's @c MsgStream object
+   /// @return A pointer to the container if successful, @c nullptr if not
+   ///
+   const SG::AuxElement* getElement( const std::string& key,
+                                     IProxyDict& evtStore,
+                                     bool allowMissing,
+                                     MsgStream& msg ) {
+
+      // Find all proxies with this key:
+      auto proxies = evtStore.proxies();
+      proxies.erase( std::remove_if( proxies.begin(), proxies.end(),
+                                     std::not1( ProxyWithName( key ) ) ),
+                     proxies.end() );
+      // Now iterate over them:
+      for( const SG::DataProxy* proxy : proxies ) {
+         // We need a non-const version of it... :-(
+         SG::DataProxy* proxy_nc = const_cast< SG::DataProxy* >( proxy );
+         // Try to get the right object out of it.
+         DataBucketBase* bucket =
+            dynamic_cast< DataBucketBase* >( proxy_nc->accessData() );
+         if( ! bucket ) {
+            // This is a big problem in the job. Return right away.
+            msg << MSG::ERROR
+                << "Couldn't access data object as a data bucket?!?" << endmsg;
+            return nullptr;
+         }
+         // Get the dictionary for the type:
+         TClass* cl = TClass::GetClass( bucket->tinfo() );
+         if( ! cl ) {
+            if( msg.level() <= MSG::VERBOSE ) {
+               msg << MSG::VERBOSE << "No dictionary found for: "
+                   << bucket->tinfo().name() << endmsg;
+            }
+            continue;
+         }
+         // Check whether the object inherits from AuxVectorBase:
+         if( ! cl->InheritsFrom( "SG::AuxElement" ) ) {
+            if( msg.level() <= MSG::VERBOSE ) {
+               msg << MSG::VERBOSE << "Object \"" << key << "/" << cl->GetName()
+                   << "\" does not inherit from SG::AuxElement" << endmsg;
+            }
+            continue;
+         }
+         // If all is well, just assume that the inheritance is direct/simple:
+         return reinterpret_cast< const SG::AuxElement* >( bucket->object() );
+      }
+
+      // Apparently we failed...
+      if( ! allowMissing ) {
+         msg << MSG::ERROR << "Couldn't retrieve object \"" << key
+             << "\" as SG::AuxElement" << endmsg;
+      }
+      return nullptr;
+   }
+#endif // XAOD_STANDALONE
+
+   /// This function is used internally in the code when creating primitive
+   /// branches. I just took the code from xAODRootAccess, which itself too it
+   /// from SFrame... :-P
+   ///
+   /// @param typeidType The type name coming from typeid(...).name()
+   /// @param msg The caller's @c MsgStream object
+   /// @return The character describing this type for @c TTree::Branch
+   ///
+   char rootType( char typeidType, MsgStream& msg ) {
+
+      // Do the hard-coded translation:
+      switch( typeidType ) {
+
+         case 'c':
+            return 'B';
+            break;
+         case 'h':
+            return 'b';
+            break;
+         case 's':
+            return 'S';
+            break;
+         case 't':
+            return 's';
+            break;
+         case 'i':
+            return 'I';
+            break;
+         case 'j':
+            return 'i';
+            break;
+         case 'f':
+            return 'F';
+            break;
+         case 'd':
+            return 'D';
+            break;
+         case 'x':
+            return 'L';
+            break;
+         case 'y':
+         case 'm': // Not sure how platform-independent this one is...
+            return 'l';
+            break;
+         case 'b':
+            return 'O';
+            break;
+         default:
+            // If we didn't find this type:
+            msg << MSG::ERROR << "Received an unknown type: " << typeidType
+                << endmsg;
+            return '\0';
+            break;
+      }
+   }
+
+   /// Check if an aux item exists in the aux store
+   ///
+   /// @param key The name of the container in the event store
+   /// @return True if branch exists, false if not
+   ///
+   bool auxItemExists( const std::string& key ) {
+      // Get a pointer to the vector factory.
+      const SG::AuxTypeRegistry& reg = SG::AuxTypeRegistry::instance();
+
+      // Try to find the aux item
+      return reg.findAuxID( key ) != SG::null_auxid;
+   }
+} // private namespace
+
+namespace CP {
+
+   AsgxAODNTupleMakerAlg::AsgxAODNTupleMakerAlg( const std::string& name,
+                                                 ISvcLocator* svcLoc )
+   : EL::AnaAlgorithm( name, svcLoc ) {
+
+      // Declare the algorithm's properties.
+      declareProperty( "TreeName", m_treeName = "physics",
+                       "Name of the tree to write" );
+      declareProperty( "Branches", m_branches,
+                       "Branches to write to the output tree" );
+   }
+
+   StatusCode AsgxAODNTupleMakerAlg::initialize() {
+
+      // Check that at least one branch is configured.
+      if( m_branches.empty() ) {
+         ATH_MSG_ERROR( "No branches set up for writing" );
+         return StatusCode::FAILURE;
+      }
+
+      // Set up the systematics list.
+      ATH_CHECK( m_systematicsList.initialize() );
+
+      // Reset the initialisation flag:
+      m_isInitialized = false;
+
+      // Return gracefully.
+      return StatusCode::SUCCESS;
+   }
+
+   StatusCode AsgxAODNTupleMakerAlg::execute() {
+
+      // Initialise the processor objects on the first event.
+      if( ! m_isInitialized ) {
+         // Initialise the output tree.
+         m_tree = tree( m_treeName );
+         if( ! m_tree ) {
+            ATH_MSG_ERROR( "Could not find output tree \"" << m_treeName
+                           << "\"" );
+            return StatusCode::FAILURE;
+         }
+         // Call the setup function.
+         ATH_CHECK( setupTree() );
+         // The processor objects are now set up.
+         m_isInitialized = true;
+      }
+
+      // Process the standalone objects:
+      for( auto& element_itr : m_elements ) {
+         // Retrieve the object:
+         static const bool ALLOW_MISSING = false;
+         const SG::AuxElement* el = getElement( element_itr.first,
+                                                *( evtStore() ),
+                                                ALLOW_MISSING, msg() );
+         if( ! el ) {
+            ATH_MSG_ERROR( "Failed to retrieve object \"" << element_itr.first
+                           << "\"" );
+            return StatusCode::FAILURE;
+         }
+         // Process it:
+         ATH_CHECK( element_itr.second.process( *el ) );
+      }
+
+      // Process the container objects:
+      for( auto& container_itr : m_containers ) {
+         // Retrieve the container:
+         static const bool ALLOW_MISSING = false;
+         const SG::AuxVectorBase* vec = getVector( container_itr.first,
+                                                   *( evtStore() ),
+                                                   ALLOW_MISSING, msg() );
+         if( ! vec ) {
+            ATH_MSG_ERROR( "Failed to retrieve container \""
+                           << container_itr.first << "\"" );
+            return StatusCode::FAILURE;
+         }
+         // Process it.
+         ATH_CHECK( container_itr.second.process( *vec ) );
+      }
+
+      // Return gracefully.
+      return StatusCode::SUCCESS;
+   }
+
+   StatusCode AsgxAODNTupleMakerAlg::finalize() {
+
+      // Return gracefully.
+      return StatusCode::SUCCESS;
+   }
+
+   StatusCode AsgxAODNTupleMakerAlg::setupTree() {
+
+      // First process nominal
+      CP::SystematicSet nominal{};
+      for( const std::string& branchDecl : m_branches ) {
+         ATH_CHECK( setupBranch( branchDecl, nominal ) );
+      }
+
+      // Consider all systematics but skip the nominal one
+      for( const auto& sys : m_systematicsList.systematicsVector() ) {
+         // Nominal already processed
+         if( sys.empty() ) {
+            continue;
+         }
+
+         // Iterate over the branch specifications.
+         for( const std::string& branchDecl : m_branches ) {
+            ATH_CHECK( setupBranch( branchDecl, sys ) );
+         }
+      }
+
+      // Return gracefully.
+      return StatusCode::SUCCESS;
+   }
+
+   StatusCode AsgxAODNTupleMakerAlg::setupBranch( const std::string &branchDecl,
+                                                  const CP::SystematicSet &sys ) {
+
+      // The regular expression used to extract the needed info. The logic
+      // is supposed to be:
+      //
+      // (match[1]).(match[2])<any whitespace>-><any whitespace>(match[3])
+      //
+      // Like:
+      //    "Electrons.eta  -> el_eta"
+      //
+      // , where we would pick up "Electrons", "eta" and "el_eta" as the
+      // three words using this regexp.
+      static const std::regex
+         re( "\\s*([\\w%]+)\\.([\\w%]+)\\s*->\\s*([\\w%]+)" );
+
+      // Interpret this branch declaration.
+      std::smatch match;
+      if( ! std::regex_match( branchDecl, match, re ) ) {
+         ATH_MSG_ERROR( "Expression \"" << branchDecl
+                        << "\" doesn't match \"<object>.<variable> ->"
+                        " <branch>\"" );
+         return StatusCode::FAILURE;
+      }
+
+      // Check if we are running nominal
+      bool nominal = sys.empty();
+
+      // Event store key for the object under consideration.
+      const std::string key = makeSystematicsName( match[ 1 ], sys );
+      // Auxiliary variable name for the object under consideration.
+      const std::string auxName = makeSystematicsName( match[ 2 ],
+                                                         sys );
+      // Branch name for the variable.
+      const std::string brName = makeSystematicsName( match[ 3 ],
+                                                      sys );
+
+      // If the %SYS% pattern was not used in this setup, then stop
+      // on non-nominal systematic.
+      if( ! nominal &&
+          ( key == match[ 1 ] ) && ( auxName == match[ 2 ] ) &&
+          ( brName == match[ 3 ] ) ) {
+         return StatusCode::SUCCESS;
+      }
+
+      // Check that we use the %SYS% pattern reasonably in the names.
+      if( ( ( key == match[ 1 ] ) && ( auxName == match[ 2 ] ) &&
+            ( brName != match[ 3 ] ) ) ||
+            ( ( ( key != match[ 1 ] ) || ( auxName != match[ 2 ] ) ) &&
+            ( brName == match[ 3 ] ) ) ) {
+         ATH_MSG_ERROR( "The systematic variation pattern is used "
+                        "inconsistently in: \"" << branchDecl
+                        << "\"" );
+         return StatusCode::FAILURE;
+      }
+
+      // Flag keeping track whether any branch was set up for this rule.
+      bool branchCreated = false;
+
+      // Decide whether the specified key belongs to a container or
+      // a standalone object.
+      static const bool ALLOW_MISSING = true;
+      if( getVector( key, *( evtStore() ), ALLOW_MISSING,
+                     msg() ) ) {
+         bool created = false;
+         ATH_CHECK( m_containers[ key ].addBranch( *m_tree,
+                                                   auxName,
+                                                   brName,
+                                                   ALLOW_MISSING,
+                                                   created ) );
+         if( created ) {
+            ATH_MSG_DEBUG( "Writing branch \"" << brName
+                           << "\" from container/variable \"" << key
+                           << "." << auxName << "\"" );
+            branchCreated = true;
+         } else {
+            ATH_MSG_DEBUG( "Skipping branch \"" << brName
+                           << "\" from container/variable \"" << key
+                           << "." << auxName << "\"" );
+         }
+      } else if( getElement( key, *( evtStore() ),
+                              ALLOW_MISSING, msg() ) ) {
+         bool created = false;
+         ATH_CHECK( m_elements[ key ].addBranch( *m_tree,
+                                                   auxName,
+                                                   brName,
+                                                   ALLOW_MISSING,
+                                                   created ) );
+         if( created ) {
+            ATH_MSG_DEBUG( "Writing branch \"" << brName
+                           << "\" from object/variable \"" << key
+                           << "." << auxName << "\"" );
+            branchCreated = true;
+         } else {
+            ATH_MSG_DEBUG( "Skipping branch \"" << brName
+                        << "\" from object/variable \"" << key
+                        << "." << auxName << "\"" );
+         }
+      } else {
+         ATH_MSG_DEBUG( "Container \"" << key
+                        << "\" not readable for expression: \""
+                        << branchDecl << "\"" );
+      }
+
+      // Check if the rule was meaningful or not:
+      if( nominal && ! branchCreated && key == match[ 1 ] ) {
+         ATH_MSG_ERROR( "No branch was created for rule: \""
+                        << branchDecl << "\"" );
+         return StatusCode::FAILURE;
+      }
+
+      // Return gracefully.
+      return StatusCode::SUCCESS;
+   }
+
+   AsgxAODNTupleMakerAlg::ElementProcessor::ElementProcessor()
+   : asg::AsgMessaging( "CP::AsgxAODNTupleMakerAlg::ElementProcessor" ) {
+
+   }
+
+   StatusCode AsgxAODNTupleMakerAlg::ElementProcessor::
+   process( const SG::AuxElement& element ) {
+
+      // Process all branches.
+      for( BranchProcessor& p : m_branches ) {
+         ATH_CHECK( p.process( element, msg() ) );
+      }
+
+      // Return gracefully.
+      return StatusCode::SUCCESS;
+   }
+
+   StatusCode AsgxAODNTupleMakerAlg::ElementProcessor::
+   addBranch( TTree& tree, const std::string& auxName,
+              const std::string& branchName,
+              bool allowMissing,
+              bool &created ) {
+
+      /// Helper class for finding an already existing branch processor.
+      class BranchFinder {
+      public:
+         /// Type of the predicate's argument
+         typedef const BranchProcessor& argument_type;
+         /// Constructor with key/name
+         BranchFinder( const std::string& branchName ) : m_name( branchName ) {}
+         /// Operator evaluating whether this is the branch we're looking for
+         bool operator()( argument_type bp ) const {
+            return ( bp.m_branchName == m_name );
+         }
+      private:
+         std::string m_name; ///< Name of the branch
+      }; // class BranchFinder
+
+      // Check if the corresponding aux item exists
+      bool validAuxItem = auxItemExists( auxName );
+      if( ! validAuxItem ) {
+         if( allowMissing ) {
+            // Return gracefully.
+            ATH_MSG_DEBUG( "Aux item \"" << auxName
+                           << "\" not readable for branch \""
+                           << branchName << "\"" );
+            return StatusCode::SUCCESS;
+         } else {
+            // Return gracefully.
+            ATH_MSG_ERROR( "Aux item \"" << auxName
+                           << "\" not readable for branch \""
+                           << branchName << "\"" );
+            return StatusCode::FAILURE;
+         }
+      }
+
+      // Check whether this branch is already set up:
+      auto itr = std::find_if( m_branches.begin(), m_branches.end(),
+                               BranchFinder( branchName ) );
+      if( itr != m_branches.end() ) {
+         ATH_MSG_WARNING( "Duplicate setup received for branch: " << branchName );
+         return StatusCode::SUCCESS;
+      }
+
+      created = true;
+
+      // Set up the new branch.
+      m_branches.emplace_back();
+      ATH_CHECK( m_branches.back().setup( tree, auxName, branchName, msg() ) );
+
+      // Return gracefully.
+      return StatusCode::SUCCESS;
+   }
+
+   StatusCode
+   AsgxAODNTupleMakerAlg::ElementProcessor::BranchProcessor::
+   setup( TTree& tree, const std::string& auxName,
+          const std::string& branchName, MsgStream& msg ) {
+
+      // Remember the branch name.
+      m_branchName = branchName;
+
+      // Create the accessor.
+      m_acc.reset( new SG::AuxElement::TypelessConstAccessor( auxName ) );
+
+      // Get a pointer to the vector factory.
+      const SG::AuxTypeRegistry& reg = SG::AuxTypeRegistry::instance();
+      const std::type_info* ti = reg.getType( m_acc->auxid() );
+      if( ! ti ) {
+         msg << MSG::ERROR
+             << "No std::type_info available for auxiliary variable: "
+             << auxName << endmsg;
+         return StatusCode::FAILURE;
+      }
+      m_factory = reg.getFactory( *ti );
+      if( ! m_factory ) {
+         msg << MSG::ERROR << "No factory found for auxiliary variable: "
+             << auxName << endmsg;
+         return StatusCode::FAILURE;
+      }
+
+      // Create the data object.
+      m_data.reset( m_factory->create( 1, 1 ) );
+
+      // Pointer to the branch, to be created.
+      TBranch* br = nullptr;
+
+      // Decide whether we're dealing with a "primitive" or an "object" branch.
+      if( strlen( ti->name() ) == 1 ) {
+
+         // This is a "primitive" variable...
+
+         // Get the type identifier for it that ROOT will understand.
+         const char rType = rootType( ti->name()[ 0 ], msg );
+         if( rType == '\0' ) {
+            msg << MSG::ERROR << "Type not recognised for variable: "
+                << branchName << endmsg;
+            return StatusCode::FAILURE;
+         }
+
+         // Construct the type description.
+         std::ostringstream typeDesc;
+         typeDesc << branchName << "/" << rType;
+
+         // Create the primitive branch.
+         br = tree.Branch( branchName.c_str(), m_data->toPtr(),
+                           typeDesc.str().c_str() );
+
+      } else {
+
+         // This is an "object" variable...
+
+         // Get a proper type name for the variable.
+         const std::string typeName = SG::normalizedTypeinfoName( *ti );
+
+         // Access the dictionary for the type.
+         TClass* cl = TClass::GetClass( *ti );
+         if( ! cl ) {
+            cl = TClass::GetClass( typeName.c_str() );
+         }
+         if( ! cl ) {
+            msg << MSG::ERROR << "Couldn't find dictionary for type: "
+                << typeName << endmsg;
+            return StatusCode::FAILURE;
+         }
+         if( ! cl->GetStreamerInfo() ) {
+            msg << MSG::ERROR << "No streamer info available for type: "
+                << cl->GetName() << endmsg;
+            return StatusCode::FAILURE;
+         }
+
+         // Create the object branch.
+         m_dataPtr = m_data->toPtr();
+         br = tree.Branch( branchName.c_str(), cl->GetName(), &m_dataPtr );
+
+      }
+
+      // Check that the branch creation succeeded.
+      if( ! br ) {
+         msg << MSG::ERROR << "Failed to create branch: " << branchName
+             << endmsg;
+         return StatusCode::FAILURE;
+      }
+
+      // Return gracefully.
+      return StatusCode::SUCCESS;
+   }
+
+   StatusCode
+   AsgxAODNTupleMakerAlg::ElementProcessor::BranchProcessor::
+   process( const SG::AuxElement& element, MsgStream& msg ) {
+
+      // A security check.
+      if( ( ! m_acc ) || ( ! m_factory ) || ( ! m_data ) ) {
+         msg << MSG::FATAL << "Internal logic error detected" << endmsg;
+         return StatusCode::FAILURE;
+      }
+
+      // Get the data out of the xAOD object.
+      const void* auxData = ( *m_acc )( element );
+
+      // Copy it into the output variable.
+      m_factory->copy( m_data->toPtr(), 0, auxData, 0 );
+
+      // Return gracefully.
+      return StatusCode::SUCCESS;
+   }
+
+   AsgxAODNTupleMakerAlg::ContainerProcessor::ContainerProcessor()
+   : asg::AsgMessaging( "CP::AsgxAODNTupleMakerAlg::ContainerProcessor" ) {
+
+   }
+
+   StatusCode AsgxAODNTupleMakerAlg::ContainerProcessor::
+   process( const SG::AuxVectorBase& container ) {
+
+      // Get the collection proxy for the type if it's not available yet.
+      if( ! m_collProxy ) {
+
+         // Try to get the dictionary for the type.
+         TClass* cl = TClass::GetClass( typeid( container ) );
+         if( ! cl ) {
+            ATH_MSG_ERROR( "No dictionary found for container" );
+            return StatusCode::FAILURE;
+         }
+
+         // Get the collection proxy from the dictionary.
+         m_collProxy = cl->GetCollectionProxy();
+         if( ! m_collProxy ) {
+            ATH_MSG_ERROR( "No collection proxy provided by type: "
+                           << cl->GetName() );
+            return StatusCode::FAILURE;
+         }
+
+         // Get the offset that one needs to use to get from the element
+         // pointers to SG::AuxElement pointers.
+         static const TClass* auxElementClass =
+            TClass::GetClass( typeid( SG::AuxElement ) );
+         m_auxElementOffset =
+            m_collProxy->GetValueClass()->GetBaseClassOffset( auxElementClass );
+         if( m_auxElementOffset < 0 ) {
+            ATH_MSG_ERROR( "Vector element type \""
+                           << m_collProxy->GetValueClass()->GetName()
+                           << "\" doesn't seem to inherit from \""
+                           << auxElementClass->GetName() << "\"" );
+            return StatusCode::FAILURE;
+         }
+      }
+
+      // Set up the iteration over the elements of the container. In a really
+      // low level / ugly way...
+      void* cPtr =
+         const_cast< void* >( static_cast< const void* >( &container ) );
+      TVirtualCollectionProxy::TPushPop helper( m_collProxy, cPtr );
+      const UInt_t cSize = m_collProxy->Size();
+
+      // Tell all branch processors to resize their variables.
+      for( BranchProcessor& p : m_branches ) {
+         ATH_CHECK( p.resize( cSize, msg() ) );
+      }
+
+      // Now iterate over the container.
+      for( UInt_t i = 0; i < cSize; ++i ) {
+
+         // Get the element.
+         char* elPtr = static_cast< char* >( m_collProxy->At( i ) );
+         if( ! elPtr ) {
+            ATH_MSG_ERROR( "Failed to get element " << i << " from container" );
+            return StatusCode::FAILURE;
+         }
+         const SG::AuxElement* element =
+            reinterpret_cast< const SG::AuxElement* >( elPtr +
+                                                       m_auxElementOffset );
+
+         // Execute all branch processors on this element.
+         for( BranchProcessor& p : m_branches ) {
+            ATH_CHECK( p.process( *element, i, msg() ) );
+         }
+      }
+
+      // Return gracefully.
+      return StatusCode::SUCCESS;
+   }
+
+   StatusCode AsgxAODNTupleMakerAlg::ContainerProcessor::
+   addBranch( TTree& tree, const std::string& auxName,
+              const std::string& branchName,
+              bool allowMissing,
+              bool &created ) {
+
+      /// Helper class for finding an already existing branch processor.
+      class BranchFinder {
+      public:
+         /// Type of the predicate's argument
+         typedef const BranchProcessor& argument_type;
+         /// Constructor with key/name
+         BranchFinder( const std::string& branchName ) : m_name( branchName ) {}
+         /// Operator evaluating whether this is the branch we're looking for
+         bool operator()( argument_type bp ) const {
+            return ( bp.m_branchName == m_name );
+         }
+      private:
+         std::string m_name; ///< Name of the branch
+      }; // class BranchFinder
+
+      // Check if the corresponding aux item exists
+      bool validAuxItem = auxItemExists( auxName );
+      if( ! validAuxItem ) {
+         if( allowMissing ) {
+            // Return gracefully.
+            ATH_MSG_DEBUG( "Aux item \"" << auxName
+                           << "\" not readable for branch \""
+                           << branchName << "\"" );
+            return StatusCode::SUCCESS;
+         } else {
+            // Return gracefully.
+            ATH_MSG_ERROR( "Aux item \"" << auxName
+                           << "\" not readable for branch \""
+                           << branchName << "\"" );
+            return StatusCode::FAILURE;
+         }
+      }
+
+      // Check whether this branch is already set up:
+      auto itr = std::find_if( m_branches.begin(), m_branches.end(),
+                               BranchFinder( branchName ) );
+      if( itr != m_branches.end() ) {
+         ATH_MSG_WARNING( "Duplicate setup received for branch: " << branchName );
+         return StatusCode::SUCCESS;
+      }
+
+      created = true;
+
+      // Set up the new branch.
+      m_branches.emplace_back();
+      ATH_CHECK( m_branches.back().setup( tree, auxName, branchName, msg() ) );
+
+      // Return gracefully.
+      return StatusCode::SUCCESS;
+   }
+
+   StatusCode AsgxAODNTupleMakerAlg::ContainerProcessor::BranchProcessor::
+   setup( TTree& tree, const std::string& auxName,
+          const std::string& branchName, MsgStream& msg ) {
+
+      // Remember the branch name.
+      m_branchName = branchName;
+
+      // Create the accessor.
+      m_acc.reset( new SG::AuxElement::TypelessConstAccessor( auxName ) );
+
+      // Get a pointer to the vector factory.
+      const SG::AuxTypeRegistry& reg = SG::AuxTypeRegistry::instance();
+      const std::type_info* ti    = reg.getType( m_acc->auxid() );
+      const std::type_info* vecTi = reg.getVecType( m_acc->auxid() );
+      if( ( ! ti ) || ( ! vecTi ) ) {
+         msg << MSG::ERROR
+             << "No std::type_info available for auxiliary variable: "
+             << auxName << endmsg;
+         return StatusCode::FAILURE;
+      }
+      m_factory = reg.getFactory( *ti );
+      if( ! m_factory ) {
+         msg << MSG::ERROR << "No factory found for auxiliary variable: "
+             << auxName << endmsg;
+         return StatusCode::FAILURE;
+      }
+
+      // Create the data object.
+      m_data.reset( m_factory->create( 0, 0 ) );
+
+      // Get a proper type name for the variable.
+      const std::string typeName = SG::normalizedTypeinfoName( *vecTi );
+
+      // Access the dictionary for the type.
+      TClass* cl = TClass::GetClass( *vecTi );
+      if( ! cl ) {
+         cl = TClass::GetClass( typeName.c_str() );
+      }
+      if( ! cl ) {
+         msg << MSG::ERROR << "Couldn't find dictionary for type: "
+             << typeName << endmsg;
+         return StatusCode::FAILURE;
+      }
+      if( ! cl->GetStreamerInfo() ) {
+         msg << MSG::ERROR << "No streamer info available for type: "
+             << cl->GetName() << endmsg;
+         return StatusCode::FAILURE;
+      }
+
+      // Create the branch.
+      m_dataPtr = m_data->toVector();
+      TBranch* br = tree.Branch( branchName.c_str(), cl->GetName(),
+                                 &m_dataPtr );
+      if( ! br ) {
+         msg << MSG::ERROR << "Failed to create branch: " << branchName
+             << endmsg;
+         return StatusCode::FAILURE;
+      }
+
+      // Return gracefully.
+      return StatusCode::SUCCESS;
+   }
+
+   StatusCode AsgxAODNTupleMakerAlg::ContainerProcessor::BranchProcessor::
+   resize( size_t size, MsgStream& msg ) {
+
+      // A security check.
+      if( ! m_data ) {
+         msg << MSG::FATAL << "Internal logic error detected" << endmsg;
+         return StatusCode::FAILURE;
+      }
+
+      // Do the deed.
+      m_data->resize( 0 );
+      m_data->resize( size );
+
+      // Return gracefully.
+      return StatusCode::SUCCESS;
+   }
+
+   StatusCode AsgxAODNTupleMakerAlg::ContainerProcessor::BranchProcessor::
+   process( const SG::AuxElement& element, size_t index, MsgStream& msg ) {
+
+      // A security check.
+      if( ( ! m_acc ) || ( ! m_factory ) || ( ! m_data ) ) {
+         msg << MSG::FATAL << "Internal logic error detected" << endmsg;
+         return StatusCode::FAILURE;
+      }
+
+      // Get the data out of the xAOD object.
+      const void* auxData = ( *m_acc )( element );
+
+      // Copy it into the output variable.
+      m_factory->copy( m_data->toPtr(), index, auxData, 0 );
+
+      // Return gracefully.
+      return StatusCode::SUCCESS;
+   }
+
+} // namespace CP
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/EventFlagSelectorAlg.cxx b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/EventFlagSelectorAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..c2a3b94c59205c0ccbf270b4a79c9db155a21cab
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/EventFlagSelectorAlg.cxx
@@ -0,0 +1,75 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Tadej Novak
+
+#include "AnaAlgorithm/FilterReporter.h"
+#include <AsgAnalysisAlgorithms/EventFlagSelectionAlg.h>
+#include <xAODEventInfo/EventInfo.h>
+
+CP::EventFlagSelectionAlg::EventFlagSelectionAlg(const std::string &name,
+                                             ISvcLocator *svcLoc)
+  : EL::AnaAlgorithm(name, svcLoc)
+{
+  declareProperty ("selectionFlags", m_selFlags, "list of flags to use as selection criteria");
+  declareProperty ("invertFlags",    m_invertFlags, "toggles for inverting the selection (index-parallel to selectionFlags)");
+}
+
+StatusCode CP::EventFlagSelectionAlg::initialize()
+{
+  if (m_invertFlags.size() != m_selFlags.size() && !m_invertFlags.empty()) {
+    ATH_MSG_ERROR("Property invertFlags has different size to selectionFlags. Please check your configuration");
+    return StatusCode::FAILURE;
+  }
+
+  for (size_t index = 0; index < m_selFlags.size(); ++index) {
+    const std::string& thisflag = m_selFlags[index];
+    if (thisflag.empty()) {
+      ATH_MSG_ERROR("Empty string passed as selection flag!");
+      return StatusCode::FAILURE;
+    } else {
+      // Extend m_invertFlags until the size matches m_selectionFlags
+      // Only done in the case that m_invert was empty
+      if (m_invertFlags.size() < index + 1) { m_invertFlags.push_back(false); }
+
+      std::unique_ptr<ISelectionAccessor> accessor;
+      ANA_CHECK (makeSelectionAccessor (m_selFlags[index], accessor));
+      m_accessors.push_back(std::move(accessor));
+    }
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode CP::EventFlagSelectionAlg::execute()
+{
+  EL::FilterReporter filter (m_filterParams, false);
+
+  const xAOD::EventInfo *evtInfo = 0;
+  ANA_CHECK(evtStore()->retrieve(evtInfo, "EventInfo"));
+
+  for (size_t index = 0; index < m_selFlags.size(); ++index) {
+    // Test against the opposite of the invert value
+    bool testval = !m_invertFlags[index];
+    ATH_MSG_VERBOSE("Now testing flag \"" << m_selFlags[index] << "\" requiring value " << testval);
+
+    if (m_accessors[index]->getBool(*evtInfo) != testval) {
+      ATH_MSG_VERBOSE("Event failed.");
+      filter.setPassed(false);
+      return StatusCode::SUCCESS;
+    }
+  }
+
+  ATH_MSG_VERBOSE("Event passed all flags.");
+  filter.setPassed(true);
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode CP::EventFlagSelectionAlg::finalize()
+{
+  ANA_CHECK (m_filterParams.finalize());
+
+  return StatusCode::SUCCESS;
+}
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/EventSelectionByObjectFlagAlg.cxx b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/EventSelectionByObjectFlagAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..22ff1923a18952297f4e8b94b39b9a90a4e98086
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/EventSelectionByObjectFlagAlg.cxx
@@ -0,0 +1,68 @@
+/*
+  Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Miha Muskinja
+
+//
+// includes
+//
+
+#include <AsgAnalysisAlgorithms/EventSelectionByObjectFlagAlg.h>
+#include <SystematicsHandles/SysFilterReporter.h>
+#include <SystematicsHandles/SysFilterReporterCombiner.h>
+
+//
+// method implementations
+//
+
+namespace CP {
+
+EventSelectionByObjectFlagAlg ::EventSelectionByObjectFlagAlg(
+    const std::string &name, ISvcLocator *pSvcLocator)
+    : AnaAlgorithm(name, pSvcLocator) {}
+
+StatusCode EventSelectionByObjectFlagAlg ::initialize() {
+
+    m_systematicsList.addHandle(m_particleHandle);
+    ANA_CHECK(m_systematicsList.initialize());
+    ANA_CHECK(m_preselection.initialize());
+    ANA_CHECK(m_veto.initialize());
+    ANA_CHECK(m_filterParams.initialize());
+
+    return StatusCode::SUCCESS;
+}
+
+StatusCode EventSelectionByObjectFlagAlg ::execute() {
+
+    SysFilterReporterCombiner filterCombiner (m_filterParams, m_systematicsList, true);
+
+    // loop over systematics
+    return m_systematicsList.foreach ([&](const CP::SystematicSet &sys) -> StatusCode {
+        SysFilterReporter filter (filterCombiner, sys);
+
+        // particle container
+        const xAOD::IParticleContainer *particles = nullptr;
+        ANA_CHECK(m_particleHandle.retrieve(particles, sys));
+
+        // reject events with any particle passing the input selection
+        for (const xAOD::IParticle *particle : *particles) {
+            if (m_preselection.getBool(*particle)) {
+                if (m_veto.getBool(*particle)) {
+                    ATH_MSG_VERBOSE("Event failed.");
+                    filter.setPassed (false);
+                    break;
+                }
+            }
+        }
+
+        return StatusCode::SUCCESS;
+    });
+}
+
+StatusCode EventSelectionByObjectFlagAlg ::finalize() {
+    ANA_CHECK (m_filterParams.finalize());
+    return StatusCode::SUCCESS;
+}
+
+} // namespace CP
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/IsolationCloseByCorrectionAlg.cxx b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/IsolationCloseByCorrectionAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..70335657f4b1e656cbd36c8c3d27c2575d12005c
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/IsolationCloseByCorrectionAlg.cxx
@@ -0,0 +1,66 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <AsgAnalysisAlgorithms/IsolationCloseByCorrectionAlg.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  IsolationCloseByCorrectionAlg ::
+  IsolationCloseByCorrectionAlg (const std::string& name, 
+                        ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+  {
+    declareProperty ("isolationCorrectionTool", m_isolationCorrectionTool);
+    declareProperty ("topoEtConeModel", m_topoEtConeModel);
+  }
+
+
+
+  StatusCode IsolationCloseByCorrectionAlg ::
+  initialize ()
+  {
+    ANA_CHECK (m_isolationCorrectionTool.retrieve());
+    if (m_electronsHandle) m_systematicsList.addHandle (m_electronsHandle);
+    if (m_muonsHandle) m_systematicsList.addHandle (m_muonsHandle);
+    if (m_photonsHandle) m_systematicsList.addHandle (m_photonsHandle);
+    ANA_CHECK (m_systematicsList.initialize());
+    ANA_CHECK (m_outOfValidity.initialize());
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode IsolationCloseByCorrectionAlg ::
+  execute ()
+  {
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode {
+        xAOD::ElectronContainer *electrons {nullptr};
+        if (m_electronsHandle)
+          ANA_CHECK (m_electronsHandle.getCopy (electrons, sys));
+        xAOD::MuonContainer *muons {nullptr};
+        if (m_muonsHandle)
+          ANA_CHECK (m_muonsHandle.getCopy (muons, sys));
+        xAOD::PhotonContainer *photons {nullptr};
+        if (m_photonsHandle)
+          ANA_CHECK (m_photonsHandle.getCopy (photons, sys));
+
+        ANA_CHECK_CORRECTION_EVENT
+          (m_outOfValidity, m_isolationCorrectionTool->getCloseByIsoCorrection
+           (electrons, muons, photons, m_topoEtConeModel));
+
+        return StatusCode::SUCCESS;
+      });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/KinematicHistAlg.cxx b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/KinematicHistAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..ab71365c73ad04500a6f0125388b29a7167da4c5
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/KinematicHistAlg.cxx
@@ -0,0 +1,110 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <AsgAnalysisAlgorithms/KinematicHistAlg.h>
+
+#include <RootCoreUtils/StringUtil.h>
+#include <TH1.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  KinematicHistAlg ::
+  KinematicHistAlg (const std::string& name, 
+                          ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+  {
+    declareProperty ("histPattern", m_histPattern, "the pattern for histogram names");
+  }
+
+
+
+  StatusCode KinematicHistAlg ::
+  initialize ()
+  {
+    m_systematicsList.addHandle (m_inputHandle);
+    ANA_CHECK (m_systematicsList.initialize());
+    ANA_CHECK (m_preselection.initialize());
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode KinematicHistAlg ::
+  execute ()
+  {
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode {
+        const xAOD::IParticleContainer *input = nullptr;
+        ANA_CHECK (m_inputHandle.retrieve (input, sys));
+
+        auto histIter = m_hist.find (sys);
+        if (histIter == m_hist.end())
+        {
+          std::string name;
+          HistGroup group;
+
+          name = RCU::substitute (m_histPattern, "%VAR%", "multiplicity");
+          name = makeSystematicsName (name, sys);
+          ANA_CHECK (book (TH1F (name.c_str(), "multiplicity", 20, 0, 20)));
+          group.multiplicity = hist (name);
+
+          m_hist.insert (std::make_pair (sys, group));
+          histIter = m_hist.find (sys);
+          assert (histIter != m_hist.end());
+        }
+
+        std::size_t count = 0;
+        for (std::size_t iter = 0; iter != input->size(); ++ iter)
+        {
+          const xAOD::IParticle *particle = (*input)[iter];
+          if (m_preselection.getBool (*particle))
+          {
+            while (histIter->second.perObject.size() <= count)
+            {
+              std::string name;
+              HistSubgroup group;
+
+              name = RCU::substitute (m_histPattern, "%VAR%", "pt" + std::to_string(histIter->second.perObject.size()));
+              name = makeSystematicsName (name, sys);
+              ANA_CHECK (book (TH1F (name.c_str(), "pt", 20, 0, 200e3)));
+              group.pt = hist (name);
+
+              name = RCU::substitute (m_histPattern, "%VAR%", "eta" + std::to_string(histIter->second.perObject.size()));
+              name = makeSystematicsName (name, sys);
+              ANA_CHECK (book (TH1F (name.c_str(), "eta", 20, -5, 5)));
+              group.eta = hist (name);
+
+              name = RCU::substitute (m_histPattern, "%VAR%", "phi" + std::to_string(histIter->second.perObject.size()));
+              name = makeSystematicsName (name, sys);
+              ANA_CHECK (book (TH1F (name.c_str(), "phi", 20, -M_PI, M_PI)));
+              group.phi = hist (name);
+
+              histIter->second.perObject.push_back (group);
+            }
+
+            HistSubgroup& group = histIter->second.perObject[count];
+
+            group.pt->Fill (particle->pt());
+            group.eta->Fill (particle->eta());
+            group.phi->Fill (particle->phi());
+            count += 1;
+          }
+        }
+
+        histIter->second.multiplicity->Fill (count);
+
+        return StatusCode::SUCCESS;
+      });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/ObjectCutFlowHistAlg.cxx b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/ObjectCutFlowHistAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..ff58bd5a78a3b99531c6db0ad20650459665f350
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/ObjectCutFlowHistAlg.cxx
@@ -0,0 +1,136 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <AsgAnalysisAlgorithms/ObjectCutFlowHistAlg.h>
+
+#include <RootCoreUtils/StringUtil.h>
+#include <TH1.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  ObjectCutFlowHistAlg ::
+  ObjectCutFlowHistAlg (const std::string& name, 
+                          ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+  {
+    declareProperty ("histPattern", m_histPattern, "the pattern for histogram names");
+
+    declareProperty ("selection", m_selection, "the list of selection decorations");
+    declareProperty ("selectionNCuts", m_selectionNCuts, "the number of cuts for each selection decoration");
+  }
+
+
+
+  StatusCode ObjectCutFlowHistAlg ::
+  initialize ()
+  {
+    m_systematicsList.addHandle (m_inputHandle);
+    ANA_CHECK (m_systematicsList.initialize());
+    ANA_CHECK (m_preselection.initialize());
+
+    if (m_selectionNCuts.size() != m_selection.size())
+    {
+      ANA_MSG_ERROR ("selection and selectionNCuts properties need to be the same size");
+      return StatusCode::FAILURE;
+    }
+
+    // Total label
+    m_labels.push_back ("total");
+
+    for (std::size_t iter = 0, end = m_selection.size(); iter != end; ++ iter)
+    {
+      const unsigned ncuts = m_selectionNCuts[iter];
+      if (ncuts == 0)
+      {
+        ANA_MSG_ERROR ("all entries of selectionNCuts need to be greater than 0");
+        return StatusCode::FAILURE;
+      }
+      if (ncuts > 8 * sizeof (SelectionType))
+      {
+        ANA_MSG_ERROR ("entries in selectionNCuts need to be less or equal to " << (8 * sizeof (SelectionType)));
+        return StatusCode::FAILURE;
+      }
+      std::unique_ptr<ISelectionAccessor> accessor;
+      ANA_CHECK (makeSelectionAccessor (m_selection[iter], accessor));
+      m_accessors.push_back (std::make_pair (std::move (accessor), ncuts));
+      for (unsigned i = 1; i <= ncuts; i++)
+      {
+        m_labels.push_back (m_accessors.back().first->label() + std::to_string(i));
+      }
+      m_allCutsNum += ncuts;
+    }
+
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode ObjectCutFlowHistAlg ::
+  execute ()
+  {
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode {
+        const xAOD::IParticleContainer *input = nullptr;
+        ANA_CHECK (m_inputHandle.retrieve (input, sys));
+
+        auto histIter = m_hist.find (sys);
+        if (histIter == m_hist.end())
+        {
+          const std::string name
+            = makeSystematicsName (m_histPattern, sys);
+
+          ANA_CHECK (book (TH1F (name.c_str(), "object cut flow", m_allCutsNum+1, 0, m_allCutsNum+1)));
+
+          m_hist.insert (std::make_pair (sys, hist (name)));
+          histIter = m_hist.find (sys);
+          assert (histIter != m_hist.end());
+
+          for (unsigned i = 0; i < m_allCutsNum+1; i++)
+          {
+            histIter->second->GetXaxis()->SetBinLabel(i + 1, m_labels[i].c_str());
+          }
+        }
+
+        for (const xAOD::IParticle *particle : *input)
+        {
+          if (m_preselection.getBool (*particle))
+          {
+            bool keep = true;
+            unsigned cutIndex = 1;
+            histIter->second->Fill (0);
+            for (const auto& accessor : m_accessors)
+            {
+              const auto selection = accessor.first->getBits (*particle);
+              for (unsigned index = 0, end = accessor.second;
+                   index != end; ++ index, ++ cutIndex)
+              {
+                if (selection & (1 << index))
+                {
+                  histIter->second->Fill (cutIndex);
+                } else
+                {
+                  keep = false;
+                  break;
+                }
+              }
+              if (!keep)
+                break;
+            }
+          }
+        }
+
+        return StatusCode::SUCCESS;
+      });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/OverlapRemovalAlg.cxx b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/OverlapRemovalAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..02d98dedbdcb07034df655447e8d0ee10b1197df
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/OverlapRemovalAlg.cxx
@@ -0,0 +1,75 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <AsgAnalysisAlgorithms/OverlapRemovalAlg.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  OverlapRemovalAlg ::
+  OverlapRemovalAlg (const std::string& name, 
+                        ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+  {
+    declareProperty ("overlapTool", m_overlapTool);
+  }
+
+
+
+  StatusCode OverlapRemovalAlg ::
+  initialize ()
+  {
+    ANA_CHECK (m_overlapTool.retrieve());
+    if (m_electronsHandle) m_systematicsList.addHandle (m_electronsHandle);
+    if (m_muonsHandle) m_systematicsList.addHandle (m_muonsHandle);
+    if (m_jetsHandle) m_systematicsList.addHandle (m_jetsHandle);
+    if (m_tausHandle) m_systematicsList.addHandle (m_tausHandle);
+    if (m_photonsHandle) m_systematicsList.addHandle (m_photonsHandle);
+    if (m_fatJetsHandle) m_systematicsList.addHandle (m_fatJetsHandle);
+    ANA_CHECK (m_systematicsList.initialize());
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode OverlapRemovalAlg ::
+  execute ()
+  {
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode {
+        xAOD::ElectronContainer *electrons {nullptr};
+        if (m_electronsHandle)
+          ANA_CHECK (m_electronsHandle.getCopy (electrons, sys));
+        xAOD::MuonContainer *muons {nullptr};
+        if (m_muonsHandle)
+          ANA_CHECK (m_muonsHandle.getCopy (muons, sys));
+        xAOD::JetContainer *jets {nullptr};
+        if (m_jetsHandle)
+          ANA_CHECK (m_jetsHandle.getCopy (jets, sys));
+        xAOD::TauJetContainer *taus {nullptr};
+        if (m_tausHandle)
+          ANA_CHECK (m_tausHandle.getCopy (taus, sys));
+        xAOD::PhotonContainer *photons {nullptr};
+        if (m_photonsHandle)
+          ANA_CHECK (m_photonsHandle.getCopy (photons, sys));
+        xAOD::JetContainer *fatJets {nullptr};
+        if (m_fatJetsHandle)
+          ANA_CHECK (m_fatJetsHandle.getCopy (fatJets, sys));
+
+        ATH_CHECK (m_overlapTool->removeOverlaps (electrons, muons, jets, taus,
+                                                  photons, fatJets));
+
+        return StatusCode::SUCCESS;
+      });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/PMGTruthWeightAlg.cxx b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/PMGTruthWeightAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..a3ee593b8d970b947eb06ba477386506e215bec6
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/PMGTruthWeightAlg.cxx
@@ -0,0 +1,65 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Tadej Novak
+
+
+//
+// includes
+//
+
+#include <AsgAnalysisAlgorithms/PMGTruthWeightAlg.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  PMGTruthWeightAlg ::
+  PMGTruthWeightAlg (const std::string& name, 
+                     ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+    , m_truthWeightTool ("PMGTools::PMGTruthWeightTool", this)
+  {
+    declareProperty ("truthWeightTool", m_truthWeightTool, "the truth weight tool");
+  }
+
+
+
+  StatusCode PMGTruthWeightAlg ::
+  initialize ()
+  {
+    if (m_decoration.empty())
+    {
+      ANA_MSG_ERROR ("no decoration name set");
+      return StatusCode::FAILURE;
+    }
+
+    ANA_CHECK (m_truthWeightTool.retrieve());
+    ANA_CHECK (m_systematicsList.addAffectingSystematics (m_truthWeightTool->affectingSystematics()));
+    m_systematicsList.addHandle (m_eventInfoHandle);
+    ANA_CHECK (m_systematicsList.initialize());
+
+    return StatusCode::SUCCESS;
+  }
+
+
+  StatusCode PMGTruthWeightAlg ::
+  execute ()
+  {
+    ANA_CHECK (m_decoration.preExecute (m_systematicsList));
+
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode
+    {
+      ANA_CHECK (m_truthWeightTool->applySystematicVariation (sys));
+      xAOD::EventInfo *eventInfo = nullptr;
+      ANA_CHECK (m_eventInfoHandle.getCopy (eventInfo, sys));
+
+      m_decoration.set (*eventInfo, m_truthWeightTool->getSysWeight (), sys);
+
+      return StatusCode::SUCCESS;
+    });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/PileupReweightingAlg.cxx b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/PileupReweightingAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..76432c7b31c9dd8c3e3d8590f1e234d995cdf9da
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/PileupReweightingAlg.cxx
@@ -0,0 +1,119 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <AsgAnalysisAlgorithms/PileupReweightingAlg.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  PileupReweightingAlg ::
+  PileupReweightingAlg (const std::string& name, 
+                        ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+    , m_pileupReweightingTool ("CP::PileupReweightingTool", this)
+  {
+    declareProperty ("pileupReweightingTool", m_pileupReweightingTool, "the pileup reweighting tool we apply");
+    declareProperty ("correctedScaledAverageMuDecoration", m_correctedScaledAverageMuDecoration, "the decoration for the corrected and scaled average interactions per crossing");
+    declareProperty ("correctedActualMuDecoration", m_correctedActualMuDecoration, "the decoration for the corrected actual interactions per crossing");
+    declareProperty ("correctedScaledActualMuDecoration", m_correctedScaledActualMuDecoration, "the decoration for the corrected and scaled actual interactions per crossing");
+  }
+
+
+
+  StatusCode PileupReweightingAlg ::
+  initialize ()
+  {
+    if (!m_correctedScaledAverageMuDecoration.empty())
+    {
+      m_correctedScaledAverageMuAccessor = std::make_unique<SG::AuxElement::Accessor<float> > (m_correctedScaledAverageMuDecoration);
+    }
+    if (!m_correctedActualMuDecoration.empty())
+    {
+      m_correctedActualMuAccessor = std::make_unique<SG::AuxElement::Accessor<float> > (m_correctedActualMuDecoration);
+    }
+    if (!m_correctedScaledActualMuDecoration.empty())
+    {
+      m_correctedScaledActualMuAccessor = std::make_unique<SG::AuxElement::Accessor<float> > (m_correctedScaledActualMuDecoration);
+    }
+
+    ANA_CHECK (m_pileupReweightingTool.retrieve());
+    m_systematicsList.addHandle (m_eventInfoHandle);
+    ANA_CHECK (m_systematicsList.addAffectingSystematics (m_pileupReweightingTool->affectingSystematics()));
+    ANA_CHECK (m_systematicsList.initialize());
+    ANA_CHECK (m_outOfValidity.initialize());
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode PileupReweightingAlg ::
+  execute ()
+  {
+    unsigned int nominalRandomRunNumber{};
+
+    ANA_CHECK (m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode {
+        ANA_CHECK (m_pileupReweightingTool->applySystematicVariation (sys));
+        xAOD::EventInfo *eventInfo = nullptr;
+        ANA_CHECK (m_eventInfoHandle.getCopy (eventInfo, sys));
+
+        ATH_CHECK (m_pileupReweightingTool->apply (*eventInfo));
+
+        // Add additional decorations
+        if (m_correctedScaledAverageMuAccessor)
+        {
+          (*m_correctedScaledAverageMuAccessor) (*eventInfo)
+            = m_pileupReweightingTool->getCorrectedAverageInteractionsPerCrossing (*eventInfo, true);
+        }
+
+        if (m_correctedActualMuAccessor)
+        {
+          (*m_correctedActualMuAccessor) (*eventInfo)
+            = m_pileupReweightingTool->getCorrectedActualInteractionsPerCrossing (*eventInfo);
+        }
+
+        if (m_correctedScaledActualMuAccessor)
+        {
+          (*m_correctedScaledActualMuAccessor) (*eventInfo)
+            = m_pileupReweightingTool->getCorrectedActualInteractionsPerCrossing (*eventInfo, true);
+        }
+
+        //--- PRWHash to recalculate PU weights using analysis ntuples
+        //--- https://twiki.cern.ch/twiki/bin/view/AtlasProtected/ExtendedPileupReweighting#Using_PRWHash_to_change_pileup_w
+
+        if (eventInfo->eventType (xAOD::EventInfo::IS_SIMULATION))
+        {
+          eventInfo->auxdata<ULong64_t>("PRWHash")
+            = m_pileupReweightingTool->getPRWHash (*eventInfo);
+        }
+
+        // In the case of nominal systematics store the RandomRunNumber for
+        // later. Event info can not be decorated at this point as the
+        // decoration will then also be present in subsequent shallow copies.
+        if (sys.empty())
+        {
+          nominalRandomRunNumber = eventInfo->auxdecor<unsigned int> ("RandomRunNumber");
+        }
+
+        return StatusCode::SUCCESS;
+      }));
+
+    // Must decorate the actual instance in the event store for
+    // the electron tool to work. The decoration is done out of the loop
+    // to avoid it being present in all subsequent shallow copies.
+    evtStore()->retrieve<const xAOD::EventInfo>("EventInfo")
+      ->auxdecor<unsigned int>("RandomRunNumber") = nominalRandomRunNumber;
+
+    return StatusCode::SUCCESS;
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/SysListDumperAlg.cxx b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/SysListDumperAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..5ed2c57b46ac4e98bcc4d4512fbe9c83b68f745e
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/SysListDumperAlg.cxx
@@ -0,0 +1,76 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Tadej Novak
+
+
+//
+// includes
+//
+
+#include <AsgAnalysisAlgorithms/SysListDumperAlg.h>
+#include <SystematicsHandles/Helpers.h>
+#include <TH1.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  SysListDumperAlg :: 
+  SysListDumperAlg (const std::string& name, 
+                    ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+  {
+    declareProperty ("histogramName", m_histogramName, "the name of the output histogram");
+  }
+
+
+
+  StatusCode SysListDumperAlg ::
+  initialize ()
+  {
+    if (m_histogramName.empty())
+    {
+      ANA_MSG_ERROR ("histogram name should not be empty");
+      return StatusCode::FAILURE;
+    }
+
+    ANA_CHECK (m_systematicsList.initialize());
+
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode SysListDumperAlg ::
+  execute ()
+  {
+    if (!m_firstEvent)
+    {
+      return StatusCode::SUCCESS;
+    }
+
+    m_firstEvent = false;
+
+    const std::unordered_set<CP::SystematicSet> systematics = m_systematicsList.systematicsVector();
+
+    ANA_CHECK (book (TH1F (m_histogramName.c_str(), "systematics", systematics.size(), 0, systematics.size())));
+    TH1 *histogram = hist (m_histogramName);
+
+    int i = 1;
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode
+    {
+      std::string name = sys.name();
+      if (name.empty())
+        name = nominalSystematicsName();
+
+      histogram->GetXaxis()->SetBinLabel(i, name.c_str());
+      i++;
+
+      return StatusCode::SUCCESS;
+    });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/SysListLoaderAlg.cxx b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/SysListLoaderAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..068697fa1fb374f1216c17e5d169b00576b85d35
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/SysListLoaderAlg.cxx
@@ -0,0 +1,137 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <regex>
+
+#include <AsgAnalysisAlgorithms/SysListLoaderAlg.h>
+
+#include <AsgTools/MessageCheck.h>
+#include <PATInterfaces/MakeSystematicsVector.h>
+#include <PATInterfaces/SystematicRegistry.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  SysListLoaderAlg :: 
+  SysListLoaderAlg (const std::string& name, 
+                    ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+  {
+    declareProperty ("systematicsName", m_systematicsName, "the name of the systematics in the event store");
+    declareProperty ("systematicsList", m_systematicsList, "the list of systematics to run");
+    declareProperty ("systematicsRegex", m_systematicsRegex, "systematics filter regex");
+    declareProperty ("sigmaRecommended", m_sigmaRecommended, "the sigma with which to run recommended systematics");
+  }
+
+
+
+  StatusCode SysListLoaderAlg ::
+  initialize ()
+  {
+    if (m_systematicsName.empty())
+    {
+      ANA_MSG_ERROR ("no name configured for the systematics list");
+      return StatusCode::FAILURE;
+    }
+
+    if (!std::isfinite (m_sigmaRecommended) ||
+        m_sigmaRecommended < 0)
+    {
+      ANA_MSG_ERROR ("invalid value for sigmaRecommended: " << m_sigmaRecommended);
+      return StatusCode::FAILURE;
+    }
+
+    if (m_sigmaRecommended != 0)
+    {
+      if (!m_systematicsList.empty())
+      {
+        ANA_MSG_ERROR ("can't specify both sigmaRecommended and systematicsList");
+        return StatusCode::FAILURE;
+      }
+    } else if (m_systematicsList.empty())
+    {
+      // take an empty property as running just the central
+      // systematics set.  implication is that you can't really run
+      // doing nothing, but that ought to be Ok.
+         m_systematicsVector.push_back (CP::SystematicSet ());
+    } else
+    {
+      for (const std::string& sysName : m_systematicsList)
+        m_systematicsVector.push_back (CP::SystematicSet (sysName));
+    }
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode SysListLoaderAlg ::
+  execute ()
+  {
+    if (m_firstEvent)
+    {
+      m_firstEvent = false;
+      const CP::SystematicSet recommended
+        = CP::SystematicRegistry::getInstance().recommendedSystematics();
+      const CP::SystematicSet affecting
+        = CP::SystematicRegistry::getInstance().globalSystematics();
+
+      for (const CP::SystematicVariation& mysys : affecting)
+      {
+        // this logic checks whether a systematic is recommended and
+        // affecting, or only affecting.  if it is only the later, it
+        // reports the systematic in parenthesis to set it apart.
+        if (recommended.find (mysys) == recommended.end())
+          ANA_MSG_INFO ("found systematic: (" << mysys << ")");
+        else
+          ANA_MSG_INFO ("found systematic: " << mysys);
+      }
+      bool error = false;
+      for (const CP::SystematicVariation& mysys : recommended)
+      {
+        if (affecting.find (mysys) == affecting.end())
+        {
+          ANA_MSG_ERROR ("systematic is only registered as recommended, not affecting: " << mysys);
+          error = true;
+        }
+      }
+      if (error)
+        return StatusCode::FAILURE;
+
+
+      if (m_systematicsVector.empty())
+      {
+        assert (m_sigmaRecommended > 0);
+        CP::MakeSystematicsVector sys;
+        sys.setSigma (m_sigmaRecommended);
+        sys.calc (recommended);
+
+        std::regex expr (m_systematicsRegex);
+        for (const CP::SystematicSet& mysys : sys.result(""))
+        {
+          if (!regex_match (mysys.name(), expr))
+          {
+            ANA_MSG_INFO ("skipping systematic: " << mysys.name());
+          } else {
+            ANA_MSG_INFO ("configuring systematic: " << mysys.name());
+            m_systematicsVector.push_back (mysys);
+          }
+        }
+      }
+    }
+
+    std::unique_ptr<SysListType> list (new SysListType (m_systematicsVector));
+    ANA_CHECK (evtStore()->record (list.release(), m_systematicsName));
+    return StatusCode::SUCCESS;
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/TreeFillerAlg.cxx b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/TreeFillerAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..a91a87021a62d3aa58519789d503de0f7fbbac00
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/TreeFillerAlg.cxx
@@ -0,0 +1,60 @@
+// Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+
+// Local include(s):
+#include "AsgAnalysisAlgorithms/TreeFillerAlg.h"
+
+// EDM include(s):
+#include "AthContainersInterfaces/IAuxTypeVectorFactory.h"
+#include "AthContainers/AuxElement.h"
+#include "AthContainers/AuxVectorBase.h"
+#include "AthContainers/normalizedTypeinfoName.h"
+
+// Framework include(s):
+#include "SystematicsHandles/Helpers.h"
+
+// ROOT include(s):
+#include <TClass.h>
+#include <TTree.h>
+#include <TBranch.h>
+#include <TVirtualCollectionProxy.h>
+
+// System include(s):
+#include <regex>
+#include <algorithm>
+#include <functional>
+#include <sstream>
+
+namespace CP {
+
+   TreeFillerAlg::TreeFillerAlg( const std::string& name,
+                                                 ISvcLocator* svcLoc )
+   : EL::AnaAlgorithm( name, svcLoc ) {
+
+      // Declare the algorithm's properties.
+      declareProperty( "TreeName", m_treeName = "physics",
+                       "Name of the tree to write" );
+   }
+
+   StatusCode TreeFillerAlg::execute() {
+      // get the output tree for the first time
+      if( ! m_tree ) {
+         m_tree = tree( m_treeName );
+      }
+
+      if( ! m_tree ) {
+         ATH_MSG_ERROR( "Could not find output tree \"" << m_treeName
+                        << "\"" );
+         return StatusCode::FAILURE;
+      }
+
+      // Fill the tree.
+      if( m_tree->Fill() < 0 ) {
+         ATH_MSG_ERROR( "Error while filling TTree" );
+         return StatusCode::FAILURE;
+      }
+
+      // Return gracefully.
+      return StatusCode::SUCCESS;
+   }
+
+} // namespace CP
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/TreeMakerAlg.cxx b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/TreeMakerAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..3e41601569439c009ad521ac17cd9e5a3061828d
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/TreeMakerAlg.cxx
@@ -0,0 +1,44 @@
+// Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+
+// Local include(s):
+#include "AsgAnalysisAlgorithms/TreeMakerAlg.h"
+
+// ROOT include(s):
+#include <TTree.h>
+
+namespace CP {
+
+   TreeMakerAlg::TreeMakerAlg( const std::string& name,
+                                                 ISvcLocator* svcLoc )
+   : EL::AnaAlgorithm( name, svcLoc ) {
+
+      // Declare the algorithm's properties.
+      declareProperty( "TreeName", m_treeName = "physics",
+                       "Name of the tree to write" );
+      declareProperty( "TreeAutoFlush", m_treeAutoFlush = 200,
+                       "AutoFlush value for the output tree" );
+   }
+
+   StatusCode TreeMakerAlg::execute() {
+      if( m_treeConfigured ) {
+         return StatusCode::SUCCESS;
+      }
+
+      // Create the output tree.
+      ATH_CHECK( book( TTree( m_treeName.c_str(), "xAOD->NTuple tree" ) ) );
+      TTree *mytree { tree( m_treeName ) };
+      if( ! mytree ) {
+         ATH_MSG_ERROR( "Could not create output tree \"" << m_treeName
+                        << "\"" );
+         return StatusCode::FAILURE;
+      }
+      mytree->SetAutoFlush( m_treeAutoFlush );
+      ATH_MSG_INFO( "Created xAOD->NTuple tree: " << m_treeName );
+
+      m_treeConfigured = true;
+
+      // Return gracefully.
+      return StatusCode::SUCCESS;
+   }
+
+} // namespace CP
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/python/AsgAnalysisAlgorithmsTest.py b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/python/AsgAnalysisAlgorithmsTest.py
new file mode 100644
index 0000000000000000000000000000000000000000..5d99638fa074f9ba76fab67d9d16cbeafeff43da
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/python/AsgAnalysisAlgorithmsTest.py
@@ -0,0 +1,217 @@
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+#
+# @author Nils Krumnack
+# @author Tadej Novak
+
+from AnaAlgorithm.AlgSequence import AlgSequence
+from AnaAlgorithm.DualUseConfig import createAlgorithm
+
+def makeOverlapSequence (dataType) :
+    algSeq = AlgSequence()
+
+    # Skip events with no primary vertex:
+    algSeq += createAlgorithm( 'CP::VertexSelectionAlg',
+                               'PrimaryVertexSelectorAlg' )
+    algSeq.PrimaryVertexSelectorAlg.VertexContainer = 'PrimaryVertices'
+    algSeq.PrimaryVertexSelectorAlg.MinVertices = 1
+
+    # Set up the systematics loader/handler algorithm:
+    algSeq += createAlgorithm( 'CP::SysListLoaderAlg', 'SysLoaderAlg' )
+    algSeq.SysLoaderAlg.sigmaRecommended = 1
+
+    # Include, and then set up the pileup analysis sequence:
+    from AsgAnalysisAlgorithms.PileupAnalysisSequence import \
+        makePileupAnalysisSequence
+    pileupSequence = makePileupAnalysisSequence( dataType )
+    pileupSequence.configure( inputName = 'EventInfo', outputName = 'EventInfo_%SYS%' )
+    algSeq += pileupSequence
+
+    # Include, and then set up the electron analysis sequence:
+    from EgammaAnalysisAlgorithms.ElectronAnalysisSequence import \
+        makeElectronAnalysisSequence
+    electronSequence = makeElectronAnalysisSequence( dataType, 'LooseLHElectron.GradientLoose',
+                                                     recomputeLikelihood = True )
+    electronSequence.configure( inputName = 'Electrons',
+                                outputName = 'AnalysisElectrons_%SYS%' )
+    algSeq += electronSequence
+
+    # Include, and then set up the photon analysis sequence:
+    from EgammaAnalysisAlgorithms.PhotonAnalysisSequence import \
+        makePhotonAnalysisSequence
+    photonSequence = makePhotonAnalysisSequence( dataType, 'Tight.FixedCutTight', recomputeIsEM = True )
+    photonSequence.configure( inputName = 'Photons',
+                              outputName = 'AnalysisPhotons_%SYS%' )
+    algSeq += photonSequence
+
+    # Include, and then set up the muon analysis algorithm sequence:
+    from MuonAnalysisAlgorithms.MuonAnalysisSequence import makeMuonAnalysisSequence
+    muonSequence = makeMuonAnalysisSequence( dataType, 'Tight.Iso' )
+    muonSequence.configure( inputName = 'Muons',
+                            outputName = 'AnalysisMuons_%SYS%' )
+    algSeq += muonSequence
+
+    # Include, and then set up the jet analysis algorithm sequence:
+    jetContainer = 'AntiKt4EMPFlowJets'
+    from JetAnalysisAlgorithms.JetAnalysisSequence import makeJetAnalysisSequence
+    jetSequence = makeJetAnalysisSequence( dataType, jetContainer )
+    jetSequence.configure( inputName = jetContainer,
+                           outputName = 'AnalysisJets_%SYS%' )
+    algSeq += jetSequence
+
+    # Include, and then set up the tau analysis algorithm sequence:
+    from TauAnalysisAlgorithms.TauAnalysisSequence import makeTauAnalysisSequence
+    tauSequence = makeTauAnalysisSequence( dataType, 'Tight' )
+    tauSequence.configure( inputName = 'TauJets',
+                           outputName = 'AnalysisTauJets_%SYS%' )
+    algSeq += tauSequence
+
+    # Include, and then set up the overlap analysis algorithm sequence:
+    from AsgAnalysisAlgorithms.OverlapAnalysisSequence import \
+        makeOverlapAnalysisSequence
+    overlapSequence = makeOverlapAnalysisSequence( dataType, doMuPFJetOR=True, enableCutflow=True )
+    overlapSequence.configure(
+        inputName = {
+            'electrons' : 'AnalysisElectrons_%SYS%',
+            'photons'   : 'AnalysisPhotons_%SYS%',
+            'muons'     : 'AnalysisMuons_%SYS%',
+            'jets'      : 'AnalysisJets_%SYS%',
+            'taus'      : 'AnalysisTauJets_%SYS%' },
+        outputName = {
+            'electrons' : 'AnalysisElectronsOR_%SYS%',
+            'photons'   : 'AnalysisPhotonsOR_%SYS%',
+            'muons'     : 'AnalysisMuonsOR_%SYS%',
+            'jets'      : 'AnalysisJetsOR_%SYS%',
+            'taus'      : 'AnalysisTauJetsOR_%SYS%' },
+        affectingSystematics = {
+            'electrons' : electronSequence.affectingSystematics(),
+            'photons'   : photonSequence.affectingSystematics(),
+            'muons'     : muonSequence.affectingSystematics(),
+            'jets'      : jetSequence.affectingSystematics(),
+            'taus'      : tauSequence.affectingSystematics() } )
+    algSeq += overlapSequence
+
+    # Set up an ntuple to check the job with:
+    treeMaker = createAlgorithm( 'CP::TreeMakerAlg', 'TreeMaker' )
+    treeMaker.TreeName = 'particles'
+    algSeq += treeMaker
+    ntupleMaker = createAlgorithm( 'CP::AsgxAODNTupleMakerAlg', 'NTupleMaker' )
+    ntupleMaker.TreeName = 'particles'
+    ntupleMaker.Branches = [
+        'EventInfo.runNumber   -> runNumber',
+        'EventInfo.eventNumber -> eventNumber',
+        'AnalysisElectrons_%SYS%.eta -> el_%SYS%_eta',
+        'AnalysisElectrons_%SYS%.phi -> el_%SYS%_phi',
+        'AnalysisElectrons_%SYS%.pt  -> el_%SYS%_pt',
+        'AnalysisElectronsOR_%SYS%.eta -> el_OR_%SYS%_eta',
+        'AnalysisElectronsOR_%SYS%.phi -> el_OR_%SYS%_phi',
+        'AnalysisElectronsOR_%SYS%.pt  -> el_OR_%SYS%_pt',
+        'AnalysisPhotons_%SYS%.eta -> ph_%SYS%_eta',
+        'AnalysisPhotons_%SYS%.phi -> ph_%SYS%_phi',
+        'AnalysisPhotons_%SYS%.pt  -> ph_%SYS%_pt',
+        'AnalysisPhotonsOR_%SYS%.eta -> ph_OR_%SYS%_eta',
+        'AnalysisPhotonsOR_%SYS%.phi -> ph_OR_%SYS%_phi',
+        'AnalysisPhotonsOR_%SYS%.pt  -> ph_OR_%SYS%_pt',
+        'AnalysisMuons_%SYS%.eta -> mu_%SYS%_eta',
+        'AnalysisMuons_%SYS%.phi -> mu_%SYS%_phi',
+        'AnalysisMuons_%SYS%.pt  -> mu_%SYS%_pt',
+        'AnalysisMuonsOR_%SYS%.eta -> mu_OR_%SYS%_eta',
+        'AnalysisMuonsOR_%SYS%.phi -> mu_OR_%SYS%_phi',
+        'AnalysisMuonsOR_%SYS%.pt  -> mu_OR_%SYS%_pt',
+        'AnalysisJets_%SYS%.eta -> jet_%SYS%_eta',
+        'AnalysisJets_%SYS%.phi -> jet_%SYS%_phi',
+        'AnalysisJets_%SYS%.pt  -> jet_%SYS%_pt',
+        'AnalysisJetsOR_%SYS%.eta -> jet_OR_%SYS%_eta',
+        'AnalysisJetsOR_%SYS%.phi -> jet_OR_%SYS%_phi',
+        'AnalysisJetsOR_%SYS%.pt  -> jet_OR_%SYS%_pt',
+        'AnalysisTauJets_%SYS%.eta -> tau_%SYS%_eta',
+        'AnalysisTauJets_%SYS%.phi -> tau_%SYS%_phi',
+        'AnalysisTauJets_%SYS%.pt  -> tau_%SYS%_pt',
+        'AnalysisTauJetsOR_%SYS%.eta -> tau_OR_%SYS%_eta',
+        'AnalysisTauJetsOR_%SYS%.phi -> tau_OR_%SYS%_phi',
+        'AnalysisTauJetsOR_%SYS%.pt  -> tau_OR_%SYS%_pt' ]
+    ntupleMaker.systematicsRegex = '.*'
+    algSeq += ntupleMaker
+    treeFiller = createAlgorithm( 'CP::TreeFillerAlg', 'TreeFiller' )
+    treeFiller.TreeName = 'particles'
+    algSeq += treeFiller
+
+    return algSeq
+
+
+def makeEventAlgorithmsSequence (dataType) :
+
+    # Config:
+    GRLFiles = ['GoodRunsLists/data16_13TeV/20180129/data16_13TeV.periodAllYear_DetStatus-v89-pro21-01_DQDefects-00-02-04_PHYS_StandardGRL_All_Good_25ns.xml']
+
+    algSeq = AlgSequence()
+
+    # Set up the systematics loader/handler algorithm:
+    algSeq += createAlgorithm( 'CP::SysListLoaderAlg', 'SysLoaderAlg' )
+    algSeq.SysLoaderAlg.sigmaRecommended = 1
+
+    # Include, and then set up the pileup analysis sequence:
+    from AsgAnalysisAlgorithms.EventSelectionAnalysisSequence import \
+        makeEventSelectionAnalysisSequence
+    eventSelectionSequence = \
+        makeEventSelectionAnalysisSequence( dataType, userGRLFiles=GRLFiles )
+    algSeq += eventSelectionSequence
+
+    # Set up an ntuple to check the job with:
+    treeMaker = createAlgorithm( 'CP::TreeMakerAlg', 'TreeMaker' )
+    treeMaker.TreeName = 'events'
+    algSeq += treeMaker
+    ntupleMaker = createAlgorithm( 'CP::AsgxAODNTupleMakerAlg', 'NTupleMaker' )
+    ntupleMaker.TreeName = 'events'
+    ntupleMaker.Branches = [
+        'EventInfo.runNumber   -> runNumber',
+        'EventInfo.eventNumber -> eventNumber',
+        ]
+    ntupleMaker.systematicsRegex = '.*'
+    algSeq += ntupleMaker
+    treeFiller = createAlgorithm( 'CP::TreeFillerAlg', 'TreeFiller' )
+    treeFiller.TreeName = 'events'
+    algSeq += treeFiller
+
+    return algSeq
+
+
+def makeGeneratorAlgorithmsSequence (dataType) :
+
+    algSeq = AlgSequence()
+
+    # Set up the systematics loader/handler algorithm:
+    algSeq += createAlgorithm( 'CP::SysListLoaderAlg', 'SysLoaderAlg' )
+    algSeq.SysLoaderAlg.sigmaRecommended = 1
+
+    # Include, and then set up the pileup analysis sequence (to make a copy):
+    from AsgAnalysisAlgorithms.PileupAnalysisSequence import \
+        makePileupAnalysisSequence
+    pileupSequence = makePileupAnalysisSequence( dataType )
+    pileupSequence.configure( inputName = 'EventInfo', outputName = 'EventInfo_%SYS%' )
+    algSeq += pileupSequence
+
+    # Include, and then set up the generator analysis sequence:
+    from AsgAnalysisAlgorithms.GeneratorAnalysisSequence import \
+        makeGeneratorAnalysisSequence
+    generatorSequence = makeGeneratorAnalysisSequence( dataType, saveCutBookkeepers=True, runNumber=284500, cutBookkeepersSystematics=True )
+    generatorSequence.configure( inputName = 'EventInfo_%SYS%', outputName = {} )
+    algSeq += generatorSequence
+
+    # Set up an ntuple to check the job with:
+    treeMaker = createAlgorithm( 'CP::TreeMakerAlg', 'TreeMaker' )
+    treeMaker.TreeName = 'events'
+    algSeq += treeMaker
+    ntupleMaker = createAlgorithm( 'CP::AsgxAODNTupleMakerAlg', 'NTupleMaker' )
+    ntupleMaker.TreeName = 'events'
+    ntupleMaker.Branches = [
+        'EventInfo_NOSYS.runNumber   -> runNumber',
+        'EventInfo_NOSYS.eventNumber -> eventNumber',
+        'EventInfo_NOSYS.generatorWeight_%SYS% -> generatorWeight_%SYS%',
+    ]
+    ntupleMaker.systematicsRegex = '.*'
+    algSeq += ntupleMaker
+    treeFiller = createAlgorithm( 'CP::TreeFillerAlg', 'TreeFiller' )
+    treeFiller.TreeName = 'events'
+    algSeq += treeFiller
+
+    return algSeq
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/python/EventSelectionAnalysisSequence.py b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/python/EventSelectionAnalysisSequence.py
new file mode 100644
index 0000000000000000000000000000000000000000..94f78652dea7d12a7272b787194301822d63fe79
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/python/EventSelectionAnalysisSequence.py
@@ -0,0 +1,56 @@
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+#
+# @author Tadej Novak
+
+# AnaAlgorithm import(s):
+from AnaAlgorithm.AnaAlgSequence import AnaAlgSequence
+from AnaAlgorithm.DualUseConfig import createAlgorithm, addPrivateTool
+
+
+def makeEventSelectionAnalysisSequence( dataType,
+                                        runPrimaryVertexSelection = True,
+                                        runEventCleaning = False,
+                                        userGRLFiles = []):
+    """Create a basic event selection analysis algorithm sequence
+
+    Keyword arguments:
+      dataType -- The data type to run on ("data", "mc" or "afii")
+      runPrimaryVertexSelection -- whether to run primary vertex selection
+      runEventCleaning -- wether to run event cleaning
+      userGRLFiles -- a list of GRL files to select data from
+    """
+
+    if dataType not in ["data", "mc", "afii"] :
+        raise ValueError ("invalid data type: " + dataType)
+
+    # Create the analysis algorithm sequence object:
+    seq = AnaAlgSequence( "EventSelectionAnalysisSequence" )
+
+    if dataType == 'data':
+        grlFiles = userGRLFiles[:]
+
+        # Set up the GRL selection:
+        alg = createAlgorithm( 'GRLSelectorAlg', 'GRLSelectorAlg' )
+        addPrivateTool( alg, 'Tool', 'GoodRunsListSelectionTool' )
+        alg.Tool.GoodRunsListVec = grlFiles
+
+        seq.append( alg, inputPropName = None )
+
+    # Skip events with no primary vertex:
+    if runPrimaryVertexSelection:
+        alg = createAlgorithm( 'CP::VertexSelectionAlg',
+                               'PrimaryVertexSelectorAlg' )
+        alg.VertexContainer = 'PrimaryVertices'
+        alg.MinVertices = 1
+
+        seq.append( alg, inputPropName = None )
+
+    # Set up the event cleaning selection:
+    if runEventCleaning:
+        alg = createAlgorithm( 'CP::EventFlagSelectionAlg', 'EventFlagSelectorAlg' )
+        alg.selectionFlags = ['DFCommonJets_eventClean_LooseBad,as_char']
+
+        seq.append( alg, inputPropName = None )
+
+    # Return the sequence:
+    return seq
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/python/GeneratorAnalysisSequence.py b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/python/GeneratorAnalysisSequence.py
new file mode 100644
index 0000000000000000000000000000000000000000..0a8e7ca75176b5147c1f78f1f34fd19b229f9b52
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/python/GeneratorAnalysisSequence.py
@@ -0,0 +1,47 @@
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+
+# AnaAlgorithm import(s):
+from AnaAlgorithm.AnaAlgSequence import AnaAlgSequence
+from AnaAlgorithm.DualUseConfig import createAlgorithm, addPrivateTool
+
+def makeGeneratorAnalysisSequence( dataType,
+                                   saveCutBookkeepers=False,
+                                   runNumber=0,
+                                   cutBookkeepersSystematics=False ):
+    """Create a generator analysis algorithm sequence
+
+    Keyword arguments:
+      dataType -- The data type to run on ("mc" or "afii")
+      saveCutBookkeepers -- save cut bokkeepers information into output file
+      runNumber -- MC run number
+      cutBookkeepersSystematics -- store CutBookkeepers systematics
+    """
+
+    if dataType not in ["mc", "afii"] :
+        raise ValueError ("invalid data type: " + dataType)
+
+    if saveCutBookkeepers and not runNumber:
+        raise ValueError ("invalid run number: " + 0)
+
+    # Create the analysis algorithm sequence object:
+    seq = AnaAlgSequence( "GeneratorAnalysisSequence" )
+
+    # Set up the CutBookkeepers algorithm:
+    if saveCutBookkeepers:
+      alg = createAlgorithm('CP::AsgCutBookkeeperAlg', 'CutBookkeeperAlg')
+      alg.runNumber = runNumber
+      alg.enableSystematics = cutBookkeepersSystematics
+      addPrivateTool( alg, 'truthWeightTool', 'PMGTools::PMGTruthWeightTool' )
+      seq.append( alg, inputPropName = None )
+
+    # Set up the weights algorithm:
+    alg = createAlgorithm( 'CP::PMGTruthWeightAlg', 'PMGTruthWeightAlg' )
+    addPrivateTool( alg, 'truthWeightTool', 'PMGTools::PMGTruthWeightTool' )
+    alg.decoration = 'generatorWeight_%SYS%'
+    alg.decorationRegex = '(^GEN_.*)'
+
+    seq.append( alg, inputPropName = 'eventInfo',
+                affectingSystematics = '(^GEN_.*)' )
+
+    # Return the sequence:
+    return seq
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/python/OverlapAnalysisSequence.py b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/python/OverlapAnalysisSequence.py
new file mode 100644
index 0000000000000000000000000000000000000000..d530ba9bb34aa136f2d2de60dea624f0211cfcb0
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/python/OverlapAnalysisSequence.py
@@ -0,0 +1,287 @@
+# Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+
+# AnaAlgorithm import(s):
+from AnaAlgorithm.AnaAlgSequence import AnaAlgSequence
+from AnaAlgorithm.DualUseConfig import createAlgorithm, addPrivateTool
+
+def makeOverlapAnalysisSequence( dataType,
+                                 inputLabel = '', outputLabel = 'passesOR',
+                                 linkOverlapObjects = False, doMuPFJetOR=False,
+                                 doEleEleOR = False, doElectrons = True,
+                                 doMuons = True, doJets = True, doTaus = True,
+                                 doPhotons = True, doFatJets = False,
+                                 bJetLabel = '',
+                                 boostedLeptons = False,
+                                 postfix = '',
+                                 enableCutflow = False ):
+    """Function creating the overlap removal algorithm sequence
+
+    The function sets up a multi-input/multi-output analysis algorithm sequnce,
+    which needs to be used in a quite particular way. First off you need to set
+    the arguments of this function correctly.
+
+    Then, you need to call the configure(...) method on the algorithm sequence
+    returned by this function in the following way:
+
+      overlapSequence.configure(
+         inputName = {
+            'electrons' : 'AnalysisElectrons_%SYS%',
+            'photons'   : 'AnalysisPhotons_%SYS%',
+            'muons'     : 'AnalysisMuons_%SYS%',
+            'jets'      : 'AnalysisJets_%SYS%',
+            'taus'      : 'AnalysisTauJets_%SYS%' },
+         outputName = {
+            'electrons' : 'AnalysisElectronsOR_%SYS%',
+            'photons'   : 'AnalysisPhotonsOR_%SYS%',
+            'muons'     : 'AnalysisMuonsOR_%SYS%',
+            'jets'      : 'AnalysisJetsOR_%SYS%',
+            'taus'      : 'AnalysisTauJetsOR_%SYS%' },
+         affectingSystematics = {
+            'electrons' : '(^$)|(^EG_.*)|(^EL_.*)',
+            'photons'   : '(^$)|(^EG_.*)|(^PH_.*)',
+            'muons'     : '(^$)|(^MUON_.*)',
+            'jets'      : '(^$)|(^JET_.*)',
+            'taus'      : '(^$)|(^TAUS_.*)' } )
+
+    Where:
+      - You need to provide input and output names in pairs, you must not skip
+        specifying an output name if you specified an input name, and vice
+        versa.
+      - You only define inputs/outputs that your analysis uses. The "labels" of
+        the possible inputs/outputs are: "electrons", "photons", "muons",
+        "jets", "taus" and "fatJets".
+      - You have to define with affectingSystematics which systematic variations
+        are affecting the containers you passed to the sequence as inputs. If
+        left empty, the configuration assumes that no systematic variation is
+        affecting the input(s).
+
+    Function keyword arguments:
+      dataType -- The data type to run on ("data", "mc" or "afii")
+      inputLabel -- Any possible label to pick up the selected objects with. If
+                    left empty, all objects from the input containers are
+                    considered.
+      outputLabel -- Decoration put on the output variables. Set to "true" for
+                     objects passing the overlap removal.
+      linkOverlapObjects -- Set up an element link between overlapping objects
+      doMuPFJetOR -- Set up overlap removal for PFlow jets that are acutally muons
+      doEleEleOR -- Set up electron-electron overlap removal
+      doXXXX     -- these flags enable/disable object types to
+                    configure tools for: doElectrons, doMuons,
+                    doJets, doTaus, doPhotons, doFatJets.
+      bJetLabel -- Flag to select b-jets with. If left empty, no b-jets are used
+                   in the overlap removal.
+      boostedLeptons -- Set to True to enable boosted lepton overlap removal
+      enableCutflow -- Whether or not to dump the cutflow
+    """
+
+    if dataType not in ["data", "mc", "afii"] :
+        raise ValueError ("invalid data type: " + dataType)
+
+    # Create the analysis algorithm sequence object:
+    seq = AnaAlgSequence( 'OverlapAnalysisSequence' + postfix )
+
+    # Create the overlap removal algorithm:
+    alg = createAlgorithm( 'CP::OverlapRemovalAlg', 'OverlapRemovalAlg' + postfix )
+
+    # Create its main tool, and set its basic properties:
+    addPrivateTool( alg, 'overlapTool', 'ORUtils::OverlapRemovalTool' )
+    alg.overlapTool.InputLabel = inputLabel
+    alg.overlapTool.OutputLabel = outputLabel
+
+    # By default the OverlapRemovalTool would flag objects that need to be
+    # suppressed, with a "true" value. But since the analysis algorithms expect
+    # the opposite behaviour from selection flags, we need to tell the tool
+    # explicitly to use the "true" flag on objects that pass the overlap
+    # removal.
+    alg.overlapTool.OutputPassValue = True
+
+    # Set up overlap removal for PFlow jets that are acutally muons, if requested.
+    if doMuPFJetOR:
+       addPrivateTool( alg, 'overlapTool.MuPFJetORT',
+                       'ORUtils::MuPFJetOverlapTool' )
+       alg.overlapTool.MuPFJetORT.InputLabel = inputLabel
+       alg.overlapTool.MuPFJetORT.OutputLabel = outputLabel
+       alg.overlapTool.MuPFJetORT.LinkOverlapObjects = linkOverlapObjects
+       alg.overlapTool.MuPFJetORT.OutputPassValue = True
+       pass
+
+    # Set up the electron-electron overlap removal, if requested.
+    if doElectrons and doEleEleOR:
+        addPrivateTool( alg, 'overlapTool.EleEleORT',
+                        'ORUtils::EleEleOverlapTool' )
+        alg.overlapTool.EleEleORT.InputLabel = inputLabel
+        alg.overlapTool.EleEleORT.OutputLabel = outputLabel
+        alg.overlapTool.EleEleORT.LinkOverlapObjects = linkOverlapObjects
+        alg.overlapTool.EleEleORT.OutputPassValue = True
+        pass
+
+    # Set up the electron-muon overlap removal.
+    if doElectrons and doMuons:
+        addPrivateTool( alg, 'overlapTool.EleMuORT',
+                        'ORUtils::EleMuSharedTrkOverlapTool' )
+        alg.overlapTool.EleMuORT.InputLabel = inputLabel
+        alg.overlapTool.EleMuORT.OutputLabel = outputLabel
+        alg.overlapTool.EleMuORT.LinkOverlapObjects = linkOverlapObjects
+        alg.overlapTool.EleMuORT.OutputPassValue = True
+        pass
+
+    # Set up the electron-(narrow-)jet overlap removal.
+    if doElectrons and doJets:
+        addPrivateTool( alg, 'overlapTool.EleJetORT',
+                        'ORUtils::EleJetOverlapTool' )
+        alg.overlapTool.EleJetORT.InputLabel = inputLabel
+        alg.overlapTool.EleJetORT.OutputLabel = outputLabel
+        alg.overlapTool.EleJetORT.LinkOverlapObjects = linkOverlapObjects
+        alg.overlapTool.EleJetORT.BJetLabel = bJetLabel
+        alg.overlapTool.EleJetORT.UseSlidingDR = boostedLeptons
+        alg.overlapTool.EleJetORT.OutputPassValue = True
+        pass
+
+    # Set up the muon-(narrow-)jet overlap removal.
+    if doMuons and doJets:
+        addPrivateTool( alg, 'overlapTool.MuJetORT',
+                        'ORUtils::MuJetOverlapTool' )
+        alg.overlapTool.MuJetORT.InputLabel = inputLabel
+        alg.overlapTool.MuJetORT.OutputLabel = outputLabel
+        alg.overlapTool.MuJetORT.LinkOverlapObjects = linkOverlapObjects
+        alg.overlapTool.MuJetORT.BJetLabel = bJetLabel
+        alg.overlapTool.MuJetORT.UseSlidingDR = boostedLeptons
+        alg.overlapTool.MuJetORT.OutputPassValue = True
+        pass
+
+    # Set up the tau-electron overlap removal.
+    if doTaus and doElectrons:
+        addPrivateTool( alg, 'overlapTool.TauEleORT',
+                        'ORUtils::DeltaROverlapTool' )
+        alg.overlapTool.TauEleORT.InputLabel = inputLabel
+        alg.overlapTool.TauEleORT.OutputLabel = outputLabel
+        alg.overlapTool.TauEleORT.LinkOverlapObjects = linkOverlapObjects
+        alg.overlapTool.TauEleORT.DR = 0.2
+        alg.overlapTool.TauEleORT.OutputPassValue = True
+        pass
+
+    # Set up the tau-muon overlap removal.
+    if doTaus and doMuons:
+        addPrivateTool( alg, 'overlapTool.TauMuORT',
+                        'ORUtils::DeltaROverlapTool' )
+        alg.overlapTool.TauMuORT.InputLabel = inputLabel
+        alg.overlapTool.TauMuORT.OutputLabel = outputLabel
+        alg.overlapTool.TauMuORT.LinkOverlapObjects = linkOverlapObjects
+        alg.overlapTool.TauMuORT.DR = 0.2
+        alg.overlapTool.TauMuORT.OutputPassValue = True
+        pass
+
+    # Set up the tau-(narrow-)jet overlap removal.
+    if doTaus and doJets:
+        addPrivateTool( alg, 'overlapTool.TauJetORT',
+                        'ORUtils::DeltaROverlapTool' )
+        alg.overlapTool.TauJetORT.InputLabel = inputLabel
+        alg.overlapTool.TauJetORT.OutputLabel = outputLabel
+        alg.overlapTool.TauJetORT.LinkOverlapObjects = linkOverlapObjects
+        alg.overlapTool.TauJetORT.DR = 0.2
+        alg.overlapTool.TauJetORT.OutputPassValue = True
+        pass
+
+    # Set up the photon-electron overlap removal.
+    if doPhotons and doElectrons:
+        addPrivateTool( alg, 'overlapTool.PhoEleORT',
+                        'ORUtils::DeltaROverlapTool' )
+        alg.overlapTool.PhoEleORT.InputLabel = inputLabel
+        alg.overlapTool.PhoEleORT.OutputLabel = outputLabel
+        alg.overlapTool.PhoEleORT.LinkOverlapObjects = linkOverlapObjects
+        alg.overlapTool.PhoEleORT.OutputPassValue = True
+        pass
+
+    # Set up the photon-muon overlap removal.
+    if doPhotons and doMuons:
+        addPrivateTool( alg, 'overlapTool.PhoMuORT',
+                        'ORUtils::DeltaROverlapTool' )
+        alg.overlapTool.PhoMuORT.InputLabel = inputLabel
+        alg.overlapTool.PhoMuORT.OutputLabel = outputLabel
+        alg.overlapTool.PhoMuORT.LinkOverlapObjects = linkOverlapObjects
+        alg.overlapTool.PhoMuORT.OutputPassValue = True
+        pass
+
+    # Set up the photon-(narrow-)jet overlap removal.
+    if doPhotons and doJets:
+        addPrivateTool( alg, 'overlapTool.PhoJetORT',
+                        'ORUtils::DeltaROverlapTool' )
+        alg.overlapTool.PhoJetORT.InputLabel = inputLabel
+        alg.overlapTool.PhoJetORT.OutputLabel = outputLabel
+        alg.overlapTool.PhoJetORT.LinkOverlapObjects = linkOverlapObjects
+        alg.overlapTool.PhoJetORT.OutputPassValue = True
+        pass
+
+    # Set up the electron-fat-jet overlap removal.
+    if doElectrons and doFatJets:
+        addPrivateTool( alg, 'overlapTool.EleFatJetORT',
+                        'ORUtils::DeltaROverlapTool' )
+        alg.overlapTool.EleFatJetORT.InputLabel = inputLabel
+        alg.overlapTool.EleFatJetORT.OutputLabel = outputLabel
+        alg.overlapTool.EleFatJetORT.LinkOverlapObjects = linkOverlapObjects
+        alg.overlapTool.EleFatJetORT.DR = 1.0
+        alg.overlapTool.EleFatJetORT.OutputPassValue = True
+        pass
+
+    # Set up the (narrow-)jet-fat-jet overlap removal.
+    if doJets and doFatJets:
+        addPrivateTool( alg, 'overlapTool.JetFatJetORT',
+                        'ORUtils::DeltaROverlapTool' )
+        alg.overlapTool.JetFatJetORT.InputLabel = inputLabel
+        alg.overlapTool.JetFatJetORT.OutputLabel = outputLabel
+        alg.overlapTool.JetFatJetORT.LinkOverlapObjects = linkOverlapObjects
+        alg.overlapTool.JetFatJetORT.DR = 1.0
+        alg.overlapTool.JetFatJetORT.OutputPassValue = True
+        pass
+
+    # Add the algorithm to the analysis sequence.
+    seq.append( alg,
+                inputPropName = { 'electrons' : 'electrons',
+                                  'muons'     : 'muons',
+                                  'jets'      : 'jets',
+                                  'taus'      : 'taus',
+                                  'photons'   : 'photons',
+                                  'fatJets'   : 'fatJets' },
+                outputPropName = { 'electrons' : 'electronsOut',
+                                   'muons'     : 'muonsOut',
+                                   'jets'      : 'jetsOut',
+                                   'taus'      : 'tausOut',
+                                   'photons'   : 'photonsOut',
+                                   'fatJets'   : 'fatJetsOut' } )
+
+    # Add view container creation algorithms for all types.
+    for container in [ ( 'electrons', doElectrons ),
+                       ( 'muons',     doMuons ),
+                       ( 'jets',      doJets ),
+                       ( 'taus',      doTaus ),
+                       ( 'photons',   doPhotons ),
+                       ( 'fatJets',   doFatJets ) ]:
+
+        # Skip setting up a view container if the type is not being processed.
+        if not container[ 1 ]:
+            continue
+
+        # Set up a cutflow alg.
+        if enableCutflow:
+            alg = createAlgorithm( 'CP::ObjectCutFlowHistAlg',
+                                   'OverlapRemovalCutFlowDumperAlg_%s' % container[ 0 ] + postfix )
+            alg.histPattern = container[ 0 ] + postfix + '_OR_cflow_%SYS%'
+            if inputLabel:
+                alg.selection = [ '%s,as_char' % inputLabel,
+                                  '%s,as_char' % outputLabel ]
+                alg.selectionNCuts = [1, 1]
+            else:
+                alg.selection = [ '%s,as_char' % outputLabel ]
+                alg.selectionNCuts = [1]
+            seq.append( alg, inputPropName = { container[ 0 ] : 'input' } )
+
+        # Set up a view container for the type.
+        alg = createAlgorithm( 'CP::AsgViewFromSelectionAlg',
+                               'OverlapRemovalViewMaker_%s' % container[ 0 ] + postfix )
+        alg.selection = [ '%s,as_char' % outputLabel ]
+        seq.append( alg, inputPropName = { container[ 0 ] : 'input' },
+                    outputPropName = { container[ 0 ] : 'output' } )
+        pass
+
+    # Return the sequence:
+    return seq
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/python/PileupAnalysisSequence.py b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/python/PileupAnalysisSequence.py
new file mode 100644
index 0000000000000000000000000000000000000000..2198606ef20ae549b3ecf38a304b1d413341ef78
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/python/PileupAnalysisSequence.py
@@ -0,0 +1,51 @@
+# Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+
+# AnaAlgorithm import(s):
+from AnaAlgorithm.AnaAlgSequence import AnaAlgSequence
+from AnaAlgorithm.DualUseConfig import createAlgorithm, addPrivateTool
+
+def makePileupAnalysisSequence( dataType, userPileupConfigs=[], userLumicalcFiles=[] , autoConfig=False ):
+    """Create a PRW analysis algorithm sequence
+
+    Keyword arguments:
+      dataType -- The data type to run on ("data", "mc" or "afii")
+    """
+
+    if dataType not in ["data", "mc", "afii"] :
+        raise ValueError ("invalid data type: " + dataType)
+
+    # Create the analysis algorithm sequence object:
+    seq = AnaAlgSequence( "PileupAnalysisSequence" )
+
+    muMcFiles = userPileupConfigs[:]
+    if autoConfig:
+        from PileupReweighting.AutoconfigurePRW import getLumiCalcFiles,getMCMuFiles
+        userLumicalcFiles = getLumiCalcFiles()
+        if len(muMcFiles)==0:
+            muMcFiles = getMCMuFiles()
+        else:
+            from AthenaCommon import Logging
+            prwlog = Logging.logging.getLogger('makePileupAnalysisSequence')
+            prwlog.warning('Sent autoconfig and userPileupConfigs='+str(userPileupConfigs))
+            prwlog.warning('Ignoring autoconfig and keeping user-specified files')
+
+    if userLumicalcFiles==[]:
+        muDataFiles = ["GoodRunsLists/data15_13TeV/20170619/PHYS_StandardGRL_All_Good_25ns_276262-284484_OflLumi-13TeV-008.root",
+                       "GoodRunsLists/data16_13TeV/20180129/PHYS_StandardGRL_All_Good_25ns_297730-311481_OflLumi-13TeV-009.root",
+                       "GoodRunsLists/data17_13TeV/20180619/physics_25ns_Triggerno17e33prim.lumicalc.OflLumi-13TeV-010.root",
+                       "GoodRunsLists/data18_13TeV/20190708/ilumicalc_histograms_None_348885-364292_OflLumi-13TeV-010.root" ]
+    else:
+        muDataFiles = userLumicalcFiles[:]
+
+    # Set up the only algorithm of the sequence:
+    alg = createAlgorithm( 'CP::PileupReweightingAlg', 'PileupReweightingAlg' )
+    addPrivateTool( alg, 'pileupReweightingTool', 'CP::PileupReweightingTool' )
+    alg.pileupReweightingTool.ConfigFiles = muMcFiles
+    alg.pileupReweightingTool.LumiCalcFiles = muDataFiles
+
+    seq.append( alg, inputPropName = 'eventInfo',
+                outputPropName = 'eventInfoOut',
+                affectingSystematics = '(^PRW_.*)' )
+
+    # Return the sequence:
+    return seq
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/python/SequencePostConfiguration.py b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/python/SequencePostConfiguration.py
new file mode 100644
index 0000000000000000000000000000000000000000..4069de29a5a68e5b5aab0304df633c1bc09c693d
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/python/SequencePostConfiguration.py
@@ -0,0 +1,32 @@
+# Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+#
+# @author Nils Krumnack
+
+
+def sequencePostConfiguration (sequence, inputContainer) :
+    currentContainer = inputContainer
+    inputRegex = "(^$)"
+    nextTemp = 0
+    for alg in sequence :
+        config = alg["alg"]
+        config.__setattr__ (alg["in"], currentContainer)
+        config.__setattr__ (alg["in"] + "Regex", inputRegex)
+
+        # need to make a shallow copy if we add systematics, or if this is
+        # the first algorithm, of if we are explicitly asked for an output
+        if "out" in alg and (inputContainer == currentContainer or "sys" in alg or ("needOut" in alg and alg["needOut"])) :
+            currentContainer = inputContainer + "_tmp" + str (nextTemp) + "_%SYS%"
+            nextTemp = nextTemp + 1
+            config.__setattr__ (alg["out"], currentContainer)
+            if "sys" in alg :
+                inputRegex = inputRegex + "|" + alg["sys"]
+                pass
+            pass
+
+        if "sys" in alg :
+            config.systematicsRegex = alg["sys"]
+            pass
+
+        pass
+
+    pass
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/python/__init__.py b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/python/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..5ccb446532599c01cc3844e5b1b18db06f721002
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/python/__init__.py
@@ -0,0 +1,3 @@
+# Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+
+__version__ = '1.0.0'
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/share/EventAlgorithmsTest_eljob.py b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/share/EventAlgorithmsTest_eljob.py
new file mode 100755
index 0000000000000000000000000000000000000000..efd9a38e6f085e11fe37c43afbd85b069226766a
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/share/EventAlgorithmsTest_eljob.py
@@ -0,0 +1,75 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+#
+# @author Tadej Novak
+
+
+# Read the submission directory as a command line argument. You can
+# extend the list of arguments with your private ones later on.
+import optparse
+parser = optparse.OptionParser()
+parser.add_option( '-d', '--data-type', dest = 'data_type',
+                   action = 'store', type = 'string', default = 'data',
+                   help = 'Type of data to run over. Valid options are data, mc, afii' )
+parser.add_option( '-s', '--submission-dir', dest = 'submission_dir',
+                   action = 'store', type = 'string', default = 'submitDir',
+                   help = 'Submission directory for EventLoop' )
+parser.add_option( '-u', '--unit-test', dest='unit_test',
+                   action = 'store_true', default = False,
+                   help = 'Run the job in "unit test mode"' )
+( options, args ) = parser.parse_args()
+
+# Set up (Py)ROOT.
+import ROOT
+ROOT.xAOD.Init().ignore()
+
+# ideally we'd run over all of them, but we don't have a mechanism to
+# configure per-sample right now
+
+dataType = options.data_type
+
+inputfile = {"data": 'ASG_TEST_FILE_DATA',
+             "mc":   'ASG_TEST_FILE_MC',
+             "afii": 'ASG_TEST_FILE_MC_AFII'}
+
+if dataType not in ["data", "mc", "afii"] :
+    raise ValueError ("invalid data type: " + dataType)
+
+# Set up the sample handler object. See comments from the C++ macro
+# for the details about these lines.
+import os
+sh = ROOT.SH.SampleHandler()
+sh.setMetaString( 'nc_tree', 'CollectionTree' )
+sample = ROOT.SH.SampleLocal (dataType)
+sample.add (os.getenv (inputfile[dataType]))
+sh.add (sample)
+sh.printContent()
+
+# Create an EventLoop job.
+job = ROOT.EL.Job()
+job.sampleHandler( sh )
+job.options().setDouble( ROOT.EL.Job.optMaxEvents, 500 )
+
+from AsgAnalysisAlgorithms.AsgAnalysisAlgorithmsTest import makeEventAlgorithmsSequence
+algSeq = makeEventAlgorithmsSequence (dataType)
+print algSeq # For debugging
+for alg in algSeq:
+    job.algsAdd( alg )
+    pass
+
+# Set up an output file for the job:
+job.outputAdd( ROOT.EL.OutputStream( 'ANALYSIS' ) )
+
+# Find the right output directory:
+submitDir = options.submission_dir
+if options.unit_test:
+    import os
+    import tempfile
+    submitDir = tempfile.mkdtemp( prefix = 'evenTest_', dir = os.getcwd() )
+    os.rmdir( submitDir )
+    pass
+
+# Run the job using the direct driver.
+driver = ROOT.EL.DirectDriver()
+driver.submit( job, submitDir )
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/share/EventAlgorithmsTest_jobOptions.py b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/share/EventAlgorithmsTest_jobOptions.py
new file mode 100644
index 0000000000000000000000000000000000000000..348981c7f08479e492930eaedd2fd99c565ecdf7
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/share/EventAlgorithmsTest_jobOptions.py
@@ -0,0 +1,44 @@
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+#
+# @author Tadej Novak
+
+# User options, which can be set from command line after a "-" character
+# athena EventAlgorithmsTest_jobOptions.py - --myOption ...
+from AthenaCommon.AthArgumentParser import AthArgumentParser
+athArgsParser = AthArgumentParser()
+athArgsParser.add_argument("--data-type", action = "store", dest = "data_type",
+                           default = "mc",
+                           help = "Type of input to run over. Valid options are 'data', 'mc', 'afii'")
+athArgs = athArgsParser.parse_args()
+
+dataType = athArgs.data_type
+if not dataType in ["data", "mc", "afii"] :
+    raise Exception ("invalid data type: " + dataType)
+
+print("Running on data type: " + dataType)
+
+inputfile = {"data": 'ASG_TEST_FILE_DATA',
+             "mc":   'ASG_TEST_FILE_MC',
+             "afii": 'ASG_TEST_FILE_MC_AFII'}
+
+# Set up the reading of the input file:
+import AthenaRootComps.ReadAthenaxAODHybrid
+theApp.EvtMax = 500
+testFile = os.getenv ( inputfile[dataType] )
+svcMgr.EventSelector.InputCollections = [testFile]
+
+from AsgAnalysisAlgorithms.AsgAnalysisAlgorithmsTest import makeEventAlgorithmsSequence
+algSeq = makeEventAlgorithmsSequence (dataType)
+print algSeq # For debugging
+
+# Add all algorithms from the sequence to the job.
+athAlgSeq += algSeq
+
+# Set up a histogram output file for the job:
+ServiceMgr += CfgMgr.THistSvc()
+ServiceMgr.THistSvc.Output += [
+    "ANALYSIS DATAFILE='EventAlgorithmsTest." + dataType + ".hist.root' OPT='RECREATE'"
+    ]
+
+# Reduce the printout from Athena:
+include( "AthAnalysisBaseComps/SuppressLogging.py" )
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/share/GeneratorAlgorithmsTest_eljob.py b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/share/GeneratorAlgorithmsTest_eljob.py
new file mode 100755
index 0000000000000000000000000000000000000000..2fff23fdec478b3167b3b0ead5892173ec57ce78
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/share/GeneratorAlgorithmsTest_eljob.py
@@ -0,0 +1,74 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+#
+# @author Tadej Novak
+
+
+# Read the submission directory as a command line argument. You can
+# extend the list of arguments with your private ones later on.
+import optparse
+parser = optparse.OptionParser()
+parser.add_option( '-d', '--data-type', dest = 'data_type',
+                   action = 'store', type = 'string', default = 'mc',
+                   help = 'Type of data to run over. Valid options are mc, afii' )
+parser.add_option( '-s', '--submission-dir', dest = 'submission_dir',
+                   action = 'store', type = 'string', default = 'submitDir',
+                   help = 'Submission directory for EventLoop' )
+parser.add_option( '-u', '--unit-test', dest='unit_test',
+                   action = 'store_true', default = False,
+                   help = 'Run the job in "unit test mode"' )
+( options, args ) = parser.parse_args()
+
+# Set up (Py)ROOT.
+import ROOT
+ROOT.xAOD.Init().ignore()
+
+# ideally we'd run over all of them, but we don't have a mechanism to
+# configure per-sample right now
+
+dataType = options.data_type
+
+inputfile = {"mc":   'ASG_TEST_FILE_MC',
+             "afii": 'ASG_TEST_FILE_MC_AFII'}
+
+if dataType not in ["mc", "afii"] :
+    raise ValueError ("invalid data type: " + dataType)
+
+# Set up the sample handler object. See comments from the C++ macro
+# for the details about these lines.
+import os
+sh = ROOT.SH.SampleHandler()
+sh.setMetaString( 'nc_tree', 'CollectionTree' )
+sample = ROOT.SH.SampleLocal (dataType)
+sample.add (os.getenv (inputfile[dataType]))
+sh.add (sample)
+sh.printContent()
+
+# Create an EventLoop job.
+job = ROOT.EL.Job()
+job.sampleHandler( sh )
+job.options().setDouble( ROOT.EL.Job.optMaxEvents, 500 )
+
+from AsgAnalysisAlgorithms.AsgAnalysisAlgorithmsTest import makeGeneratorAlgorithmsSequence
+algSeq = makeGeneratorAlgorithmsSequence (dataType)
+print algSeq # For debugging
+for alg in algSeq:
+    job.algsAdd( alg )
+    pass
+
+# Set up an output file for the job:
+job.outputAdd( ROOT.EL.OutputStream( 'ANALYSIS' ) )
+
+# Find the right output directory:
+submitDir = options.submission_dir
+if options.unit_test:
+    import os
+    import tempfile
+    submitDir = tempfile.mkdtemp( prefix = 'genTest_', dir = os.getcwd() )
+    os.rmdir( submitDir )
+    pass
+
+# Run the job using the direct driver.
+driver = ROOT.EL.DirectDriver()
+driver.submit( job, submitDir )
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/share/GeneratorAlgorithmsTest_jobOptions.py b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/share/GeneratorAlgorithmsTest_jobOptions.py
new file mode 100644
index 0000000000000000000000000000000000000000..f1c19a2b1063fb4dfac78a1721bcc8c5a0293b98
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/share/GeneratorAlgorithmsTest_jobOptions.py
@@ -0,0 +1,43 @@
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+#
+# @author Tadej Novak
+
+# User options, which can be set from command line after a "-" character
+# athena OverlapAlgorithmsTest_jobOptions.py - --myOption ...
+from AthenaCommon.AthArgumentParser import AthArgumentParser
+athArgsParser = AthArgumentParser()
+athArgsParser.add_argument("--data-type", action = "store", dest = "data_type",
+                           default = "mc",
+                           help = "Type of input to run over. Valid options are 'mc', 'afii'")
+athArgs = athArgsParser.parse_args()
+
+dataType = athArgs.data_type
+if not dataType in ["mc", "afii"] :
+    raise Exception ("invalid data type: " + dataType)
+
+print("Running on data type: " + dataType)
+
+inputfile = {"mc":   'ASG_TEST_FILE_MC',
+             "afii": 'ASG_TEST_FILE_MC_AFII'}
+
+# Set up the reading of the input file:
+import AthenaRootComps.ReadAthenaxAODHybrid
+theApp.EvtMax = 500
+testFile = os.getenv ( inputfile[dataType] )
+svcMgr.EventSelector.InputCollections = [testFile]
+
+from AsgAnalysisAlgorithms.AsgAnalysisAlgorithmsTest import makeGeneratorAlgorithmsSequence
+algSeq = makeGeneratorAlgorithmsSequence (dataType)
+print algSeq # For debugging
+
+# Add all algorithms from the sequence to the job.
+athAlgSeq += algSeq
+
+# Set up a histogram output file for the job:
+ServiceMgr += CfgMgr.THistSvc()
+ServiceMgr.THistSvc.Output += [
+    "ANALYSIS DATAFILE='GeneratorAlgorithmsTest." + dataType + ".hist.root' OPT='RECREATE'"
+]
+
+# Reduce the printout from Athena:
+include( "AthAnalysisBaseComps/SuppressLogging.py" )
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/share/OverlapAlgorithmsTest_eljob.py b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/share/OverlapAlgorithmsTest_eljob.py
new file mode 100755
index 0000000000000000000000000000000000000000..a213be63996c107fb5039dd723c5701a854ca331
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/share/OverlapAlgorithmsTest_eljob.py
@@ -0,0 +1,76 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+#
+# @author Nils Krumnack
+
+
+# Read the submission directory as a command line argument. You can
+# extend the list of arguments with your private ones later on.
+import optparse
+parser = optparse.OptionParser()
+parser.add_option( '-d', '--data-type', dest = 'data_type',
+                   action = 'store', type = 'string', default = 'data',
+                   help = 'Type of data to run over. Valid options are data, mc, afii' )
+parser.add_option( '-s', '--submission-dir', dest = 'submission_dir',
+                   action = 'store', type = 'string', default = 'submitDir',
+                   help = 'Submission directory for EventLoop' )
+parser.add_option( '-u', '--unit-test', dest='unit_test',
+                   action = 'store_true', default = False,
+                   help = 'Run the job in "unit test mode"' )
+( options, args ) = parser.parse_args()
+
+# Set up (Py)ROOT.
+import ROOT
+ROOT.xAOD.Init().ignore()
+
+# ideally we'd run over all of them, but we don't have a mechanism to
+# configure per-sample right now
+
+dataType = options.data_type
+
+inputfile = {"data": 'ASG_TEST_FILE_DATA',
+             "mc":   'ASG_TEST_FILE_MC',
+             "afii": 'ASG_TEST_FILE_MC_AFII'}
+
+if dataType not in ["data", "mc", "afii"] :
+    raise ValueError ("invalid data type: " + dataType)
+
+# Set up the sample handler object. See comments from the C++ macro
+# for the details about these lines.
+import os
+sh = ROOT.SH.SampleHandler()
+sh.setMetaString( 'nc_tree', 'CollectionTree' )
+sample = ROOT.SH.SampleLocal (dataType)
+sample.add (os.getenv (inputfile[dataType]))
+sh.add (sample)
+sh.printContent()
+
+# Create an EventLoop job.
+job = ROOT.EL.Job()
+job.sampleHandler( sh )
+job.options().setDouble( ROOT.EL.Job.optMaxEvents, 500 )
+
+from AsgAnalysisAlgorithms.AsgAnalysisAlgorithmsTest import makeOverlapSequence
+algSeq = makeOverlapSequence (dataType)
+print algSeq # For debugging
+for alg in algSeq :
+    job.algsAdd( alg )
+    pass
+
+
+# Set up an output file for the job:
+job.outputAdd( ROOT.EL.OutputStream( 'ANALYSIS' ) )
+
+# Find the right output directory:
+submitDir = options.submission_dir
+if options.unit_test:
+    import os
+    import tempfile
+    submitDir = tempfile.mkdtemp( prefix = 'overlapTest_', dir = os.getcwd() )
+    os.rmdir( submitDir )
+    pass
+
+# Run the job using the direct driver.
+driver = ROOT.EL.DirectDriver()
+driver.submit( job, submitDir )
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/share/OverlapAlgorithmsTest_jobOptions.py b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/share/OverlapAlgorithmsTest_jobOptions.py
new file mode 100644
index 0000000000000000000000000000000000000000..4e753b0d13576c1254c1e9c9eb84720854d8d647
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/share/OverlapAlgorithmsTest_jobOptions.py
@@ -0,0 +1,44 @@
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+#
+# @author Nils Krumnack
+
+# User options, which can be set from command line after a "-" character
+# athena OverlapAlgorithmsTest_jobOptions.py - --myOption ...
+from AthenaCommon.AthArgumentParser import AthArgumentParser
+athArgsParser = AthArgumentParser()
+athArgsParser.add_argument("--data-type", action = "store", dest = "data_type",
+                           default = "data",
+                           help = "Type of input to run over. Valid options are 'data', 'mc', 'afii'")
+athArgs = athArgsParser.parse_args()
+
+dataType = athArgs.data_type
+if not dataType in ["data", "mc", "afii"] :
+    raise Exception ("invalid data type: " + dataType)
+
+print("Running on data type: " + dataType)
+
+inputfile = {"data": 'ASG_TEST_FILE_DATA',
+             "mc":   'ASG_TEST_FILE_MC',
+             "afii": 'ASG_TEST_FILE_MC_AFII'}
+
+# Set up the reading of the input file:
+import AthenaRootComps.ReadAthenaxAODHybrid
+theApp.EvtMax = 500
+testFile = os.getenv ( inputfile[dataType] )
+svcMgr.EventSelector.InputCollections = [testFile]
+
+from AsgAnalysisAlgorithms.AsgAnalysisAlgorithmsTest import makeOverlapSequence
+algSeq = makeOverlapSequence (dataType)
+print algSeq # For debugging
+
+# Add all algorithms from the sequence to the job.
+athAlgSeq += algSeq
+
+# Set up a histogram output file for the job:
+ServiceMgr += CfgMgr.THistSvc()
+ServiceMgr.THistSvc.Output += [
+    "ANALYSIS DATAFILE='OverlapAlgorithmsTest." + dataType + ".hist.root' OPT='RECREATE'"
+    ]
+
+# Reduce the printout from Athena:
+include( "AthAnalysisBaseComps/SuppressLogging.py" )
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/src/components/AsgAnalysisAlgorithms_entries.cxx b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/src/components/AsgAnalysisAlgorithms_entries.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..14e40a32afaa936d47565f72dcec16591008ebaf
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/src/components/AsgAnalysisAlgorithms_entries.cxx
@@ -0,0 +1,76 @@
+/*
+  Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#include <GaudiKernel/DeclareFactoryEntries.h>
+
+#include <AsgAnalysisAlgorithms/AsgFlagSelectionTool.h>
+#include <AsgAnalysisAlgorithms/AsgPtEtaSelectionTool.h>
+#include <AsgAnalysisAlgorithms/AsgCutBookkeeperAlg.h>
+#include <AsgAnalysisAlgorithms/AsgEventScaleFactorAlg.h>
+#include <AsgAnalysisAlgorithms/AsgLeptonTrackSelectionAlg.h>
+#include <AsgAnalysisAlgorithms/AsgOriginalObjectLinkAlg.h>
+#include <AsgAnalysisAlgorithms/AsgSelectionAlg.h>
+#include <AsgAnalysisAlgorithms/AsgViewFromSelectionAlg.h>
+#include <AsgAnalysisAlgorithms/AsgxAODNTupleMakerAlg.h>
+#include <AsgAnalysisAlgorithms/EventFlagSelectionAlg.h>
+#include <AsgAnalysisAlgorithms/EventSelectionByObjectFlagAlg.h>
+#include <AsgAnalysisAlgorithms/IsolationCloseByCorrectionAlg.h>
+#include <AsgAnalysisAlgorithms/KinematicHistAlg.h>
+#include <AsgAnalysisAlgorithms/ObjectCutFlowHistAlg.h>
+#include <AsgAnalysisAlgorithms/OverlapRemovalAlg.h>
+#include <AsgAnalysisAlgorithms/PileupReweightingAlg.h>
+#include <AsgAnalysisAlgorithms/PMGTruthWeightAlg.h>
+#include <AsgAnalysisAlgorithms/SysListDumperAlg.h>
+#include <AsgAnalysisAlgorithms/SysListLoaderAlg.h>
+#include <AsgAnalysisAlgorithms/TreeFillerAlg.h>
+#include <AsgAnalysisAlgorithms/TreeMakerAlg.h>
+
+DECLARE_NAMESPACE_TOOL_FACTORY (CP, AsgFlagSelectionTool)
+DECLARE_NAMESPACE_TOOL_FACTORY (CP, AsgPtEtaSelectionTool)
+DECLARE_NAMESPACE_ALGORITHM_FACTORY (CP, AsgCutBookkeeperAlg)
+DECLARE_NAMESPACE_ALGORITHM_FACTORY (CP, AsgEventScaleFactorAlg)
+DECLARE_NAMESPACE_ALGORITHM_FACTORY (CP, AsgLeptonTrackSelectionAlg)
+DECLARE_NAMESPACE_ALGORITHM_FACTORY (CP, AsgOriginalObjectLinkAlg)
+DECLARE_NAMESPACE_ALGORITHM_FACTORY (CP, AsgSelectionAlg)
+DECLARE_NAMESPACE_ALGORITHM_FACTORY (CP, AsgViewFromSelectionAlg)
+DECLARE_NAMESPACE_ALGORITHM_FACTORY (CP, AsgxAODNTupleMakerAlg)
+DECLARE_NAMESPACE_ALGORITHM_FACTORY (CP, EventFlagSelectionAlg)
+DECLARE_NAMESPACE_ALGORITHM_FACTORY (CP, EventSelectionByObjectFlagAlg)
+DECLARE_NAMESPACE_ALGORITHM_FACTORY (CP, IsolationCloseByCorrectionAlg)
+DECLARE_NAMESPACE_ALGORITHM_FACTORY (CP, KinematicHistAlg)
+DECLARE_NAMESPACE_ALGORITHM_FACTORY (CP, ObjectCutFlowHistAlg)
+DECLARE_NAMESPACE_ALGORITHM_FACTORY (CP, OverlapRemovalAlg)
+DECLARE_NAMESPACE_ALGORITHM_FACTORY (CP, PileupReweightingAlg)
+DECLARE_NAMESPACE_ALGORITHM_FACTORY (CP, PMGTruthWeightAlg)
+DECLARE_NAMESPACE_ALGORITHM_FACTORY (CP, SysListDumperAlg)
+DECLARE_NAMESPACE_ALGORITHM_FACTORY (CP, SysListLoaderAlg)
+DECLARE_NAMESPACE_ALGORITHM_FACTORY (CP, TreeFillerAlg)
+DECLARE_NAMESPACE_ALGORITHM_FACTORY (CP, TreeMakerAlg)
+
+DECLARE_FACTORY_ENTRIES(AsgAnalysisAlgorithms) {
+  DECLARE_NAMESPACE_ALGTOOL (CP, AsgFlagSelectionTool)
+  DECLARE_NAMESPACE_ALGTOOL (CP, AsgPtEtaSelectionTool)
+  DECLARE_NAMESPACE_ALGORITHM (CP, AsgCutBookkeeperAlg)
+  DECLARE_NAMESPACE_ALGORITHM (CP, AsgEventScaleFactorAlg)
+  DECLARE_NAMESPACE_ALGORITHM (CP, AsgLeptonTrackSelectionAlg)
+  DECLARE_NAMESPACE_ALGORITHM (CP, AsgOriginalObjectLinkAlg)
+  DECLARE_NAMESPACE_ALGORITHM (CP, AsgSelectionAlg)
+  DECLARE_NAMESPACE_ALGORITHM (CP, AsgViewFromSelectionAlg)
+  DECLARE_NAMESPACE_ALGORITHM (CP, AsgxAODNTupleMakerAlg)
+  DECLARE_NAMESPACE_ALGORITHM (CP, EventFlagSelectionAlg)
+  DECLARE_NAMESPACE_ALGORITHM (CP, EventSelectionByObjectFlagAlg)
+  DECLARE_NAMESPACE_ALGORITHM (CP, IsolationCloseByCorrectionAlg)
+  DECLARE_NAMESPACE_ALGORITHM (CP, KinematicHistAlg)
+  DECLARE_NAMESPACE_ALGORITHM (CP, ObjectCutFlowHistAlg)
+  DECLARE_NAMESPACE_ALGORITHM (CP, OverlapRemovalAlg)
+  DECLARE_NAMESPACE_ALGORITHM (CP, PileupReweightingAlg)
+  DECLARE_NAMESPACE_ALGORITHM (CP, PMGTruthWeightAlg)
+  DECLARE_NAMESPACE_ALGORITHM (CP, SysListDumperAlg)
+  DECLARE_NAMESPACE_ALGORITHM (CP, SysListLoaderAlg)
+  DECLARE_NAMESPACE_ALGORITHM (CP, TreeFillerAlg)
+  DECLARE_NAMESPACE_ALGORITHM (CP, TreeMakerAlg)
+}
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/src/components/AsgAnalysisAlgorithms_load.cxx b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/src/components/AsgAnalysisAlgorithms_load.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..d9df74517d2afe019c514713e128a740f6e8a25c
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/src/components/AsgAnalysisAlgorithms_load.cxx
@@ -0,0 +1,10 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#include "GaudiKernel/LoadFactoryEntries.h"
+
+LOAD_FACTORY_ENTRIES(AsgAnalysisAlgorithms)
diff --git a/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/CMakeLists.txt b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..61b27027550c137b8154b169727b8ad21f38e5b0
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/CMakeLists.txt
@@ -0,0 +1,65 @@
+# Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+#
+# @author Nils Krumnack
+
+
+# The name of the package:
+atlas_subdir( EgammaAnalysisAlgorithms )
+
+# The package's dependencies:
+atlas_depends_on_subdirs(
+   PUBLIC
+   Event/xAOD/xAODEgamma
+   PhysicsAnalysis/Algorithms/SelectionHelpers
+   PhysicsAnalysis/Algorithms/SystematicsHandles
+   PhysicsAnalysis/AnalysisCommon/IsolationSelection
+   PhysicsAnalysis/D3PDTools/AnaAlgorithm
+   PhysicsAnalysis/ElectronPhotonID/IsolationCorrections
+   PhysicsAnalysis/Interfaces/EgammaAnalysisInterfaces )
+
+atlas_add_library( EgammaAnalysisAlgorithmsLib
+   EgammaAnalysisAlgorithms/*.h EgammaAnalysisAlgorithms/*.icc Root/*.cxx
+   PUBLIC_HEADERS EgammaAnalysisAlgorithms
+   LINK_LIBRARIES xAODEgamma SelectionHelpersLib SystematicsHandlesLib
+   IsolationSelectionLib AnaAlgorithmLib IsolationCorrectionsLib
+   EgammaAnalysisInterfacesLib )
+
+atlas_add_dictionary( EgammaAnalysisAlgorithmsDict
+   EgammaAnalysisAlgorithms/EgammaAnalysisAlgorithmsDict.h
+   EgammaAnalysisAlgorithms/selection.xml
+   LINK_LIBRARIES EgammaAnalysisAlgorithmsLib )
+
+if( NOT XAOD_STANDALONE )
+   atlas_add_component( EgammaAnalysisAlgorithms
+      src/*.h src/*.cxx src/components/*.cxx
+      LINK_LIBRARIES GaudiKernel EgammaAnalysisAlgorithmsLib )
+endif()
+
+atlas_install_python_modules( python/*.py )
+atlas_install_joboptions( share/*_jobOptions.py )
+atlas_install_scripts( share/*_eljob.py )
+
+if( XAOD_STANDALONE )
+   atlas_add_test( testJobData
+      SCRIPT EgammaAnalysisAlgorithmsTest_eljob.py --data-type data --unit-test
+      PROPERTIES TIMEOUT 600 )
+   atlas_add_test( testJobFullSim
+      SCRIPT EgammaAnalysisAlgorithmsTest_eljob.py --data-type mc --unit-test
+      PROPERTIES TIMEOUT 600 )
+   atlas_add_test( testJobFastSim
+      SCRIPT EgammaAnalysisAlgorithmsTest_eljob.py --data-type afii --unit-test
+      PROPERTIES TIMEOUT 600 )
+else()
+   atlas_add_test( testJobData
+      SCRIPT athena.py
+      EgammaAnalysisAlgorithms/EgammaAnalysisAlgorithmsTest_jobOptions.py - --data-type data
+      PROPERTIES TIMEOUT 600 )
+   atlas_add_test( testJobFullSim
+      SCRIPT athena.py
+      EgammaAnalysisAlgorithms/EgammaAnalysisAlgorithmsTest_jobOptions.py - --data-type mc
+      PROPERTIES TIMEOUT 600 )
+   atlas_add_test( testJobFastSim
+      SCRIPT athena.py
+      EgammaAnalysisAlgorithms/EgammaAnalysisAlgorithmsTest_jobOptions.py - --data-type afii
+      PROPERTIES TIMEOUT 600 )
+endif()
diff --git a/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/EgammaAnalysisAlgorithms/CopyHelpers.h b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/EgammaAnalysisAlgorithms/CopyHelpers.h
new file mode 100644
index 0000000000000000000000000000000000000000..fecb243d040131fba2678ef4931c7c33e1ec6124
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/EgammaAnalysisAlgorithms/CopyHelpers.h
@@ -0,0 +1,50 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+#ifndef EGAMMA_ANALYSIS_ALGORITHMS__COPY_HELPERS_H
+#define EGAMMA_ANALYSIS_ALGORITHMS__COPY_HELPERS_H
+
+#include <SystematicsHandles/CopyHelpers.h>
+
+#include <xAODEgamma/EgammaContainer.h>
+
+namespace CP
+{
+  namespace detail
+  {
+    template<>
+    struct ShallowCopy<xAOD::EgammaContainer>
+    {
+      /// \brief the type of the event store we use
+    public:
+      typedef std::decay<decltype(*((EL::AnaAlgorithm*)0)->evtStore())>::type StoreType;
+
+      static StatusCode
+      getCopy (MsgStream& msgStream, StoreType& store,
+               xAOD::EgammaContainer*& object,
+               const xAOD::EgammaContainer *inputObject,
+               const std::string& outputName, const std::string& auxName)
+      {
+        const auto msg = [&] (MSG::Level lvl) -> MsgStream& {msgStream << lvl; return msgStream;};
+
+        xAOD::IParticleContainer *subobject = nullptr;
+        if (!ShallowCopy<xAOD::IParticleContainer>::getCopy (msgStream, store, subobject, inputObject, outputName, auxName).isSuccess())
+          return StatusCode::FAILURE;
+        if (!(object = dynamic_cast<xAOD::EgammaContainer*>(subobject)))
+        {
+          ANA_MSG_ERROR ("copy of EgammaContainer is not an EgammaContainer"); 
+          ANA_MSG_ERROR ("check logic in CopyHelpers");
+          return StatusCode::FAILURE;
+        }
+        return StatusCode::SUCCESS;
+      }
+    };
+  }
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/EgammaAnalysisAlgorithms/EgammaAnalysisAlgorithmsDict.h b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/EgammaAnalysisAlgorithms/EgammaAnalysisAlgorithmsDict.h
new file mode 100644
index 0000000000000000000000000000000000000000..6873d4560ca86ef2efaab2098ba4405ec1a43e7a
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/EgammaAnalysisAlgorithms/EgammaAnalysisAlgorithmsDict.h
@@ -0,0 +1,19 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef EGAMMA_ANALYSIS_ALGORITHMS__EGAMMA_ANALYSIS_ALGORITHMS_DICT_H
+#define EGAMMA_ANALYSIS_ALGORITHMS__EGAMMA_ANALYSIS_ALGORITHMS_DICT_H
+
+#include <EgammaAnalysisAlgorithms/EgammaCalibrationAndSmearingAlg.h>
+#include <EgammaAnalysisAlgorithms/EgammaIsGoodOQSelectionTool.h>
+#include <EgammaAnalysisAlgorithms/EgammaIsolationCorrectionAlg.h>
+#include <EgammaAnalysisAlgorithms/EgammaIsolationSelectionAlg.h>
+#include <EgammaAnalysisAlgorithms/ElectronEfficiencyCorrectionAlg.h>
+#include <EgammaAnalysisAlgorithms/PhotonEfficiencyCorrectionAlg.h>
+#include <EgammaAnalysisAlgorithms/PhotonShowerShapeFudgeAlg.h>
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/EgammaAnalysisAlgorithms/EgammaCalibrationAndSmearingAlg.h b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/EgammaAnalysisAlgorithms/EgammaCalibrationAndSmearingAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..bd361e9a53652ce0bd72ee5af02693decd908399
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/EgammaAnalysisAlgorithms/EgammaCalibrationAndSmearingAlg.h
@@ -0,0 +1,65 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+#ifndef EGAMMA_ANALYSIS_ALGORITHMS__EGAMMA_CALIBRATION_AND_SMEARING_ALG_H
+#define EGAMMA_ANALYSIS_ALGORITHMS__EGAMMA_CALIBRATION_AND_SMEARING_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <EgammaAnalysisInterfaces/IEgammaCalibrationAndSmearingTool.h>
+#include <EgammaAnalysisAlgorithms/CopyHelpers.h>
+#include <SelectionHelpers/SelectionReadHandle.h>
+#include <SelectionHelpers/OutOfValidityHelper.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+
+namespace CP
+{
+  /// \brief an algorithm for calling \ref
+  /// CP::IEgammaCalibrationAndSmearingTool
+
+  class EgammaCalibrationAndSmearingAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    EgammaCalibrationAndSmearingAlg (const std::string& name, 
+                                     ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+    
+
+
+    /// \brief the smearing tool
+  private:
+    ToolHandle<CP::IEgammaCalibrationAndSmearingTool> m_calibrationAndSmearingTool;
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the egamma collection we run on
+  private:
+    SysCopyHandle<xAOD::EgammaContainer> m_egammaHandle {
+      this, "egammas", "Electrons", "the egamma collection to run on"};
+
+    /// \brief the preselection we apply to our input
+  private:
+    SelectionReadHandle m_preselection {
+      this, "preselection", "", "the preselection to apply"};
+
+    /// \brief the helper for OutOfValidity results
+  private:
+    OutOfValidityHelper m_outOfValidity {this};
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/EgammaAnalysisAlgorithms/EgammaIsGoodOQSelectionTool.h b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/EgammaAnalysisAlgorithms/EgammaIsGoodOQSelectionTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..179e9b7fac68d5b9f26c772302b8722e880533eb
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/EgammaAnalysisAlgorithms/EgammaIsGoodOQSelectionTool.h
@@ -0,0 +1,71 @@
+// Dear emacs, this is -*- c++ -*-
+//
+// Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+//
+#ifndef EGAMMAANALYSISALGORITHMS_EGAMMAISGOODOQSELECTIONTOOL_H
+#define EGAMMAANALYSISALGORITHMS_EGAMMAISGOODOQSELECTIONTOOL_H
+
+// Framework include(s):
+#include "AsgTools/AsgTool.h"
+#include "PATCore/IAsgSelectionTool.h"
+
+namespace CP {
+
+   /// Tool selecting e/gamma objects based on their object quality flags
+   ///
+   /// This tool can be used in tandem with @c CP::AsgSelectionAlg to flag which
+   /// e/gamma objects pass a given object quality requirement. The idea is that
+   /// the mask to use by the tool should be taken from xAODEgamma's dictionary,
+   /// ideally using a PyROOT configuration.
+   ///
+   /// @author Attila Krasznahorkay <Attila.Krasznahorkay@cern.ch>
+   ///
+   class EgammaIsGoodOQSelectionTool final :
+   public asg::AsgTool, virtual public IAsgSelectionTool {
+
+   public:
+      /// Declare the tool's interface to Gaudi
+      ASG_TOOL_CLASS( EgammaIsGoodOQSelectionTool, IAsgSelectionTool )
+
+      /// AsgTool constructor
+      EgammaIsGoodOQSelectionTool( const std::string& name );
+
+      /// @name Interface inherited from @c IAsgSelectionTool
+      /// @{
+
+      /// Get the results for the last object processed
+      virtual const Root::TAccept& getTAccept() const override;
+
+      /// Get the results for a given particle
+      virtual const Root::TAccept&
+      accept( const xAOD::IParticle* part ) const override;
+
+      /// @}
+
+      /// @name Interface inherited from @c asg::AsgTool
+      /// @{
+
+      /// Function initialising the tool
+      virtual StatusCode initialize() override;
+
+      /// @}
+
+   private:
+      /// @name Tool properties
+      /// @{
+
+      /// The mask to require good object quality with
+      int m_mask;
+
+      /// @}
+
+      /// Object handling the e/gamma selection decision
+      mutable Root::TAccept m_accept{ "EgammaOQ" };
+      /// Index of the object quality cut
+      int m_oqCutIndex{ -1 };
+
+   }; // class EgammaIsGoodOQSelectionTool
+
+} // namespace CP
+
+#endif // EGAMMAANALYSISALGORITHMS_EGAMMAISGOODOQSELECTIONTOOL_H
diff --git a/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/EgammaAnalysisAlgorithms/EgammaIsolationCorrectionAlg.h b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/EgammaAnalysisAlgorithms/EgammaIsolationCorrectionAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..9c193c8a20e48f3a9d2be66c37386d808e96745c
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/EgammaAnalysisAlgorithms/EgammaIsolationCorrectionAlg.h
@@ -0,0 +1,65 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+#ifndef EGAMMA_ANALYSIS_ALGORITHMS__EGAMMA_ISOLATION_CORRECTION_ALG_H
+#define EGAMMA_ANALYSIS_ALGORITHMS__EGAMMA_ISOLATION_CORRECTION_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <EgammaAnalysisAlgorithms/CopyHelpers.h>
+#include <IsolationCorrections/IIsolationCorrectionTool.h>
+#include <SelectionHelpers/OutOfValidityHelper.h>
+#include <SelectionHelpers/SelectionReadHandle.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+
+namespace CP
+{
+  /// \brief an algorithm for calling \ref
+  /// CP::IIsolationCorrectionTool
+
+  class EgammaIsolationCorrectionAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    EgammaIsolationCorrectionAlg (const std::string& name, 
+                                     ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+    
+
+
+    /// \brief the smearing tool
+  private:
+    ToolHandle<CP::IIsolationCorrectionTool> m_isolationCorrectionTool;
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the egamma collection we run on
+  private:
+    SysCopyHandle<xAOD::EgammaContainer> m_egammaHandle {
+      this, "egammas", "Electrons", "the egamma collection to run on"};
+
+    /// \brief the preselection we apply to our input
+  private:
+    SelectionReadHandle m_preselection {
+      this, "preselection", "", "the preselection to apply"};
+
+    /// \brief the helper for OutOfValidity results
+  private:
+    OutOfValidityHelper m_outOfValidity {this};
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/EgammaAnalysisAlgorithms/EgammaIsolationSelectionAlg.h b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/EgammaAnalysisAlgorithms/EgammaIsolationSelectionAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..8b4503a8387eefb40355db16b27deb5a0d1e338a
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/EgammaAnalysisAlgorithms/EgammaIsolationSelectionAlg.h
@@ -0,0 +1,73 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+#ifndef EGAMMA_ANALYSIS_ALGORITHMS__EGAMMA_ISOLATION_SELECTION_ALG_H
+#define EGAMMA_ANALYSIS_ALGORITHMS__EGAMMA_ISOLATION_SELECTION_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <IsolationSelection/IIsolationSelectionTool.h>
+#include <EgammaAnalysisAlgorithms/CopyHelpers.h>
+#include <SelectionHelpers/ISelectionAccessor.h>
+#include <SelectionHelpers/OutOfValidityHelper.h>
+#include <SelectionHelpers/SelectionReadHandle.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+
+namespace CP
+{
+  /// \brief an algorithm for calling \ref IIsolationSelectionTool
+
+  class EgammaIsolationSelectionAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    EgammaIsolationSelectionAlg (const std::string& name, 
+                                 ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+    
+
+
+    /// \brief the selection tool
+  private:
+    ToolHandle<IIsolationSelectionTool> m_selectionTool;
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the preselection we apply to our input
+  private:
+    SelectionReadHandle m_preselection {
+      this, "preselection", "", "the preselection to apply"};
+
+    /// \brief the particle continer we run on
+  private:
+    SysCopyHandle<xAOD::EgammaContainer> m_egammasHandle {
+      this, "egammas", "Electrons", "the egamma collection to run on"};
+
+    /// \brief the decoration for the asg selection
+  private:
+    std::string m_selectionDecoration {"isolated"};
+
+    /// \brief the accessor for \ref m_selectionDecoration
+  private:
+    std::unique_ptr<ISelectionAccessor> m_selectionAccessor;
+
+    /// \brief the bits to set for an object failing the preselection
+  private:
+    SelectionType m_setOnFail;
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/EgammaAnalysisAlgorithms/ElectronEfficiencyCorrectionAlg.h b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/EgammaAnalysisAlgorithms/ElectronEfficiencyCorrectionAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..a1fb77b32462b4c632533e83cc7a2da4178b712b
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/EgammaAnalysisAlgorithms/ElectronEfficiencyCorrectionAlg.h
@@ -0,0 +1,70 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef ELECTRON_ANALYSIS_ALGORITHMS__ELECTRON_EFFICIENCY_SCALE_FACTOR_ALG_H
+#define ELECTRON_ANALYSIS_ALGORITHMS__ELECTRON_EFFICIENCY_SCALE_FACTOR_ALG_H
+
+#include <xAODEgamma/ElectronContainer.h>
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <EgammaAnalysisInterfaces/IAsgElectronEfficiencyCorrectionTool.h>
+#include <SelectionHelpers/OutOfValidityHelper.h>
+#include <SelectionHelpers/SelectionReadHandle.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <SystematicsHandles/SysDecorationHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+#include <SystematicsHandles/SysReadHandle.h>
+
+namespace CP
+{
+  /// \brief an algorithm for calling \ref IElectronEfficiencyCorrectionTool
+
+  class ElectronEfficiencyCorrectionAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    ElectronEfficiencyCorrectionAlg (const std::string& name, 
+                                   ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+    
+
+
+    /// \brief the smearing tool
+  private:
+    ToolHandle<IAsgElectronEfficiencyCorrectionTool> m_efficiencyCorrectionTool;
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the electron collection we run on
+  private:
+    SysCopyHandle<xAOD::ElectronContainer> m_electronHandle {
+      this, "electrons", "Electrons", "the electron collection to run on"};
+
+    /// \brief the preselection we apply to our input
+  private:
+    SelectionReadHandle m_preselection {
+      this, "preselection", "", "the preselection to apply"};
+
+    /// \brief the helper for OutOfValidity results
+  private:
+    OutOfValidityHelper m_outOfValidity {this};
+
+    /// \brief the decoration for the electron scale factor
+  private:
+    SysDecorationHandle<float> m_scaleFactorDecoration {
+      this, "scaleFactorDecoration", "", "the decoration for the electron efficiency scale factor"};
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/EgammaAnalysisAlgorithms/PhotonEfficiencyCorrectionAlg.h b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/EgammaAnalysisAlgorithms/PhotonEfficiencyCorrectionAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..c9006d39dedce31c01046a1b7bd70237b65b337d
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/EgammaAnalysisAlgorithms/PhotonEfficiencyCorrectionAlg.h
@@ -0,0 +1,72 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef ELECTRON_ANALYSIS_ALGORITHMS__PHOTON_EFFICIENCY_SCALE_FACTOR_ALG_H
+#define ELECTRON_ANALYSIS_ALGORITHMS__PHOTON_EFFICIENCY_SCALE_FACTOR_ALG_H
+
+#include <xAODEgamma/PhotonContainer.h>
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <EgammaAnalysisInterfaces/IAsgPhotonEfficiencyCorrectionTool.h>
+#include <SelectionHelpers/OutOfValidityHelper.h>
+#include <SelectionHelpers/SelectionReadHandle.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+#include <SystematicsHandles/SysReadHandle.h>
+
+namespace CP
+{
+  /// \brief an algorithm for calling \ref IPhotonEfficiencyCorrectionTool
+
+  class PhotonEfficiencyCorrectionAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    PhotonEfficiencyCorrectionAlg (const std::string& name, 
+                                   ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+    
+
+
+    /// \brief the smearing tool
+  private:
+    ToolHandle<IAsgPhotonEfficiencyCorrectionTool> m_efficiencyCorrectionTool;
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the photon collection we run on
+  private:
+    SysCopyHandle<xAOD::PhotonContainer> m_photonHandle {
+      this, "photons", "Photons", "the photon collection to run on"};
+
+    /// \brief the preselection we apply to our input
+  private:
+    SelectionReadHandle m_preselection {
+      this, "preselection", "", "the preselection to apply"};
+
+    /// \brief the helper for OutOfValidity results
+  private:
+    OutOfValidityHelper m_outOfValidity {this};
+
+    /// \brief the decoration for the photon scale factor
+  private:
+    std::string m_scaleFactorDecoration;
+
+    /// \brief the accessor for \ref m_scaleFactorDecoration
+  private:
+    std::unique_ptr<const SG::AuxElement::Accessor<float> > m_scaleFactorAccessor;
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/EgammaAnalysisAlgorithms/PhotonShowerShapeFudgeAlg.h b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/EgammaAnalysisAlgorithms/PhotonShowerShapeFudgeAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..7ca1165f1840f13e239eef56612784db273de5c9
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/EgammaAnalysisAlgorithms/PhotonShowerShapeFudgeAlg.h
@@ -0,0 +1,66 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+#ifndef EGAMMA_ANALYSIS_ALGORITHMS__ELECTRON_PHOTON_SHOWER_SHAPE_FUDGE_ALG_H
+#define EGAMMA_ANALYSIS_ALGORITHMS__ELECTRON_PHOTON_SHOWER_SHAPE_FUDGE_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <EgammaAnalysisInterfaces/IElectronPhotonShowerShapeFudgeTool.h>
+#include <EgammaAnalysisAlgorithms/CopyHelpers.h>
+#include <SelectionHelpers/OutOfValidityHelper.h>
+#include <SelectionHelpers/SelectionReadHandle.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+#include <xAODEgamma/PhotonContainer.h>
+
+namespace CP
+{
+  /// \brief an algorithm for calling \ref
+  /// IElectronPhotonShowerShapeFudgeTool for photons
+
+  class PhotonShowerShapeFudgeAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    PhotonShowerShapeFudgeAlg (const std::string& name, 
+                                     ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+    
+
+
+    /// \brief the smearing tool
+  private:
+    ToolHandle<IElectronPhotonShowerShapeFudgeTool> m_showerShapeFudgeTool;
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the photon collection we run on
+  private:
+    SysCopyHandle<xAOD::PhotonContainer> m_photonHandle {
+      this, "photons", "Photons", "the photon collection to run on"};
+
+    /// \brief the preselection we apply to our input
+  private:
+    SelectionReadHandle m_preselection {
+      this, "preselection", "", "the preselection to apply"};
+
+    /// \brief the helper for OutOfValidity results
+  private:
+    OutOfValidityHelper m_outOfValidity {this};
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/EgammaAnalysisAlgorithms/selection.xml b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/EgammaAnalysisAlgorithms/selection.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b6f50af36ba3d4d12c880a2f9e86b1a08c9a9534
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/EgammaAnalysisAlgorithms/selection.xml
@@ -0,0 +1,11 @@
+<lcgdict>
+
+   <class name="CP::EgammaCalibrationAndSmearingAlg" />
+   <class name="CP::EgammaIsGoodOQSelectionTool" />
+   <class name="CP::EgammaIsolationCorrectionAlg" />
+   <class name="CP::EgammaIsolationSelectionAlg" />
+   <class name="CP::ElectronEfficiencyCorrectionAlg" />
+   <class name="CP::PhotonEfficiencyCorrectionAlg" />
+   <class name="CP::PhotonShowerShapeFudgeAlg" />
+   
+</lcgdict>
diff --git a/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/Root/CopyHelpers.cxx b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/Root/CopyHelpers.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..0158e1bfd2511120625a6d2a0ec2971940d751a6
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/Root/CopyHelpers.cxx
@@ -0,0 +1,21 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+//
+// includes
+//
+
+#include <EgammaAnalysisAlgorithms/CopyHelpers.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+}
diff --git a/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/Root/EgammaCalibrationAndSmearingAlg.cxx b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/Root/EgammaCalibrationAndSmearingAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..8fe486133a4d01c7165ceed397e53ccaff71f3c0
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/Root/EgammaCalibrationAndSmearingAlg.cxx
@@ -0,0 +1,63 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+//
+// includes
+//
+
+#include <EgammaAnalysisAlgorithms/EgammaCalibrationAndSmearingAlg.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  EgammaCalibrationAndSmearingAlg ::
+  EgammaCalibrationAndSmearingAlg (const std::string& name, 
+                     ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+    , m_calibrationAndSmearingTool ("CP::EgammaCalibrationAndSmearingTool", this)
+  {
+    declareProperty ("calibrationAndSmearingTool", m_calibrationAndSmearingTool, "the smearing tool we apply");
+  }
+
+
+
+  StatusCode EgammaCalibrationAndSmearingAlg ::
+  initialize ()
+  {
+    ANA_CHECK (m_calibrationAndSmearingTool.retrieve());
+    m_systematicsList.addHandle (m_egammaHandle);
+    ANA_CHECK (m_systematicsList.addAffectingSystematics (m_calibrationAndSmearingTool->affectingSystematics()));
+    ANA_CHECK (m_systematicsList.initialize());
+    ANA_CHECK (m_preselection.initialize());
+    ANA_CHECK (m_outOfValidity.initialize());
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode EgammaCalibrationAndSmearingAlg ::
+  execute ()
+  {
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode {
+        ANA_CHECK (m_calibrationAndSmearingTool->applySystematicVariation (sys));
+        xAOD::EgammaContainer *egammas = nullptr;
+        ANA_CHECK (m_egammaHandle.getCopy (egammas, sys));
+        for (xAOD::Egamma *egamma : *egammas)
+        {
+          if (m_preselection.getBool (*egamma))
+          {
+            ANA_CHECK_CORRECTION (m_outOfValidity, *egamma, m_calibrationAndSmearingTool->applyCorrection (*egamma));
+          }
+        }
+        return StatusCode::SUCCESS;
+      });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/Root/EgammaIsGoodOQSelectionTool.cxx b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/Root/EgammaIsGoodOQSelectionTool.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..ba2b504cecca8274120b29dfc87096794860d222
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/Root/EgammaIsGoodOQSelectionTool.cxx
@@ -0,0 +1,67 @@
+//
+// Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+//
+
+// Local include(s):
+#include "EgammaAnalysisAlgorithms/EgammaIsGoodOQSelectionTool.h"
+
+// EDM include(s):
+#include "xAODEgamma/Egamma.h"
+#include "xAODEgamma/EgammaDefs.h"
+
+// System include(s):
+#include <iomanip>
+
+namespace CP {
+
+   EgammaIsGoodOQSelectionTool::
+   EgammaIsGoodOQSelectionTool( const std::string& name )
+   : asg::AsgTool( name ) {
+
+      // Declare the tool's properties.
+      declareProperty( "Mask", m_mask = xAOD::EgammaParameters::ALLOQ,
+                       "Mask to require passing object quality bits with" );
+   }
+
+   const Root::TAccept& EgammaIsGoodOQSelectionTool::getTAccept() const {
+
+      // Return the internal object.
+      return m_accept;
+   }
+
+   const Root::TAccept& EgammaIsGoodOQSelectionTool::
+   accept( const xAOD::IParticle* part ) const {
+
+      // Reset the decision object.
+      m_accept.clear();
+
+      // Cast the particle to an e/gamma type.
+      const xAOD::Egamma* eg = nullptr;
+      if( ( part->type() != xAOD::Type::Electron ) &&
+          ( part->type() != xAOD::Type::Photon ) ) {
+         ATH_MSG_WARNING( "Non-e/gamma object received" );
+         return m_accept;
+      }
+      eg = static_cast< const xAOD::Egamma* >( part );
+
+      // Calculate the decision.
+      m_accept.setCutResult( m_oqCutIndex, eg->isGoodOQ( m_mask ) );
+
+      // Return the internal object.
+      return m_accept;
+   }
+
+   StatusCode EgammaIsGoodOQSelectionTool::initialize() {
+
+      // Tell the user what is going to happen.
+      ATH_MSG_INFO( "Selecting e/gamma objects with OQ mask: 0x"
+                    << std::hex << m_mask );
+
+      // Set up the TAccept object.
+      m_oqCutIndex = m_accept.addCut( "EgammaOQ", "Egamma object quality cut" );
+
+      // Return gracefully.
+      return StatusCode::SUCCESS;
+   }
+
+} // namespace CP
diff --git a/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/Root/EgammaIsolationCorrectionAlg.cxx b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/Root/EgammaIsolationCorrectionAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..c9497dec62c32ed8f7843e6c7d328afba9b9e638
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/Root/EgammaIsolationCorrectionAlg.cxx
@@ -0,0 +1,63 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+//
+// includes
+//
+
+#include <EgammaAnalysisAlgorithms/EgammaIsolationCorrectionAlg.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  EgammaIsolationCorrectionAlg ::
+  EgammaIsolationCorrectionAlg (const std::string& name, 
+                     ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+    , m_isolationCorrectionTool ("CP::IsolationCorrectionTool", this)
+  {
+    declareProperty ("isolationCorrectionTool", m_isolationCorrectionTool, "the smearing tool we apply");
+  }
+
+
+
+  StatusCode EgammaIsolationCorrectionAlg ::
+  initialize ()
+  {
+    ANA_CHECK (m_isolationCorrectionTool.retrieve());
+    m_systematicsList.addHandle (m_egammaHandle);
+    ANA_CHECK (m_systematicsList.addAffectingSystematics (m_isolationCorrectionTool->affectingSystematics()));
+    ANA_CHECK (m_systematicsList.initialize());
+    ANA_CHECK (m_preselection.initialize());
+    ANA_CHECK (m_outOfValidity.initialize());
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode EgammaIsolationCorrectionAlg ::
+  execute ()
+  {
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode {
+        ANA_CHECK (m_isolationCorrectionTool->applySystematicVariation (sys));
+        xAOD::EgammaContainer *egammas = nullptr;
+        ANA_CHECK (m_egammaHandle.getCopy (egammas, sys));
+        for (xAOD::Egamma *egamma : *egammas)
+        {
+          if (m_preselection.getBool (*egamma))
+          {
+            ANA_CHECK_CORRECTION (m_outOfValidity, *egamma, m_isolationCorrectionTool->applyCorrection (*egamma));
+          }
+        }
+        return StatusCode::SUCCESS;
+      });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/Root/EgammaIsolationSelectionAlg.cxx b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/Root/EgammaIsolationSelectionAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..35a77e1e63ecc64bfcec4f7ab610e842cf215e0e
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/Root/EgammaIsolationSelectionAlg.cxx
@@ -0,0 +1,78 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+//
+// includes
+//
+
+#include <EgammaAnalysisAlgorithms/EgammaIsolationSelectionAlg.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  EgammaIsolationSelectionAlg ::
+  EgammaIsolationSelectionAlg (const std::string& name, 
+                     ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+    , m_selectionTool ("", this)
+  {
+    declareProperty ("selectionTool", m_selectionTool, "the selection tool we apply");
+    declareProperty ("selectionDecoration", m_selectionDecoration, "the decoration for the asg selection");
+  }
+
+
+
+  StatusCode EgammaIsolationSelectionAlg ::
+  initialize ()
+  {
+    if (m_selectionDecoration.empty())
+    {
+      ANA_MSG_ERROR ("no selection decoration name set");
+      return StatusCode::FAILURE;
+    }
+    ANA_CHECK (makeSelectionAccessor (m_selectionDecoration, m_selectionAccessor));
+    ANA_CHECK (m_selectionTool.retrieve());
+      
+    m_systematicsList.addHandle (m_egammasHandle);
+    ANA_CHECK (m_systematicsList.initialize());
+    ANA_CHECK (m_preselection.initialize());
+
+    Root::TAccept blankAccept = m_selectionTool->getObjTAccept();
+    // Just in case this isn't initially set up as a failure clear it this one
+    // time. This only calls reset on the bitset
+    blankAccept.clear();
+    m_setOnFail = selectionFromAccept(blankAccept);
+
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode EgammaIsolationSelectionAlg ::
+  execute ()
+  {
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode {
+        xAOD::EgammaContainer *egammas = nullptr;
+        ANA_CHECK (m_egammasHandle.getCopy (egammas, sys));
+        for (xAOD::Egamma *egamma : *egammas)
+        {
+          if (m_preselection.getBool (*egamma))
+          {
+            m_selectionAccessor->setBits
+              (*egamma, selectionFromAccept (m_selectionTool->accept (*egamma)));
+          } else {
+            m_selectionAccessor->setBits (*egamma, m_setOnFail);
+          }
+        }
+        return StatusCode::SUCCESS;
+      });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/Root/ElectronEfficiencyCorrectionAlg.cxx b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/Root/ElectronEfficiencyCorrectionAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..bf2d404d9386f964e857d5b3da632f2d07b6f005
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/Root/ElectronEfficiencyCorrectionAlg.cxx
@@ -0,0 +1,76 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+//
+// includes
+//
+
+#include <EgammaAnalysisAlgorithms/ElectronEfficiencyCorrectionAlg.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  ElectronEfficiencyCorrectionAlg ::
+  ElectronEfficiencyCorrectionAlg (const std::string& name, 
+                     ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+    , m_efficiencyCorrectionTool ("AsgElectronEfficiencyCorrectionTool", this)
+  {
+    declareProperty ("efficiencyCorrectionTool", m_efficiencyCorrectionTool, "the calibration and smearing tool we apply");
+  }
+
+
+
+  StatusCode ElectronEfficiencyCorrectionAlg ::
+  initialize ()
+  {
+    if (m_scaleFactorDecoration.empty())
+    {
+      ANA_MSG_ERROR ("no scale factor decoration name set");
+      return StatusCode::FAILURE;
+    }
+
+    ANA_CHECK (m_efficiencyCorrectionTool.retrieve());
+    m_systematicsList.addHandle (m_electronHandle);
+    ANA_CHECK (m_systematicsList.addAffectingSystematics (m_efficiencyCorrectionTool->affectingSystematics()));
+    ANA_CHECK (m_systematicsList.initialize());
+    ANA_CHECK (m_preselection.initialize());
+    ANA_CHECK (m_outOfValidity.initialize());
+
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode ElectronEfficiencyCorrectionAlg ::
+  execute ()
+  {
+    ANA_CHECK (m_scaleFactorDecoration.preExecute (m_systematicsList));
+
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode {
+        ANA_CHECK (m_efficiencyCorrectionTool->applySystematicVariation (sys));
+        xAOD::ElectronContainer *electrons = nullptr;
+        ANA_CHECK (m_electronHandle.getCopy (electrons, sys));
+        for (xAOD::Electron *electron : *electrons)
+        {
+          if (m_preselection.getBool (*electron))
+          {
+            double sf = 0;
+            ANA_CHECK_CORRECTION (m_outOfValidity, *electron, m_efficiencyCorrectionTool->getEfficiencyScaleFactor (*electron, sf));
+            m_scaleFactorDecoration.set (*electron, sf, sys);
+          } else {
+            m_scaleFactorDecoration.set (*electron, invalidScaleFactor(), sys);
+          }
+        }
+        return StatusCode::SUCCESS;
+      });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/Root/PhotonEfficiencyCorrectionAlg.cxx b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/Root/PhotonEfficiencyCorrectionAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..163a27762e0e0011f956b8072b6065ffd0309327
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/Root/PhotonEfficiencyCorrectionAlg.cxx
@@ -0,0 +1,73 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+//
+// includes
+//
+
+#include <EgammaAnalysisAlgorithms/PhotonEfficiencyCorrectionAlg.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  PhotonEfficiencyCorrectionAlg ::
+  PhotonEfficiencyCorrectionAlg (const std::string& name, 
+                     ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+    , m_efficiencyCorrectionTool ("AsgPhotonEfficiencyCorrectionTool", this)
+  {
+    declareProperty ("efficiencyCorrectionTool", m_efficiencyCorrectionTool, "the calibration and smearing tool we apply");
+    declareProperty ("scaleFactorDecoration", m_scaleFactorDecoration, "the decoration for the photon scale factor");
+  }
+
+
+
+  StatusCode PhotonEfficiencyCorrectionAlg ::
+  initialize ()
+  {
+    if (m_scaleFactorDecoration.empty())
+    {
+      ANA_MSG_ERROR ("no scale factor decoration name set");
+      return StatusCode::FAILURE;
+    }
+    m_scaleFactorAccessor = std::make_unique<SG::AuxElement::Accessor<float> > (m_scaleFactorDecoration);
+
+    ANA_CHECK (m_efficiencyCorrectionTool.retrieve());
+    m_systematicsList.addHandle (m_photonHandle);
+    ANA_CHECK (m_systematicsList.addAffectingSystematics (m_efficiencyCorrectionTool->affectingSystematics()));
+    ANA_CHECK (m_systematicsList.initialize());
+    ANA_CHECK (m_preselection.initialize());
+    ANA_CHECK (m_outOfValidity.initialize());
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode PhotonEfficiencyCorrectionAlg ::
+  execute ()
+  {
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode {
+        ANA_CHECK (m_efficiencyCorrectionTool->applySystematicVariation (sys));
+        xAOD::PhotonContainer *photons = nullptr;
+        ANA_CHECK (m_photonHandle.getCopy (photons, sys));
+        for (xAOD::Photon *photon : *photons)
+        {
+          if (m_preselection.getBool (*photon))
+          {
+            double sf = 0;
+            ANA_CHECK_CORRECTION (m_outOfValidity, *photon, m_efficiencyCorrectionTool->getEfficiencyScaleFactor (*photon, sf));
+            (*m_scaleFactorAccessor) (*photon) = sf;
+          }
+        }
+        return StatusCode::SUCCESS;
+      });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/Root/PhotonShowerShapeFudgeAlg.cxx b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/Root/PhotonShowerShapeFudgeAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..b7b9a8a4126f5b7a0d0b2a2e0abeaed307f77b2c
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/Root/PhotonShowerShapeFudgeAlg.cxx
@@ -0,0 +1,61 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+//
+// includes
+//
+
+#include <EgammaAnalysisAlgorithms/PhotonShowerShapeFudgeAlg.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  PhotonShowerShapeFudgeAlg ::
+  PhotonShowerShapeFudgeAlg (const std::string& name, 
+                     ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+    , m_showerShapeFudgeTool ("ElectronPhotonShowerShapeFudgeTool", this)
+  {
+    declareProperty ("showerShapeFudgeTool", m_showerShapeFudgeTool, "the smearing tool we apply");
+  }
+
+
+
+  StatusCode PhotonShowerShapeFudgeAlg ::
+  initialize ()
+  {
+    ANA_CHECK (m_showerShapeFudgeTool.retrieve());
+    m_systematicsList.addHandle (m_photonHandle);
+    ANA_CHECK (m_systematicsList.initialize());
+    ANA_CHECK (m_preselection.initialize());
+    ANA_CHECK (m_outOfValidity.initialize());
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode PhotonShowerShapeFudgeAlg ::
+  execute ()
+  {
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode {
+        xAOD::PhotonContainer *photons = nullptr;
+        ANA_CHECK (m_photonHandle.getCopy (photons, sys));
+        for (xAOD::Photon *photon : *photons)
+        {
+          if (m_preselection.getBool (*photon))
+          {
+            ANA_CHECK_CORRECTION (m_outOfValidity, *photon, m_showerShapeFudgeTool->applyCorrection (*photon));
+          }
+        }
+        return StatusCode::SUCCESS;
+      });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/python/EgammaAnalysisAlgorithmsTest.py b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/python/EgammaAnalysisAlgorithmsTest.py
new file mode 100644
index 0000000000000000000000000000000000000000..eb8e4b901bcc38ab674d98db8b8ca17fb080f5df
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/python/EgammaAnalysisAlgorithmsTest.py
@@ -0,0 +1,40 @@
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+
+from AnaAlgorithm.AlgSequence import AlgSequence
+from AnaAlgorithm.DualUseConfig import createAlgorithm
+
+def makeSequence (dataType) :
+    algSeq = AlgSequence()
+
+    # Create the algorithm's configuration. Note that we'll be able to add
+    # algorithm property settings here later on.
+    alg = createAlgorithm( 'CP::SysListLoaderAlg', 'SysLoaderAlg' )
+    alg.sigmaRecommended = 1
+    algSeq += alg
+
+    # Include, and then set up the pileup analysis sequence:
+    from AsgAnalysisAlgorithms.PileupAnalysisSequence import \
+        makePileupAnalysisSequence
+    pileupSequence = makePileupAnalysisSequence( dataType )
+    pileupSequence.configure( inputName = 'EventInfo', outputName = 'EventInfo_%SYS%' )
+    algSeq += pileupSequence
+
+    # Include, and then set up the electron analysis sequence:
+    from EgammaAnalysisAlgorithms.ElectronAnalysisSequence import \
+        makeElectronAnalysisSequence
+    electronSequence = makeElectronAnalysisSequence( dataType, 'LooseLHElectron.GradientLoose', postfix = 'loose',
+                                                     recomputeLikelihood=True, enableCutflow=True, enableKinematicHistograms=True )
+    electronSequence.configure( inputName = 'Electrons',
+                                outputName = 'AnalysisElectrons_%SYS%' )
+    algSeq += electronSequence
+
+    # Include, and then set up the photon analysis sequence:
+    from EgammaAnalysisAlgorithms.PhotonAnalysisSequence import \
+        makePhotonAnalysisSequence
+    photonSequence = makePhotonAnalysisSequence( dataType, 'Tight.FixedCutTight', postfix = 'tight',
+                                                 recomputeIsEM=True, enableCutflow=True, enableKinematicHistograms=True )
+    photonSequence.configure( inputName = 'Photons',
+                              outputName = 'AnalysisPhotons_%SYS%' )
+    algSeq += photonSequence
+
+    return algSeq
diff --git a/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/python/ElectronAnalysisSequence.py b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/python/ElectronAnalysisSequence.py
new file mode 100644
index 0000000000000000000000000000000000000000..a831079c6f4a83ec155f6a7d64c11b5aab21e87e
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/python/ElectronAnalysisSequence.py
@@ -0,0 +1,284 @@
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+
+# Framework import(s):
+import ROOT
+
+# AnaAlgorithm import(s):
+from AnaAlgorithm.AnaAlgSequence import AnaAlgSequence
+from AnaAlgorithm.DualUseConfig import createAlgorithm, addPrivateTool
+
+def makeElectronAnalysisSequence( dataType, workingPoint,
+                                  deepCopyOutput = False,
+                                  shallowViewOutput = True,
+                                  postfix = '',
+                                  recomputeLikelihood = False,
+                                  chargeIDSelection = False,
+                                  isolationCorrection = False,
+                                  crackVeto = False,
+                                  ptSelectionOutput = False,
+                                  enableCutflow = False,
+                                  enableKinematicHistograms = False ):
+    """Create an electron analysis algorithm sequence
+
+    Keyword arguments:
+      dataType -- The data type to run on ("data", "mc" or "afii")
+      workingPoint -- The working point to use
+      deepCopyOutput -- If set to 'True', the output containers will be
+                        standalone, deep copies (slower, but needed for xAOD
+                        output writing)
+      shallowViewOutput -- Create a view container if required
+      postfix -- a postfix to apply to decorations and algorithm
+                 names.  this is mostly used/needed when using this
+                 sequence with multiple working points to ensure all
+                 names are unique.
+      recomputeLikelihood -- Whether to rerun the LH. If not, use derivation flags
+      chargeIDSelection -- Whether or not to perform charge ID/flip selection
+      isolationCorrection -- Whether or not to perform isolation correction
+      crackVeto -- Whether or not to perform eta crack veto
+      ptSelectionOutput -- Whether or not to apply pt selection when creating
+                           output containers.
+      enableCutflow -- Whether or not to dump the cutflow
+      enableKinematicHistograms -- Whether or not to dump the kinematic histograms
+    """
+
+    # Make sure we received a valid data type.
+    if dataType not in [ 'data', 'mc', 'afii' ]:
+        raise ValueError( 'Invalid data type: %' % dataType )
+
+    if postfix != '' :
+        postfix = '_' + postfix
+        pass
+
+    # Make sure selection options make sense
+    if deepCopyOutput and shallowViewOutput:
+        raise ValueError ("deepCopyOutput and shallowViewOutput can't both be true!")
+
+    splitWP = workingPoint.split ('.')
+    if len (splitWP) != 2 :
+        raise ValueError ('working point should be of format "likelihood.isolation", not ' + workingPoint)
+
+    likelihoodWP = splitWP[0]
+    isolationWP = splitWP[1]
+
+    # Create the analysis algorithm sequence object:
+    seq = AnaAlgSequence( "ElectronAnalysisSequence" + postfix )
+
+    # Variables keeping track of the selections being applied.
+    selectionDecorNames = []
+    selectionDecorCount = []
+
+    # Set up the eta-cut on all electrons prior to everything else
+    alg = createAlgorithm( 'CP::AsgSelectionAlg', 'ElectronEtaCutAlg' + postfix )
+    alg.preselection = "&&".join (selectionDecorNames)
+    alg.selectionDecoration = 'selectEta' + postfix + ',as_bits'
+    addPrivateTool( alg, 'selectionTool', 'CP::AsgPtEtaSelectionTool' )
+    alg.selectionTool.maxEta = 2.47
+    if crackVeto:
+        alg.selectionTool.etaGapLow = 1.37
+        alg.selectionTool.etaGapHigh = 1.52
+    alg.selectionTool.useClusterEta = True
+    seq.append( alg, inputPropName = 'particles',
+                outputPropName = 'particlesOut',
+                stageName = 'calibration' )
+    selectionDecorNames.append( alg.selectionDecoration )
+    if crackVeto :
+        selectionDecorCount.append( 5 )
+    else :
+        selectionDecorCount.append( 4 )
+
+    # Set up the track selection algorithm:
+    alg = createAlgorithm( 'CP::AsgLeptonTrackSelectionAlg',
+                           'ElectronTrackSelectionAlg' + postfix )
+    alg.preselection = "&&".join (selectionDecorNames)
+    alg.selectionDecoration = 'trackSelection' + postfix + ',as_bits'
+    alg.maxD0Significance = 5
+    alg.maxDeltaZ0SinTheta = 0.5
+    seq.append( alg, inputPropName = 'particles',
+                stageName = 'selection' )
+    selectionDecorNames.append( alg.selectionDecoration )
+    selectionDecorCount.append( 3 )
+
+    # Set up the likelihood ID selection algorithm
+    # It is safe to do this before calibration, as the cluster E is used
+    alg = createAlgorithm( 'CP::AsgSelectionAlg', 'ElectronLikelihoodAlg' + postfix )
+    alg.preselection = "&&".join (selectionDecorNames)
+    alg.selectionDecoration = 'selectLikelihood' + postfix + ',as_bits'
+    selectionDecorNames.append( alg.selectionDecoration )
+    if recomputeLikelihood:
+        # Rerun the likelihood ID
+        addPrivateTool( alg, 'selectionTool', 'AsgElectronLikelihoodTool' )
+        alg.selectionTool.primaryVertexContainer = 'PrimaryVertices'
+        alg.selectionTool.WorkingPoint = likelihoodWP
+        selectionDecorCount.append( 7 )
+    else:
+        # Select from Derivation Framework flags
+        addPrivateTool( alg, 'selectionTool', 'CP::AsgFlagSelectionTool' )
+        dfFlag = "DFCommonElectronsLH" + likelihoodWP.split('LH')[0]
+        alg.selectionTool.selectionFlags = [dfFlag]
+        selectionDecorCount.append( 1 )
+    seq.append( alg, inputPropName = 'particles',
+                stageName = 'selection' )
+
+    # Select electrons only with good object quality.
+    alg = createAlgorithm( 'CP::AsgSelectionAlg', 'ElectronObjectQualityAlg' + postfix )
+    alg.preselection = "&&".join (selectionDecorNames)
+    alg.selectionDecoration = 'goodOQ' + postfix + ',as_bits'
+    addPrivateTool( alg, 'selectionTool', 'CP::EgammaIsGoodOQSelectionTool' )
+    alg.selectionTool.Mask = ROOT.xAOD.EgammaParameters.BADCLUSELECTRON
+    seq.append( alg, inputPropName = 'particles',
+                stageName = 'calibration' )
+    selectionDecorNames.append( alg.selectionDecoration )
+    selectionDecorCount.append( 1 )
+
+    # Set up the calibration and smearing algorithm:
+    alg = createAlgorithm( 'CP::EgammaCalibrationAndSmearingAlg',
+                           'ElectronCalibrationAndSmearingAlg' + postfix )
+    alg.preselection = "&&".join (selectionDecorNames)
+    addPrivateTool( alg, 'calibrationAndSmearingTool',
+                    'CP::EgammaCalibrationAndSmearingTool' )
+    alg.calibrationAndSmearingTool.ESModel = 'es2018_R21_v0'
+    alg.calibrationAndSmearingTool.decorrelationModel = '1NP_v1'
+    if dataType == 'afii':
+        alg.calibrationAndSmearingTool.useAFII = 1
+    else:
+        alg.calibrationAndSmearingTool.useAFII = 0
+        pass
+    seq.append( alg, inputPropName = 'egammas', outputPropName = 'egammasOut',
+                affectingSystematics = '(^EG_RESOLUTION_.*)|(^EG_SCALE_.*)',
+                stageName = 'calibration' )
+
+    # Set up the the pt selection
+    ptSelectionDecoration = 'selectPt' + postfix + ',as_bits'
+    alg = createAlgorithm( 'CP::AsgSelectionAlg', 'ElectronPtCutAlg' + postfix )
+    alg.preselection = "&&".join (selectionDecorNames)
+    alg.selectionDecoration = ptSelectionDecoration
+    addPrivateTool( alg, 'selectionTool', 'CP::AsgPtEtaSelectionTool' )
+    alg.selectionTool.minPt = 4.5e3
+    seq.append( alg, inputPropName = 'particles',
+                stageName = 'selection' )
+    selectionDecorNames.append( alg.selectionDecoration )
+    selectionDecorCount.append( 2 )
+
+    # Set up the isolation correction algorithm:
+    if isolationCorrection:
+        alg = createAlgorithm( 'CP::EgammaIsolationCorrectionAlg',
+                               'ElectronIsolationCorrectionAlg' + postfix )
+        alg.preselection = "&&".join (selectionDecorNames)
+        addPrivateTool( alg, 'isolationCorrectionTool',
+                        'CP::IsolationCorrectionTool' )
+        if dataType == 'data':
+            alg.isolationCorrectionTool.IsMC = 0
+        else:
+            alg.isolationCorrectionTool.IsMC = 1
+            pass
+        seq.append( alg, inputPropName = 'egammas', outputPropName = 'egammasOut',
+                    stageName = 'calibration' )
+
+    # Set up the isolation selection algorithm:
+    if isolationWP != 'NonIso' :
+        alg = createAlgorithm( 'CP::EgammaIsolationSelectionAlg',
+                               'ElectronIsolationSelectionAlg' + postfix )
+        alg.preselection = "&&".join (selectionDecorNames)
+        alg.selectionDecoration = 'isolated' + postfix + ',as_bits'
+        addPrivateTool( alg, 'selectionTool', 'CP::IsolationSelectionTool' )
+        alg.selectionTool.ElectronWP = isolationWP
+        seq.append( alg, inputPropName = 'egammas',
+                    stageName = 'selection' )
+        selectionDecorNames.append( alg.selectionDecoration )
+        selectionDecorCount.append( 1 )
+
+    # Select electrons only if they don't appear to have flipped their charge.
+    if chargeIDSelection:
+        alg = createAlgorithm( 'CP::AsgSelectionAlg',
+                               'ElectronChargeIDSelectionAlg' + postfix )
+        alg.preselection = "&&".join (selectionDecorNames)
+        alg.selectionDecoration = 'chargeID' + postfix + ',as_bits'
+        addPrivateTool( alg, 'selectionTool',
+                        'AsgElectronChargeIDSelectorTool' )
+        alg.selectionTool.TrainingFile = \
+          'ElectronPhotonSelectorTools/ChargeID/ECIDS_20180731rel21Summer2018.root'
+        alg.selectionTool.WorkingPoint = 'Loose'
+        alg.selectionTool.CutOnBDT = -0.337671 # Loose 97%
+        seq.append( alg, inputPropName = 'particles',
+                    stageName = 'selection' )
+        selectionDecorNames.append( alg.selectionDecoration )
+        selectionDecorCount.append( 1 )
+        pass
+
+    # Set up an algorithm used for decorating baseline electron selection:
+    alg = createAlgorithm( 'CP::AsgSelectionAlg',
+                           'ElectronSelectionSummary' + postfix )
+    addPrivateTool( alg, 'selectionTool', 'CP::AsgFlagSelectionTool' )
+    alg.selectionTool.selectionFlags = selectionDecorNames[ : ]
+    alg.selectionDecoration = 'baselineSelection' + postfix + ',as_char'
+    seq.append( alg, inputPropName = 'particles',
+                stageName = 'selection' )
+
+    # Set up an algorithm used to create electron selection cutflow:
+    if enableCutflow:
+        alg = createAlgorithm( 'CP::ObjectCutFlowHistAlg', 'ElectronCutFlowDumperAlg' + postfix )
+        alg.histPattern = 'electron_cflow_%SYS%' + postfix
+        alg.selection = selectionDecorNames[ : ]
+        alg.selectionNCuts = selectionDecorCount[ : ]
+        seq.append( alg, inputPropName = 'input', stageName = 'selection' )
+
+    # Set up an algorithm dumping the kinematic properties of the electrons:
+    if enableKinematicHistograms:
+        alg = createAlgorithm( 'CP::KinematicHistAlg', 'ElectronKinematicDumperAlg' + postfix )
+        alg.preselection = "&&".join (selectionDecorNames)
+        alg.histPattern = 'electron_%VAR%_%SYS%' + postfix
+        seq.append( alg, inputPropName = 'input', stageName = 'selection' )
+
+    # Set up the output selection
+    if shallowViewOutput or deepCopyOutput:
+        selectionDecorNamesOutput = selectionDecorNames[ : ]
+        if not ptSelectionOutput:
+            selectionDecorNamesOutput.remove(ptSelectionDecoration)
+
+    # Set up an algorithm that makes a view container using the selections
+    # performed previously:
+    if shallowViewOutput:
+        alg = createAlgorithm( 'CP::AsgViewFromSelectionAlg',
+                               'ElectronViewFromSelectionAlg' + postfix )
+        alg.selection = selectionDecorNamesOutput[ : ]
+        seq.append( alg, inputPropName = 'input', outputPropName = 'output',
+                    stageName = 'selection' )
+        pass
+
+    # Set up the electron efficiency correction algorithm:
+    alg = createAlgorithm( 'CP::ElectronEfficiencyCorrectionAlg',
+                           'ElectronEfficiencyCorrectionAlg' + postfix )
+    alg.preselection = "&&".join (selectionDecorNames)
+    addPrivateTool( alg, 'efficiencyCorrectionTool',
+                    'AsgElectronEfficiencyCorrectionTool' )
+    alg.scaleFactorDecoration = 'effSF' + postfix + '_%SYS%'
+    alg.scaleFactorDecorationRegex = '(^EL_EFF_Reco.*)'
+    alg.efficiencyCorrectionTool.RecoKey = "Reconstruction"
+    alg.efficiencyCorrectionTool.CorrelationModel = "TOTAL"
+    if dataType == 'afii':
+        alg.efficiencyCorrectionTool.ForceDataType = \
+          ROOT.PATCore.ParticleDataType.Fast
+    elif dataType == 'mc':
+        alg.efficiencyCorrectionTool.ForceDataType = \
+          ROOT.PATCore.ParticleDataType.Full
+        pass
+    alg.outOfValidity = 2 #silent
+    alg.outOfValidityDeco = 'bad_eff' + postfix
+    if dataType != 'data':
+        seq.append( alg, inputPropName = 'electrons',
+                    affectingSystematics = '(^EL_EFF_Reco.*)',
+                    stageName = 'efficiency' )
+        pass
+
+    # Set up a final deep copy making algorithm if requested:
+    if deepCopyOutput:
+        alg = createAlgorithm( 'CP::AsgViewFromSelectionAlg',
+                               'ElectronDeepCopyMaker' + postfix )
+        alg.selection = selectionDecorNamesOutput[:]
+        alg.deepCopy = True
+        seq.append( alg, inputPropName = 'input', outputPropName = 'output',
+                    stageName = 'selection' )
+        pass
+
+    # Return the sequence:
+    return seq
diff --git a/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/python/PhotonAnalysisSequence.py b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/python/PhotonAnalysisSequence.py
new file mode 100644
index 0000000000000000000000000000000000000000..85583acf4453d5607083075e7b1e48e0a615daba
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/python/PhotonAnalysisSequence.py
@@ -0,0 +1,219 @@
+# Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+
+# Framework import(s):
+import ROOT
+
+# AnaAlgorithm import(s):
+from AnaAlgorithm.AnaAlgSequence import AnaAlgSequence
+from AnaAlgorithm.DualUseConfig import createAlgorithm, addPrivateTool
+
+def makePhotonAnalysisSequence( dataType, workingPoint,
+                                deepCopyOutput = False,
+                                postfix = '',
+                                recomputeIsEM = False,
+                                enableCutflow = False,
+                                enableKinematicHistograms = False ):
+    """Create a photon analysis algorithm sequence
+
+    Keywrod arguments:
+      dataType -- The data type to run on ("data", "mc" or "afii")
+      workingPoint -- The working point to use
+      deepCopyOutput -- If set to 'True', the output containers will be
+                        standalone, deep copies (slower, but needed for xAOD
+                        output writing)
+      postfix -- a postfix to apply to decorations and algorithm
+                 names.  this is mostly used/needed when using this
+                 sequence with multiple working points to ensure all
+                 names are unique.
+      recomputeIsEM -- Whether to rerun the cut-based selection. If not, use derivation flags
+      enableCutflow -- Whether or not to dump the cutflow
+      enableKinematicHistograms -- Whether or not to dump the kinematic histograms
+    """
+
+    # Make sure we received a valid data type.
+    if dataType not in [ 'data', 'mc', 'afii' ]:
+        raise ValueError( 'Invalid data type: %' % dataType )
+
+    if postfix != '' :
+        postfix = '_' + postfix
+        pass
+
+    splitWP = workingPoint.split ('.')
+    if len (splitWP) != 2 :
+        raise ValueError ('working point should be of format "quality.isolation", not ' + workingPoint)
+
+    qualityWP = splitWP[0]
+    isolationWP = splitWP[1]
+
+    if qualityWP == 'Tight' :
+        quality = ROOT.egammaPID.PhotonTight
+        pass
+    elif qualityWP == 'Loose' :
+        quality = ROOT.egammaPID.PhotonLoose
+        pass
+    else :
+        raise Exception ('unknown photon quality working point "' + qualityWP + '" should be Tight or Loose')
+
+    # Create the analysis algorithm sequence object:
+    seq = AnaAlgSequence( "PhotonAnalysisSequence" + postfix )
+
+    # Variables keeping track of the selections being applied.
+    selectionDecorNames = []
+    selectionDecorCount = []
+
+    # Set up the photon selection algorithm:
+    alg = createAlgorithm( 'CP::AsgSelectionAlg', 'PhotonIsEMSelectorAlg' + postfix )
+    alg.selectionDecoration = 'selectEM'
+    selectionDecorNames.append( alg.selectionDecoration )
+    if recomputeIsEM:
+        # Rerun the cut-based ID
+        addPrivateTool( alg, 'selectionTool', 'AsgPhotonIsEMSelector' )
+        alg.selectionTool.isEMMask = quality
+        alg.selectionTool.ConfigFile = \
+          'ElectronPhotonSelectorTools/offline/20180116/PhotonIsEMTightSelectorCutDefs.conf'
+        selectionDecorCount.append( 32 )
+    else:
+        # Select from Derivation Framework flags
+        addPrivateTool( alg, 'selectionTool', 'CP::AsgFlagSelectionTool' )
+        dfFlag = 'DFCommonPhotonsIsEM' + qualityWP
+        alg.selectionTool.selectionFlags = [ dfFlag ]
+        selectionDecorCount.append( 1 )
+        pass
+    seq.append( alg, inputPropName = 'particles',
+                outputPropName = 'particlesOut',
+                stageName = 'calibration' )
+
+    # Select electrons only with good object quality.
+    alg = createAlgorithm( 'CP::AsgSelectionAlg', 'PhotonObjectQualityAlg' + postfix )
+    alg.selectionDecoration = 'goodOQ'
+    addPrivateTool( alg, 'selectionTool', 'CP::EgammaIsGoodOQSelectionTool' )
+    alg.selectionTool.Mask = ROOT.xAOD.EgammaParameters.BADCLUSPHOTON
+    seq.append( alg, inputPropName = 'particles',
+                outputPropName = 'particlesOut',
+                stageName = 'calibration' )
+    selectionDecorNames.append( alg.selectionDecoration )
+    selectionDecorCount.append( 1 )
+
+    # Only run subsequent processing on the objects passing all of these cuts.
+    # Since these are independent of the photon calibration, and this speeds
+    # up the job.
+    alg = createAlgorithm( 'CP::AsgViewFromSelectionAlg',
+                           'PhotonPreSelViewFromSelectionAlg' + postfix )
+    alg.selection = selectionDecorNames[ : ]
+    seq.append( alg, inputPropName = 'input', outputPropName = 'output',
+                stageName = 'calibration' )
+
+    # Set up the calibration ans smearing algorithm.
+    alg = createAlgorithm( 'CP::EgammaCalibrationAndSmearingAlg',
+                           'PhotonCalibrationAndSmearingAlg' + postfix )
+    addPrivateTool( alg, 'calibrationAndSmearingTool',
+                    'CP::EgammaCalibrationAndSmearingTool' )
+    alg.calibrationAndSmearingTool.ESModel = 'es2018_R21_v0'
+    alg.calibrationAndSmearingTool.decorrelationModel = '1NP_v1'
+    if dataType == 'afii':
+        alg.calibrationAndSmearingTool.useAFII = 1
+    else :
+        alg.calibrationAndSmearingTool.useAFII = 0
+        pass
+    seq.append( alg, inputPropName = 'egammas', outputPropName = 'egammasOut',
+                affectingSystematics = '(^EG_RESOLUTION_.*)|(^EG_SCALE_.*)',
+                stageName = 'calibration' )
+
+    # should this be applied to data?  or to AFII?
+    alg = createAlgorithm( 'CP::PhotonShowerShapeFudgeAlg',
+                           'PhotonShowerShapeFudgeAlg' + postfix )
+    addPrivateTool( alg, 'showerShapeFudgeTool',
+                    'ElectronPhotonShowerShapeFudgeTool' )
+    alg.showerShapeFudgeTool.Preselection = 21 # 21 = MC15
+    alg.showerShapeFudgeTool.FFCalibFile = \
+        'ElectronPhotonShowerShapeFudgeTool/v1/PhotonFudgeFactors.root' #only for rel21
+    seq.append( alg, inputPropName = 'photons', outputPropName = 'photonsOut',
+                stageName = 'calibration' )
+
+    # Set up the isolation correction algorithm.
+    alg = createAlgorithm( 'CP::EgammaIsolationCorrectionAlg',
+                           'PhotonIsolationCorrectionAlg' + postfix )
+    addPrivateTool( alg, 'isolationCorrectionTool',
+                    'CP::IsolationCorrectionTool' )
+    if dataType == 'data':
+        alg.isolationCorrectionTool.IsMC = 0
+    else:
+        alg.isolationCorrectionTool.IsMC = 1
+        pass
+    seq.append( alg, inputPropName = 'egammas', outputPropName = 'egammasOut',
+                stageName = 'selection' )
+
+    # Set up the isolation selection algorithm:
+    alg = createAlgorithm( 'CP::EgammaIsolationSelectionAlg',
+                           'PhotonIsolationSelectionAlg' + postfix )
+    alg.selectionDecoration = 'isolated' + postfix
+    addPrivateTool( alg, 'selectionTool', 'CP::IsolationSelectionTool' )
+    alg.selectionTool.PhotonWP = isolationWP
+    seq.append( alg, inputPropName = 'egammas', outputPropName = 'egammasOut',
+                stageName = 'selection' )
+    selectionDecorNames.append( alg.selectionDecoration )
+    selectionDecorCount.append( 1 )
+
+    # Set up the photon efficiency correction algorithm.
+    alg = createAlgorithm( 'CP::PhotonEfficiencyCorrectionAlg',
+                           'PhotonEfficiencyCorrectionAlg' + postfix )
+    addPrivateTool( alg, 'efficiencyCorrectionTool',
+                    'AsgPhotonEfficiencyCorrectionTool' )
+    alg.scaleFactorDecoration = 'effSF' + postfix
+    alg.efficiencyCorrectionTool.MapFilePath = \
+        'PhotonEfficiencyCorrection/2015_2017/rel21.2/Winter2018_Prerec_v1/map0.txt'
+    if dataType == 'afii':
+        alg.efficiencyCorrectionTool.ForceDataType = \
+          ROOT.PATCore.ParticleDataType.Fast
+    elif dataType == 'mc':
+        alg.efficiencyCorrectionTool.ForceDataType = \
+          ROOT.PATCore.ParticleDataType.Full
+        pass
+    alg.outOfValidity = 2 #silent
+    alg.outOfValidityDeco = 'bad_eff' + postfix
+    if dataType != 'data':
+        seq.append( alg, inputPropName = 'photons',
+                    outputPropName = 'photonsOut',
+                    affectingSystematics = '(^PH_EFF_.*)',
+                    stageName = 'efficiency' )
+        selectionDecorNames.append( alg.outOfValidityDeco )
+        selectionDecorCount.append( 1 )
+        pass
+
+    # Set up an algorithm used to create photon selection cutflow:
+    if enableCutflow:
+        alg = createAlgorithm( 'CP::ObjectCutFlowHistAlg',
+                            'PhotonCutFlowDumperAlg' + postfix )
+        alg.histPattern = 'photon_cflow_%SYS%' + postfix
+        alg.selection = selectionDecorNames[ : ]
+        alg.selectionNCuts = selectionDecorCount[ : ]
+        seq.append( alg, inputPropName = 'input',
+                    stageName = 'selection' )
+
+    # Set up an algorithm that makes a view container using the selections
+    # performed previously:
+    alg = createAlgorithm( 'CP::AsgViewFromSelectionAlg',
+                           'PhotonViewFromSelectionAlg' + postfix )
+    alg.selection = selectionDecorNames[ : ]
+    seq.append( alg, inputPropName = 'input', outputPropName = 'output',
+                stageName = 'selection' )
+
+    # Set up an algorithm dumping the kinematic properties of the photons:
+    if enableKinematicHistograms:
+        alg = createAlgorithm( 'CP::KinematicHistAlg', 'PhotonKinematicDumperAlg' + postfix )
+        alg.preselection = "&&".join (selectionDecorNames)
+        alg.histPattern = 'photon_%VAR%_%SYS%' + postfix
+        seq.append( alg, inputPropName = 'input',
+                    stageName = 'selection' )
+
+    # Set up a final deep copy making algorithm if requested:
+    if deepCopyOutput:
+        alg = createAlgorithm( 'CP::AsgViewFromSelectionAlg',
+                               'PhotonDeepCopyMaker' + postfix )
+        alg.deepCopy = True
+        seq.append( alg, inputPropName = 'input', outputPropName = 'output',
+                    stageName = 'selection' )
+        pass
+
+    # Return the sequence:
+    return seq
diff --git a/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/python/__init__.py b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/python/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..5ccb446532599c01cc3844e5b1b18db06f721002
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/python/__init__.py
@@ -0,0 +1,3 @@
+# Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+
+__version__ = '1.0.0'
diff --git a/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/share/EgammaAnalysisAlgorithmsTest_eljob.py b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/share/EgammaAnalysisAlgorithmsTest_eljob.py
new file mode 100755
index 0000000000000000000000000000000000000000..bbf680455ce301c37cc9a1900daa50095b940a88
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/share/EgammaAnalysisAlgorithmsTest_eljob.py
@@ -0,0 +1,76 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+#
+# @author Nils Krumnack
+
+
+# Read the submission directory as a command line argument. You can
+# extend the list of arguments with your private ones later on.
+import optparse
+parser = optparse.OptionParser()
+parser.add_option( '-d', '--data-type', dest = 'data_type',
+                   action = 'store', type = 'string', default = 'data',
+                   help = 'Type of data to run over. Valid options are data, mc, afii' )
+parser.add_option( '-s', '--submission-dir', dest = 'submission_dir',
+                   action = 'store', type = 'string', default = 'submitDir',
+                   help = 'Submission directory for EventLoop' )
+parser.add_option( '-u', '--unit-test', dest='unit_test',
+                   action = 'store_true', default = False,
+                   help = 'Run the job in "unit test mode"' )
+( options, args ) = parser.parse_args()
+
+# Set up (Py)ROOT.
+import ROOT
+ROOT.xAOD.Init().ignore()
+
+# ideally we'd run over all of them, but we don't have a mechanism to
+# configure per-sample right now
+
+dataType = options.data_type
+
+if dataType not in ["data", "mc", "afii"] :
+    raise ValueError ("invalid data type: " + dataType)
+
+# Set up the sample handler object. See comments from the C++ macro
+# for the details about these lines.
+import os
+sh = ROOT.SH.SampleHandler()
+sh.setMetaString( 'nc_tree', 'CollectionTree' )
+sample = ROOT.SH.SampleLocal (dataType)
+if dataType == "data" :
+    sample.add (os.getenv ('ASG_TEST_FILE_DATA'))
+    pass
+if dataType == "mc" :
+    sample.add (os.getenv ('ASG_TEST_FILE_MC'))
+    pass
+if dataType == "afii" :
+    sample.add (os.getenv ('ASG_TEST_FILE_MC_AFII'))
+    pass
+sh.add (sample)
+sh.printContent()
+
+# Create an EventLoop job.
+job = ROOT.EL.Job()
+job.sampleHandler( sh )
+job.options().setDouble( ROOT.EL.Job.optMaxEvents, 500 )
+
+from EgammaAnalysisAlgorithms.EgammaAnalysisAlgorithmsTest import makeSequence
+algSeq = makeSequence (dataType)
+print algSeq # For debugging
+for alg in algSeq :
+    job.algsAdd( alg )
+    pass
+
+# Find the right output directory:
+submitDir = options.submission_dir
+if options.unit_test:
+    import os
+    import tempfile
+    submitDir = tempfile.mkdtemp( prefix = 'egammaTest_'+dataType+'_', dir = os.getcwd() )
+    os.rmdir( submitDir )
+    pass
+
+# Run the job using the direct driver.
+driver = ROOT.EL.DirectDriver()
+driver.submit( job, submitDir )
diff --git a/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/share/EgammaAnalysisAlgorithmsTest_jobOptions.py b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/share/EgammaAnalysisAlgorithmsTest_jobOptions.py
new file mode 100644
index 0000000000000000000000000000000000000000..f8b7e433b4d611c234ecd511a73266c034321549
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/share/EgammaAnalysisAlgorithmsTest_jobOptions.py
@@ -0,0 +1,44 @@
+# Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+#
+# @author Nils Krumnack
+
+# User options, which can be set from command line after a "-" character
+# athena EgammaAlgorithmsTest_jobOptions.py - --myOption ...
+from AthenaCommon.AthArgumentParser import AthArgumentParser
+athArgsParser = AthArgumentParser()
+athArgsParser.add_argument("--data-type", action = "store", dest = "data_type",
+                           default = "data",
+                           help = "Type of input to run over. Valid options are 'data', 'mc', 'afii'")
+athArgs = athArgsParser.parse_args()
+
+dataType = athArgs.data_type
+if not dataType in ["data", "mc", "afii"] :
+    raise Exception ("invalid data type: " + dataType)
+
+print("Running on data type: " + dataType)
+
+inputfile = {"data": 'ASG_TEST_FILE_DATA',
+             "mc":   'ASG_TEST_FILE_MC',
+             "afii": 'ASG_TEST_FILE_MC_AFII'}
+
+# Set up the reading of the input file:
+import AthenaRootComps.ReadAthenaxAODHybrid
+theApp.EvtMax = 500
+testFile = os.getenv ( inputfile[dataType] )
+svcMgr.EventSelector.InputCollections = [testFile]
+
+from EgammaAnalysisAlgorithms.EgammaAnalysisAlgorithmsTest import makeSequence
+algSeq = makeSequence (dataType)
+print algSeq # For debugging
+
+# Add all algorithms from the sequence to the job.
+athAlgSeq += algSeq
+
+# Set up a histogram output file for the job:
+ServiceMgr += CfgMgr.THistSvc()
+ServiceMgr.THistSvc.Output += [
+    "ANALYSIS DATAFILE='EgammaAnalysisAlgorithmsTest." + dataType + ".hist.root' OPT='RECREATE'"
+    ]
+
+# Reduce the printout from Athena:
+include( "AthAnalysisBaseComps/SuppressLogging.py" )
diff --git a/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/src/components/EgammaAnalysisAlgorithms_entries.cxx b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/src/components/EgammaAnalysisAlgorithms_entries.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..d4ed8c1679c72c2f347d148ce09da4d9c9483072
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/src/components/EgammaAnalysisAlgorithms_entries.cxx
@@ -0,0 +1,29 @@
+// AsgExampleTools_entries.cxx
+
+#include <GaudiKernel/DeclareFactoryEntries.h>
+
+#include <EgammaAnalysisAlgorithms/EgammaCalibrationAndSmearingAlg.h>
+#include <EgammaAnalysisAlgorithms/EgammaIsGoodOQSelectionTool.h>
+#include <EgammaAnalysisAlgorithms/EgammaIsolationCorrectionAlg.h>
+#include <EgammaAnalysisAlgorithms/EgammaIsolationSelectionAlg.h>
+#include <EgammaAnalysisAlgorithms/ElectronEfficiencyCorrectionAlg.h>
+#include <EgammaAnalysisAlgorithms/PhotonEfficiencyCorrectionAlg.h>
+#include <EgammaAnalysisAlgorithms/PhotonShowerShapeFudgeAlg.h>
+
+DECLARE_NAMESPACE_ALGORITHM_FACTORY (CP, EgammaCalibrationAndSmearingAlg)
+DECLARE_NAMESPACE_TOOL_FACTORY (CP, EgammaIsGoodOQSelectionTool)
+DECLARE_NAMESPACE_ALGORITHM_FACTORY (CP, EgammaIsolationCorrectionAlg)
+DECLARE_NAMESPACE_ALGORITHM_FACTORY (CP, EgammaIsolationSelectionAlg)
+DECLARE_NAMESPACE_ALGORITHM_FACTORY (CP, ElectronEfficiencyCorrectionAlg)
+DECLARE_NAMESPACE_ALGORITHM_FACTORY (CP, PhotonEfficiencyCorrectionAlg)
+DECLARE_NAMESPACE_ALGORITHM_FACTORY (CP, PhotonShowerShapeFudgeAlg)
+
+DECLARE_FACTORY_ENTRIES(EgammaAnalysisAlgorithms) {
+  DECLARE_NAMESPACE_ALGORITHM (CP, EgammaCalibrationAndSmearingAlg)
+  DECLARE_NAMESPACE_TOOL (CP, EgammaIsGoodOQSelectionTool)
+  DECLARE_NAMESPACE_ALGORITHM (CP, EgammaIsolationCorrectionAlg)
+  DECLARE_NAMESPACE_ALGORITHM (CP, EgammaIsolationSelectionAlg)
+  DECLARE_NAMESPACE_ALGORITHM (CP, ElectronEfficiencyCorrectionAlg)
+  DECLARE_NAMESPACE_ALGORITHM (CP, PhotonEfficiencyCorrectionAlg)
+  DECLARE_NAMESPACE_ALGORITHM (CP, PhotonShowerShapeFudgeAlg)
+}
diff --git a/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/src/components/EgammaAnalysisAlgorithms_load.cxx b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/src/components/EgammaAnalysisAlgorithms_load.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..fe3d2b768fca0948d9adf76951bedfd880d2ee68
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/src/components/EgammaAnalysisAlgorithms_load.cxx
@@ -0,0 +1,3 @@
+#include "GaudiKernel/LoadFactoryEntries.h"
+
+LOAD_FACTORY_ENTRIES(EgammaAnalysisAlgorithms)
diff --git a/PhysicsAnalysis/Algorithms/FTagAnalysisAlgorithms/CMakeLists.txt b/PhysicsAnalysis/Algorithms/FTagAnalysisAlgorithms/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..fce4b747c7714639128fabc463373253b38e06fc
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/FTagAnalysisAlgorithms/CMakeLists.txt
@@ -0,0 +1,61 @@
+# Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+#
+# @author Nils Krumnack
+
+
+# The name of the package:
+atlas_subdir( FTagAnalysisAlgorithms )
+
+atlas_depends_on_subdirs(
+    PUBLIC
+    Event/xAOD/xAODJet
+    PhysicsAnalysis/Algorithms/SelectionHelpers
+    PhysicsAnalysis/Algorithms/SystematicsHandles
+    PhysicsAnalysis/D3PDTools/AnaAlgorithm
+    PhysicsAnalysis/Interfaces/FTagAnalysisInterfaces )
+
+atlas_add_library( FTagAnalysisAlgorithmsLib
+   FTagAnalysisAlgorithms/*.h FTagAnalysisAlgorithms/*.icc Root/*.cxx
+   PUBLIC_HEADERS FTagAnalysisAlgorithms
+   LINK_LIBRARIES xAODJet SelectionHelpersLib SystematicsHandlesLib
+   AnaAlgorithmLib FTagAnalysisInterfacesLib )
+
+atlas_add_dictionary( FTagAnalysisAlgorithmsDict
+   FTagAnalysisAlgorithms/FTagAnalysisAlgorithmsDict.h
+   FTagAnalysisAlgorithms/selection.xml
+   LINK_LIBRARIES FTagAnalysisAlgorithmsLib )
+
+if( NOT XAOD_STANDALONE )
+   atlas_add_component( FTagAnalysisAlgorithms
+      src/*.h src/*.cxx src/components/*.cxx
+      LINK_LIBRARIES GaudiKernel FTagAnalysisAlgorithmsLib )
+endif()
+
+atlas_install_python_modules( python/*.py )
+atlas_install_joboptions( share/*_jobOptions.py )
+atlas_install_scripts( share/*_eljob.py )
+
+if( XAOD_STANDALONE )
+   atlas_add_test( testJobData
+      SCRIPT FTagAnalysisAlgorithmsTest_eljob.py --data-type data --unit-test
+      PROPERTIES TIMEOUT 600 )
+   atlas_add_test( testJobFullSim
+      SCRIPT FTagAnalysisAlgorithmsTest_eljob.py --data-type mc --unit-test
+      PROPERTIES TIMEOUT 600 )
+   atlas_add_test( testJobFastSim
+      SCRIPT FTagAnalysisAlgorithmsTest_eljob.py --data-type afii --unit-test
+      PROPERTIES TIMEOUT 600 )
+elseif( NOT "${CMAKE_PROJECT_NAME}" STREQUAL "AthDerivation" )
+   atlas_add_test( testJobData
+      SCRIPT athena.py
+      FTagAnalysisAlgorithms/FTagAnalysisAlgorithmsTest_jobOptions.py - --data-type data
+      PROPERTIES TIMEOUT 600 )
+   atlas_add_test( testJobFullSim
+      SCRIPT athena.py
+      FTagAnalysisAlgorithms/FTagAnalysisAlgorithmsTest_jobOptions.py - --data-type mc
+      PROPERTIES TIMEOUT 600 )
+   atlas_add_test( testJobFastSim
+      SCRIPT athena.py
+      FTagAnalysisAlgorithms/FTagAnalysisAlgorithmsTest_jobOptions.py - --data-type afii
+      PROPERTIES TIMEOUT 600 )
+endif()
diff --git a/PhysicsAnalysis/Algorithms/FTagAnalysisAlgorithms/FTagAnalysisAlgorithms/BTaggingEfficiencyAlg.h b/PhysicsAnalysis/Algorithms/FTagAnalysisAlgorithms/FTagAnalysisAlgorithms/BTaggingEfficiencyAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..d19d19630f2ee6f2e9a2848f856bcb671f3c1985
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/FTagAnalysisAlgorithms/FTagAnalysisAlgorithms/BTaggingEfficiencyAlg.h
@@ -0,0 +1,81 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef F_TAG_ANALYSIS_ALGORITHMS__B_TAGGING_EFFICIENCY_ALG_H
+#define F_TAG_ANALYSIS_ALGORITHMS__B_TAGGING_EFFICIENCY_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <FTagAnalysisInterfaces/IBTaggingEfficiencyTool.h>
+#include <SelectionHelpers/OutOfValidityHelper.h>
+#include <SelectionHelpers/ISelectionAccessor.h>
+#include <SelectionHelpers/SelectionReadHandle.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <SystematicsHandles/SysDecorationHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+#include <SystematicsHandles/SysReadHandle.h>
+#include <xAODJet/JetContainer.h>
+#include <memory>
+
+namespace CP
+{
+  /// \brief an algorithm for calling \ref IBTaggingEfficiencyTool
+
+  class BTaggingEfficiencyAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    BTaggingEfficiencyAlg (const std::string& name, 
+                           ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+    
+
+
+    /// \brief the smearing tool
+  private:
+    ToolHandle<IBTaggingEfficiencyTool> m_efficiencyTool;
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the jet collection we run on
+  private:
+    SysCopyHandle<xAOD::JetContainer> m_jetHandle {
+      this, "jets", "Jets", "the jet collection to run on"};
+
+    /// \brief the preselection we apply to our input
+  private:
+    SelectionReadHandle m_preselection {
+      this, "preselection", "", "the preselection to apply"};
+
+    /// \brief the helper for OutOfValidity results
+  private:
+    OutOfValidityHelper m_outOfValidity {this};
+
+    /// \brief the decoration for the b-tagging scale factor
+  private:
+    SysDecorationHandle<float> m_scaleFactorDecoration {
+      this, "scaleFactorDecoration", "", "the decoration for the b-tagging efficiency scale factor"};
+
+    /// \brief the decoration for the b-tagging selection
+  private:
+    SelectionReadHandle m_selectionHandle {
+      this, "selectionDecoration", "", "the decoration for the asg selection"};
+
+    /// \brief only run the inefficency for all jets
+  private:
+    bool m_onlyInefficiency {false};
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/FTagAnalysisAlgorithms/FTagAnalysisAlgorithms/FTagAnalysisAlgorithmsDict.h b/PhysicsAnalysis/Algorithms/FTagAnalysisAlgorithms/FTagAnalysisAlgorithms/FTagAnalysisAlgorithmsDict.h
new file mode 100644
index 0000000000000000000000000000000000000000..dd476fc0233a6b4f8d9152aef636a20b22ef4d23
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/FTagAnalysisAlgorithms/FTagAnalysisAlgorithms/FTagAnalysisAlgorithmsDict.h
@@ -0,0 +1,13 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef F_TAG_ANALYSIS_ALGORITHMS__F_TAG_ANALYSIS_ALGORITHMS_DICT_H
+#define F_TAG_ANALYSIS_ALGORITHMS__F_TAG_ANALYSIS_ALGORITHMS_DICT_H
+
+#include <FTagAnalysisAlgorithms/BTaggingEfficiencyAlg.h>
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/FTagAnalysisAlgorithms/FTagAnalysisAlgorithms/selection.xml b/PhysicsAnalysis/Algorithms/FTagAnalysisAlgorithms/FTagAnalysisAlgorithms/selection.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ebaebc249669d3cb1748e62ae3a5d348181eaa47
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/FTagAnalysisAlgorithms/FTagAnalysisAlgorithms/selection.xml
@@ -0,0 +1,5 @@
+<lcgdict>
+
+   <class name="CP::BTaggingEfficiencyAlg" />
+   
+</lcgdict>
diff --git a/PhysicsAnalysis/Algorithms/FTagAnalysisAlgorithms/Root/BTaggingEfficiencyAlg.cxx b/PhysicsAnalysis/Algorithms/FTagAnalysisAlgorithms/Root/BTaggingEfficiencyAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..0e901cbe8a437e068889e2ff3a52601f6bbb2245
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/FTagAnalysisAlgorithms/Root/BTaggingEfficiencyAlg.cxx
@@ -0,0 +1,101 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+//
+// includes
+//
+
+#include <FTagAnalysisAlgorithms/BTaggingEfficiencyAlg.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  BTaggingEfficiencyAlg ::
+  BTaggingEfficiencyAlg (const std::string& name, 
+                     ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+    , m_efficiencyTool ("BTaggingEfficiencyTool", this)
+  {
+    declareProperty ("efficiencyTool", m_efficiencyTool, "the calibration and smearing tool we apply");
+    declareProperty ("onlyInefficiency", m_onlyInefficiency, "whether only to calculate inefficiencies");
+  }
+
+
+
+  StatusCode BTaggingEfficiencyAlg ::
+  initialize ()
+  {
+    if (m_onlyInefficiency && m_selectionHandle)
+    {
+      ANA_MSG_ERROR ("can't specify both onlyInefficiency and selectionDecoration");
+      return StatusCode::FAILURE;
+    }
+
+    if (m_scaleFactorDecoration.empty())
+    {
+      ANA_MSG_ERROR ("no scale factor decoration name set");
+      return StatusCode::FAILURE;
+    }
+
+    ANA_CHECK (m_efficiencyTool.retrieve());
+    m_systematicsList.addHandle (m_jetHandle);
+    ANA_CHECK (m_systematicsList.addAffectingSystematics (m_efficiencyTool->affectingSystematics()));
+    ANA_CHECK (m_systematicsList.initialize());
+    ANA_CHECK (m_preselection.initialize());
+    ANA_CHECK (m_selectionHandle.initialize());
+    ANA_CHECK (m_outOfValidity.initialize());
+
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode BTaggingEfficiencyAlg ::
+  execute ()
+  {
+    ANA_CHECK (m_scaleFactorDecoration.preExecute (m_systematicsList));
+
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode {
+        ANA_CHECK (m_efficiencyTool->applySystematicVariation (sys));
+        xAOD::JetContainer *jets = nullptr;
+        ANA_CHECK (m_jetHandle.getCopy (jets, sys));
+        for (xAOD::Jet *jet : *jets)
+        {
+          if (m_preselection.getBool (*jet))
+          {
+            float sf = 0;
+
+            // The efficiency tool can calculate both efficiencies and
+            // inefficiencies.  This setup can calculate either, or
+            // both; in the case of the later a selection decoration is
+            // used to decide whether to calculate efficiencies or
+            // inefficiencies.
+            //
+            // Note that if you want to exclude jets from processing,
+            // this selection accessor/decoration has nothing to do with
+            // it.  You do the pre-selection via a view container like
+            // for all the other CP algorithms.
+            if (!m_onlyInefficiency && m_selectionHandle.getBool (*jet))
+            {
+              ANA_CHECK_CORRECTION (m_outOfValidity, *jet, m_efficiencyTool->getScaleFactor (*jet, sf));
+            } else
+            {
+              ANA_CHECK_CORRECTION (m_outOfValidity, *jet, m_efficiencyTool->getInefficiencyScaleFactor (*jet, sf));
+            }
+            m_scaleFactorDecoration.set (*jet, sf, sys);
+          } else {
+            m_scaleFactorDecoration.set (*jet, invalidScaleFactor(), sys);
+          }
+        }
+        return StatusCode::SUCCESS;
+      });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/FTagAnalysisAlgorithms/python/FTagAnalysisAlgorithmsTest.py b/PhysicsAnalysis/Algorithms/FTagAnalysisAlgorithms/python/FTagAnalysisAlgorithmsTest.py
new file mode 100644
index 0000000000000000000000000000000000000000..7e0c9e5bb4e118d9b4f92d246e4d0c1169113c3e
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/FTagAnalysisAlgorithms/python/FTagAnalysisAlgorithmsTest.py
@@ -0,0 +1,29 @@
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+#
+# @author Nils Krumnack
+
+from AnaAlgorithm.AlgSequence import AlgSequence
+from AnaAlgorithm.DualUseConfig import createAlgorithm
+
+def makeSequence (dataType, jetContainer="AntiKt4EMPFlowJets") :
+
+    algSeq = AlgSequence()
+
+    # Set up the systematics loader/handler algorithm:
+    sysLoader = createAlgorithm( 'CP::SysListLoaderAlg', 'SysLoaderAlg' )
+    sysLoader.sigmaRecommended = 1
+    algSeq += sysLoader
+
+    # Include, and then set up the jet analysis algorithm sequence:
+    from JetAnalysisAlgorithms.JetAnalysisSequence import makeJetAnalysisSequence
+    jetSequence = makeJetAnalysisSequence( dataType, jetContainer,
+                                           enableCutflow=True, enableKinematicHistograms=True )
+    from FTagAnalysisAlgorithms.FTagAnalysisSequence import makeFTagAnalysisSequence
+    makeFTagAnalysisSequence( jetSequence, dataType, jetContainer, noEfficiency = True, legacyRecommendations = True,
+                              enableCutflow=True )
+    jetSequence.configure( inputName = jetContainer, outputName = 'AnalysisJets_%SYS%' )
+
+    # Add the sequence to the job:
+    algSeq += jetSequence
+
+    return algSeq
diff --git a/PhysicsAnalysis/Algorithms/FTagAnalysisAlgorithms/python/FTagAnalysisSequence.py b/PhysicsAnalysis/Algorithms/FTagAnalysisAlgorithms/python/FTagAnalysisSequence.py
new file mode 100644
index 0000000000000000000000000000000000000000..505ac45816cf086f226afd79c22edf8bd2b358c0
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/FTagAnalysisAlgorithms/python/FTagAnalysisSequence.py
@@ -0,0 +1,112 @@
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+
+# AnaAlgorithm import(s):
+from AnaAlgorithm.DualUseConfig import createAlgorithm, addPrivateTool
+
+def makeFTagAnalysisSequence( seq, dataType, jetCollection,
+                              btagWP = "FixedCutBEff_77",
+                              btagger = "MV2c10",
+                              postfix = "",
+                              preselection=None,
+                              kinematicSelection = False,
+                              noEfficiency = False,
+                              legacyRecommendations = False,
+                              enableCutflow = False ):
+    """Create a ftag analysis algorithm sequence
+
+    for now the sequence is passed in, I'm unsure if I can concatenate
+    two sequences at the moment, or if that blows things up horribly
+
+    Keyword arguments:
+      dataType -- The data type to run on ("data", "mc" or "afii")
+      jetCollection -- Jet container to run on
+      btagWP -- Flavour tagging working point
+      btagger -- Flavour tagger
+      kinematicSelection -- Wether to run kinematic selection
+      noEfficiency -- Wether to run efficiencies calculation
+      legacyRecommendations -- Use legacy recommendations without shallow copied containers
+      enableCutflow -- Whether or not to dump the cutflow
+    """
+
+    if dataType not in ["data", "mc", "afii"] :
+        raise ValueError ("invalid data type: " + dataType)
+
+    if legacyRecommendations:
+        # Remove b-tagging calibration from the container name
+        btIndex = jetCollection.find('_BTagging')
+        if btIndex != -1:
+            jetCollection = jetCollection[:btIndex]
+
+        bTagCalibFile = "xAODBTaggingEfficiency/13TeV/2017-21-13TeV-MC16-CDI-2019-07-30_v1.root"
+    else:
+        bTagCalibFile = "xAODBTaggingEfficiency/13TeV/2019-21-13TeV-MC16-CDI-2019-10-07_v1.root"
+
+    # # Create the analysis algorithm sequence object:
+    # seq = AnaAlgSequence( "FTagAnalysisSequence" )
+
+    if kinematicSelection:
+        # Set up the ftag kinematic selection algorithm(s):
+        alg = createAlgorithm( 'CP::AsgSelectionAlg', 'FTagKinSelectionAlg'+postfix )
+        addPrivateTool( alg, 'selectionTool', 'CP::AsgPtEtaSelectionTool' )
+        alg.selectionTool.minPt = 20e3
+        alg.selectionTool.maxEta = 2.5
+        alg.selectionDecoration = 'ftag_kin_select'
+        seq.append( alg, inputPropName = 'particles',
+                    outputPropName = 'particlesOut' )
+
+        # Set up an algorithm that makes a view container using the selections
+        # performed previously:
+        alg = createAlgorithm( 'CP::AsgViewFromSelectionAlg',
+                               'FTagKinViewFromSelectionAlg'+postfix )
+        alg.selection = [ 'ftag_kin_select' ]
+        seq.append( alg, inputPropName = 'input', outputPropName = 'output',
+                    stageName = 'selection' )
+
+    # Set up the ftag selection algorithm(s):
+    alg = createAlgorithm( 'CP::AsgSelectionAlg', 'FTagSelectionAlg' + btagger + btagWP + postfix )
+    addPrivateTool( alg, 'selectionTool', 'BTaggingSelectionTool' )
+    alg.selectionTool.TaggerName = btagger
+    alg.selectionTool.OperatingPoint = btagWP
+    alg.selectionTool.JetAuthor = jetCollection
+    alg.selectionTool.FlvTagCutDefinitionsFileName = bTagCalibFile
+    if preselection is not None:
+        alg.preselection = preselection
+    alg.selectionDecoration = 'ftag_select_' + btagger + '_' + btagWP + ',as_char'
+    seq.append( alg, inputPropName = 'particles',
+                outputPropName = 'particlesOut',
+                stageName = 'selection' )
+
+    if not noEfficiency and dataType != 'data':
+        # Set up the efficiency calculation algorithm:
+        alg = createAlgorithm( 'CP::BTaggingEfficiencyAlg',
+                               'FTagEfficiencyScaleFactorAlg' + btagger + btagWP + postfix )
+        addPrivateTool( alg, 'efficiencyTool',
+                        'BTaggingEfficiencyTool' )
+        alg.efficiencyTool.TaggerName = btagger
+        alg.efficiencyTool.OperatingPoint = btagWP
+        alg.efficiencyTool.JetAuthor = jetCollection
+        alg.efficiencyTool.ScaleFactorFileName = bTagCalibFile
+        alg.efficiencyTool.SystematicsStrategy = "Envelope"
+        alg.scaleFactorDecoration = 'ftag_effSF_' + btagger + '_' + btagWP + '_%SYS%'
+        alg.scaleFactorDecorationRegex = '(^FT_EFF_.*)'
+        alg.selectionDecoration = 'ftag_select_' + btagger + '_' + btagWP + ',as_char'
+        alg.outOfValidity = 2
+        alg.outOfValidityDeco = 'no_ftag_' + btagger + '_' + btagWP
+        if preselection is not None:
+            alg.preselection = preselection
+        seq.append( alg, inputPropName = 'jets',
+                    affectingSystematics = '(^FT_EFF_.*)',
+                    stageName = 'efficiency' )
+        pass
+
+    # Set up an algorithm used to create f-tag selection cutflow:
+    if enableCutflow:
+        alg = createAlgorithm( 'CP::ObjectCutFlowHistAlg', 'FTagCutFlowDumperAlg' + btagger + btagWP + postfix )
+        alg.histPattern = 'ftag_cflow_' + btagger + '_' + btagWP + '_%SYS%'
+        alg.selection = ['ftag_select_' + btagger + '_' + btagWP + ',as_char']
+        alg.selectionNCuts = [1] # really we have 4 cuts, but we use char
+        seq.append( alg, inputPropName = 'input',
+                    stageName = 'selection' )
+
+    # Return the sequence:
+    return seq
diff --git a/PhysicsAnalysis/Algorithms/FTagAnalysisAlgorithms/python/__init__.py b/PhysicsAnalysis/Algorithms/FTagAnalysisAlgorithms/python/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..5ccb446532599c01cc3844e5b1b18db06f721002
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/FTagAnalysisAlgorithms/python/__init__.py
@@ -0,0 +1,3 @@
+# Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+
+__version__ = '1.0.0'
diff --git a/PhysicsAnalysis/Algorithms/FTagAnalysisAlgorithms/share/FTagAnalysisAlgorithmsTest_eljob.py b/PhysicsAnalysis/Algorithms/FTagAnalysisAlgorithms/share/FTagAnalysisAlgorithmsTest_eljob.py
new file mode 100755
index 0000000000000000000000000000000000000000..44a9e6a3d26cdb1bdce0684174e80b171480ddac
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/FTagAnalysisAlgorithms/share/FTagAnalysisAlgorithmsTest_eljob.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+#
+# @author Nils Krumnack
+
+
+# Read the submission directory as a command line argument. You can
+# extend the list of arguments with your private ones later on.
+import optparse
+parser = optparse.OptionParser()
+parser.add_option( '-d', '--data-type', dest = 'data_type',
+                   action = 'store', type = 'string', default = 'data',
+                   help = 'Type of data to run over. Valid options are data, mc, afii' )
+parser.add_option( '-s', '--submission-dir', dest = 'submission_dir',
+                   action = 'store', type = 'string', default = 'submitDir',
+                   help = 'Submission directory for EventLoop' )
+parser.add_option( '-u', '--unit-test', dest='unit_test',
+                   action = 'store_true', default = False,
+                   help = 'Run the job in "unit test mode"' )
+( options, args ) = parser.parse_args()
+
+# Set up (Py)ROOT.
+import ROOT
+ROOT.xAOD.Init().ignore()
+
+# this forces the jet algorithms dictionary to be loaded before
+# anything else, which works around some strange dictionary issues I
+# don't understand.
+ROOT.CP.JetCalibrationAlg ("dummy", None)
+
+# ideally we'd run over all of them, but we don't have a mechanism to
+# configure per-sample right now
+
+dataType = options.data_type
+
+inputfile = {"data": 'ASG_TEST_FILE_DATA',
+             "mc":   'ASG_TEST_FILE_MC',
+             "afii": 'ASG_TEST_FILE_MC_AFII'}
+
+if dataType not in ["data", "mc", "afii"] :
+    raise ValueError ("invalid data type: " + dataType)
+
+# Set up the sample handler object. See comments from the C++ macro
+# for the details about these lines.
+import os
+sh = ROOT.SH.SampleHandler()
+sh.setMetaString( 'nc_tree', 'CollectionTree' )
+sample = ROOT.SH.SampleLocal (dataType)
+sample.add (os.getenv (inputfile[dataType]))
+sh.add (sample)
+sh.printContent()
+
+# Create an EventLoop job.
+job = ROOT.EL.Job()
+job.sampleHandler( sh )
+job.options().setDouble( ROOT.EL.Job.optMaxEvents, 500 )
+
+from FTagAnalysisAlgorithms.FTagAnalysisAlgorithmsTest import makeSequence
+algSeq = makeSequence (dataType)
+print algSeq # For debugging
+for alg in algSeq:
+    job.algsAdd( alg )
+    pass
+
+# Find the right output directory:
+submitDir = options.submission_dir
+if options.unit_test:
+    import os
+    import tempfile
+    submitDir = tempfile.mkdtemp( prefix = 'jetTest_'+dataType+'_', dir = os.getcwd() )
+    os.rmdir( submitDir )
+    pass
+
+# Run the job using the direct driver.
+driver = ROOT.EL.DirectDriver()
+driver.submit( job, submitDir )
diff --git a/PhysicsAnalysis/Algorithms/FTagAnalysisAlgorithms/share/FTagAnalysisAlgorithmsTest_jobOptions.py b/PhysicsAnalysis/Algorithms/FTagAnalysisAlgorithms/share/FTagAnalysisAlgorithmsTest_jobOptions.py
new file mode 100644
index 0000000000000000000000000000000000000000..91d0e0056a66e05ec5a52715da308929552e0d31
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/FTagAnalysisAlgorithms/share/FTagAnalysisAlgorithmsTest_jobOptions.py
@@ -0,0 +1,44 @@
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+#
+# @author Nils Krumnack
+
+# User options, which can be set from command line after a "-" character
+# athena EgammaAlgorithmsTest_jobOptions.py - --myOption ...
+from AthenaCommon.AthArgumentParser import AthArgumentParser
+athArgsParser = AthArgumentParser()
+athArgsParser.add_argument("--data-type", action = "store", dest = "data_type",
+                           default = "data",
+                           help = "Type of input to run over. Valid options are 'data', 'mc', 'afii'")
+athArgs = athArgsParser.parse_args()
+
+dataType = athArgs.data_type
+if not dataType in ["data", "mc", "afii"] :
+    raise Exception ("invalid data type: " + dataType)
+
+print("Running on data type: " + dataType)
+
+inputfile = {"data": 'ASG_TEST_FILE_DATA',
+             "mc":   'ASG_TEST_FILE_MC',
+             "afii": 'ASG_TEST_FILE_MC_AFII'}
+
+# Set up the reading of the input file:
+import AthenaRootComps.ReadAthenaxAODHybrid
+theApp.EvtMax = 500
+testFile = os.getenv ( inputfile[dataType] )
+svcMgr.EventSelector.InputCollections = [testFile]
+
+from FTagAnalysisAlgorithms.FTagAnalysisAlgorithmsTest import makeSequence
+algSeq = makeSequence (dataType)
+print algSeq # For debugging
+
+# Add all algorithms from the sequence to the job.
+athAlgSeq += algSeq
+
+# Set up a histogram output file for the job:
+ServiceMgr += CfgMgr.THistSvc()
+ServiceMgr.THistSvc.Output += [
+    "ANALYSIS DATAFILE='JetAnalysisAlgorithmsTest." + dataType + ".hist.root' OPT='RECREATE'"
+    ]
+
+# Reduce the printout from Athena:
+include( "AthAnalysisBaseComps/SuppressLogging.py" )
diff --git a/PhysicsAnalysis/Algorithms/FTagAnalysisAlgorithms/src/components/FTagAnalysisAlgorithms_entries.cxx b/PhysicsAnalysis/Algorithms/FTagAnalysisAlgorithms/src/components/FTagAnalysisAlgorithms_entries.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..05df68d48072e4f484febf42f5d33811d2051381
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/FTagAnalysisAlgorithms/src/components/FTagAnalysisAlgorithms_entries.cxx
@@ -0,0 +1,11 @@
+// AsgExampleTools_entries.cxx
+
+#include <GaudiKernel/DeclareFactoryEntries.h>
+
+#include <FTagAnalysisAlgorithms/BTaggingEfficiencyAlg.h>
+
+DECLARE_ALGORITHM_FACTORY (CP::BTaggingEfficiencyAlg)
+
+DECLARE_FACTORY_ENTRIES(FTagAnalysisAlgorithms) {
+  DECLARE_ALGORITHM (CP::BTaggingEfficiencyAlg)
+}
diff --git a/PhysicsAnalysis/Algorithms/FTagAnalysisAlgorithms/src/components/FTagAnalysisAlgorithms_load.cxx b/PhysicsAnalysis/Algorithms/FTagAnalysisAlgorithms/src/components/FTagAnalysisAlgorithms_load.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..7542cfe5a54de55a238454183dfac20fa8e6af9c
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/FTagAnalysisAlgorithms/src/components/FTagAnalysisAlgorithms_load.cxx
@@ -0,0 +1,5 @@
+// AsgExampleTools_load.cxx
+
+#include "GaudiKernel/LoadFactoryEntries.h"
+
+LOAD_FACTORY_ENTRIES(FTagAnalysisAlgorithms)
diff --git a/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/CMakeLists.txt b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..44c39deb7c52c37b440dd9c9dd84cf7cf6955052
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/CMakeLists.txt
@@ -0,0 +1,88 @@
+# Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+#
+# @author Nils Krumnack
+
+
+# The name of the package:
+atlas_subdir( JetAnalysisAlgorithms )
+
+atlas_depends_on_subdirs(
+    PUBLIC
+    Event/xAOD/xAODJet
+    PhysicsAnalysis/Algorithms/SelectionHelpers
+    PhysicsAnalysis/Algorithms/SystematicsHandles
+    PhysicsAnalysis/D3PDTools/AnaAlgorithm
+    Reconstruction/Jet/JetCalibTools
+    Reconstruction/Jet/JetCPInterfaces
+    Reconstruction/Jet/JetInterface
+    Reconstruction/Jet/JetJvtEfficiency
+    Reconstruction/Jet/JetResolution
+    Reconstruction/MET/METUtilities )
+
+atlas_add_library( JetAnalysisAlgorithmsLib
+   JetAnalysisAlgorithms/*.h JetAnalysisAlgorithms/*.icc Root/*.cxx
+   PUBLIC_HEADERS JetAnalysisAlgorithms
+   LINK_LIBRARIES xAODJet SelectionHelpersLib SystematicsHandlesLib
+   AnaAlgorithmLib JetCalibToolsLib JetInterface JetResolutionLib
+   JetCPInterfaces JetJvtEfficiencyLib JetAnalysisInterfacesLib METUtilitiesLib )
+
+atlas_add_dictionary( JetAnalysisAlgorithmsDict
+   JetAnalysisAlgorithms/JetAnalysisAlgorithmsDict.h
+   JetAnalysisAlgorithms/selection.xml
+   LINK_LIBRARIES JetAnalysisAlgorithmsLib )
+
+if( NOT XAOD_STANDALONE )
+   atlas_add_component( JetAnalysisAlgorithms
+      src/*.h src/*.cxx src/components/*.cxx
+      LINK_LIBRARIES GaudiKernel JetAnalysisAlgorithmsLib )
+endif()
+
+atlas_install_python_modules( python/*.py )
+atlas_install_joboptions( share/*_jobOptions.py )
+atlas_install_scripts( share/*_eljob.py )
+
+if( XAOD_STANDALONE )
+   atlas_add_test( testJobDataEMTopo
+      SCRIPT JetAnalysisAlgorithmsTest_EMTopo_eljob.py --data-type data --unit-test
+      PROPERTIES TIMEOUT 600 )
+   atlas_add_test( testJobDataPFlow
+      SCRIPT JetAnalysisAlgorithmsTest_PFlow_eljob.py --data-type data --unit-test
+      PROPERTIES TIMEOUT 600 )
+   atlas_add_test( testJobFullSimEMTopo
+      SCRIPT JetAnalysisAlgorithmsTest_EMTopo_eljob.py --data-type mc --unit-test
+      PROPERTIES TIMEOUT 600 )
+   atlas_add_test( testJobFullSimPFlow
+      SCRIPT JetAnalysisAlgorithmsTest_PFlow_eljob.py --data-type mc --unit-test
+      PROPERTIES TIMEOUT 600 )
+   atlas_add_test( testJobFastSimEMTopo
+      SCRIPT JetAnalysisAlgorithmsTest_EMTopo_eljob.py --data-type afii --unit-test
+      PROPERTIES TIMEOUT 600 )
+   atlas_add_test( testJobFastSimPFlow
+      SCRIPT JetAnalysisAlgorithmsTest_PFlow_eljob.py --data-type afii --unit-test
+      PROPERTIES TIMEOUT 600 )
+else()
+   atlas_add_test( testJobDataEMTopo
+      SCRIPT athena.py
+      JetAnalysisAlgorithms/JetAnalysisAlgorithmsTest_EMTopo_jobOptions.py - --data-type data
+      PROPERTIES TIMEOUT 600 )
+   atlas_add_test( testJobDataPFlow
+      SCRIPT athena.py
+      JetAnalysisAlgorithms/JetAnalysisAlgorithmsTest_PFlow_jobOptions.py - --data-type data
+      PROPERTIES TIMEOUT 600 )
+   atlas_add_test( testJobFullSimEMTopo
+      SCRIPT athena.py
+      JetAnalysisAlgorithms/JetAnalysisAlgorithmsTest_EMTopo_jobOptions.py - --data-type mc
+      PROPERTIES TIMEOUT 600 )
+   atlas_add_test( testJobFullSimPFlow
+      SCRIPT athena.py
+      JetAnalysisAlgorithms/JetAnalysisAlgorithmsTest_PFlow_jobOptions.py - --data-type mc
+      PROPERTIES TIMEOUT 600 )
+   atlas_add_test( testJobFastSimEMTopo
+      SCRIPT athena.py
+      JetAnalysisAlgorithms/JetAnalysisAlgorithmsTest_EMTopo_jobOptions.py - --data-type afii
+      PROPERTIES TIMEOUT 600 )
+   atlas_add_test( testJobFastSimPFlow
+      SCRIPT athena.py
+      JetAnalysisAlgorithms/JetAnalysisAlgorithmsTest_PFlow_jobOptions.py - --data-type afii
+      PROPERTIES TIMEOUT 600 )
+endif()
diff --git a/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/JetAnalysisAlgorithms/JetAnalysisAlgorithmsDict.h b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/JetAnalysisAlgorithms/JetAnalysisAlgorithmsDict.h
new file mode 100644
index 0000000000000000000000000000000000000000..3f2128c28272d43e761db7c4fb7aeee482afcea6
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/JetAnalysisAlgorithms/JetAnalysisAlgorithmsDict.h
@@ -0,0 +1,20 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef JET_ANALYSIS_ALGORITHMS__JET_ANALYSIS_ALGORITHMS_DICT_H
+#define JET_ANALYSIS_ALGORITHMS__JET_ANALYSIS_ALGORITHMS_DICT_H
+
+#include <JetAnalysisAlgorithms/JetCalibrationAlg.h>
+#include <JetAnalysisAlgorithms/JetGhostMuonAssociationAlg.h>
+#include <JetAnalysisAlgorithms/JetModifierAlg.h>
+#include <JetAnalysisAlgorithms/JetSelectionAlg.h>
+#include <JetAnalysisAlgorithms/JetSmearingAlg.h>
+#include <JetAnalysisAlgorithms/JetUncertaintiesAlg.h>
+#include <JetAnalysisAlgorithms/JvtEfficiencyAlg.h>
+#include <JetAnalysisAlgorithms/JvtUpdateAlg.h>
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/JetAnalysisAlgorithms/JetCalibrationAlg.h b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/JetAnalysisAlgorithms/JetCalibrationAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..61a745b581ca356d5ddba1d5cff8cc38e5de5a5a
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/JetAnalysisAlgorithms/JetCalibrationAlg.h
@@ -0,0 +1,62 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef JET_ANALYSIS_ALGORITHMS__JET_CALIBRATION_ALG_H
+#define JET_ANALYSIS_ALGORITHMS__JET_CALIBRATION_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <JetCalibTools/IJetCalibrationTool.h>
+#include <SelectionHelpers/OutOfValidityHelper.h>
+#include <SelectionHelpers/SelectionReadHandle.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+
+namespace CP
+{
+  /// \brief an algorithm for calling \ref IJetCalibrationTool
+
+  class JetCalibrationAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    JetCalibrationAlg (const std::string& name, 
+                       ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+    
+
+
+    /// \brief the calibration tool
+  private:
+    ToolHandle<IJetCalibrationTool> m_calibrationTool;
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the jet collection we run on
+  private:
+    SysCopyHandle<xAOD::JetContainer> m_jetHandle {
+      this, "jets", "AntiKt4EMTopoJets", "the jet collection to run on"};
+
+    /// \brief the preselection we apply to our input
+  private:
+    SelectionReadHandle m_preselection {
+      this, "preselection", "", "the preselection to apply"};
+
+    /// \brief the helper for OutOfValidity results
+  private:
+    OutOfValidityHelper m_outOfValidity {this};
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/JetAnalysisAlgorithms/JetGhostMuonAssociationAlg.h b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/JetAnalysisAlgorithms/JetGhostMuonAssociationAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..66951ced52797c91cb2d83a64fd81dfeb807b47f
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/JetAnalysisAlgorithms/JetGhostMuonAssociationAlg.h
@@ -0,0 +1,45 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Tadej Novak
+
+
+#ifndef JET_ANALYSIS_ALGORITHMS__JET_GHOST_MUON_ASSOCIATION_ALG_H
+#define JET_ANALYSIS_ALGORITHMS__JET_GHOST_MUON_ASSOCIATION_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+#include <xAODJet/JetContainer.h>
+
+namespace CP
+{
+  /// \brief an algorithm for adding ghost muons to jets
+
+  class JetGhostMuonAssociationAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    JetGhostMuonAssociationAlg (const std::string& name, 
+                                ISvcLocator* pSvcLocator);
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the jet collection we run on
+  private:
+    SysCopyHandle<xAOD::JetContainer> m_jetHandle {
+      this, "jets", "AntiKt4EMTopoJets", "the jet collection to run on"};
+  };
+}
+
+#endif // JET_ANALYSIS_ALGORITHMS__JET_GHOST_MUON_ASSOCIATION_ALG_H
diff --git a/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/JetAnalysisAlgorithms/JetModifierAlg.h b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/JetAnalysisAlgorithms/JetModifierAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..548e29202b20e43bcf490fe4f78265e15720ad75
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/JetAnalysisAlgorithms/JetModifierAlg.h
@@ -0,0 +1,56 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef JET_ANALYSIS_ALGORITHMS__JET_MODIFIER_ALG_H
+#define JET_ANALYSIS_ALGORITHMS__JET_MODIFIER_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <JetInterface/IJetModifier.h>
+#include <SelectionHelpers/OutOfValidityHelper.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+
+namespace CP
+{
+  /// \brief an algorithm for calling \ref IJetModifierTool
+
+  class JetModifierAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    JetModifierAlg (const std::string& name, 
+                       ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+    
+
+
+    /// \brief the modifier tool
+  private:
+    ToolHandle<IJetModifier> m_modifierTool;
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the jet collection we run on
+  private:
+    SysCopyHandle<xAOD::JetContainer> m_jetHandle {
+      this, "jets", "AntiKt4EMTopoJets", "the jet collection to run on"};
+
+    /// \brief the helper for OutOfValidity results
+  private:
+    OutOfValidityHelper m_outOfValidity {this};
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/JetAnalysisAlgorithms/JetSelectionAlg.h b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/JetAnalysisAlgorithms/JetSelectionAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..14e727242a5c770df989c32222ebc705d9029ff1
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/JetAnalysisAlgorithms/JetSelectionAlg.h
@@ -0,0 +1,67 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef JET_ANALYSIS_ALGORITHMS__JET_SELECTION_ALG_H
+#define JET_ANALYSIS_ALGORITHMS__JET_SELECTION_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <JetInterface/IJetSelector.h>
+#include <SelectionHelpers/ISelectionAccessor.h>
+#include <SelectionHelpers/SelectionReadHandle.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+#include <xAODJet/JetContainer.h>
+
+namespace CP
+{
+  /// \brief an algorithm for calling \ref IJetSelector
+
+  class JetSelectionAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    JetSelectionAlg (const std::string& name, 
+                         ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+    
+
+
+    /// \brief the selection tool
+  private:
+    ToolHandle<IJetSelector> m_selectionTool;
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the jet collection we run on
+  private:
+    SysCopyHandle<xAOD::JetContainer> m_jetHandle {
+      this, "jets", "AntiKt4EMTopoJets", "the jet collection to run on"};
+
+    /// \brief the preselection we apply to our input
+  private:
+    SelectionReadHandle m_preselection {
+      this, "preselection", "", "the preselection to apply"};
+
+    /// \brief the decoration for the jet selection
+  private:
+    std::string m_selectionDecoration {"clean_jet"};
+
+    /// \brief the accessor for \ref m_selectionDecoration
+  private:
+    std::unique_ptr<ISelectionAccessor> m_selectionAccessor;
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/JetAnalysisAlgorithms/JetSmearingAlg.h b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/JetAnalysisAlgorithms/JetSmearingAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..ef56cf1848191122ce48b1dcc1e1baa3753690d5
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/JetAnalysisAlgorithms/JetSmearingAlg.h
@@ -0,0 +1,63 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef JET_ANALYSIS_ALGORITHMS__JET_SMEARING_ALG_H
+#define JET_ANALYSIS_ALGORITHMS__JET_SMEARING_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <JetResolution/IJERSmearingTool.h>
+#include <SelectionHelpers/OutOfValidityHelper.h>
+#include <SelectionHelpers/SelectionReadHandle.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+#include <xAODJet/JetContainer.h>
+
+namespace CP
+{
+  /// \brief an algorithm for calling \ref IJERSmearingTool
+
+  class JetSmearingAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    JetSmearingAlg (const std::string& name, 
+                         ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+    
+
+
+    /// \brief the smearing tool
+  private:
+    ToolHandle<IJERSmearingTool> m_smearingTool;
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the jet collection we run on
+  private:
+    SysCopyHandle<xAOD::JetContainer> m_jetHandle {
+      this, "jets", "AntiKt4EMTopoJets", "the jet collection to run on"};
+
+    /// \brief the preselection we apply to our input
+  private:
+    SelectionReadHandle m_preselection {
+      this, "preselection", "", "the preselection to apply"};
+
+    /// \brief the helper for OutOfValidity results
+  private:
+    OutOfValidityHelper m_outOfValidity {this};
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/JetAnalysisAlgorithms/JetUncertaintiesAlg.h b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/JetAnalysisAlgorithms/JetUncertaintiesAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..70efa430e4a30bc401e42ed4ef42e8eb1b50fa58
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/JetAnalysisAlgorithms/JetUncertaintiesAlg.h
@@ -0,0 +1,62 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef JET_ANALYSIS_ALGORITHMS__JET_UNCERTAINTIES_ALG_H
+#define JET_ANALYSIS_ALGORITHMS__JET_UNCERTAINTIES_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <JetCPInterfaces/ICPJetUncertaintiesTool.h>
+#include <SelectionHelpers/OutOfValidityHelper.h>
+#include <SelectionHelpers/SelectionReadHandle.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+
+namespace CP
+{
+  /// \brief an algorithm for calling \ref ICPJetUncertaintiesTool
+
+  class JetUncertaintiesAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    JetUncertaintiesAlg (const std::string& name, 
+                         ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+    
+
+
+    /// \brief the uncertainties tool
+  private:
+    ToolHandle<ICPJetUncertaintiesTool> m_uncertaintiesTool;
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the jet collection we run on
+  private:
+    SysCopyHandle<xAOD::JetContainer> m_jetHandle {
+      this, "jets", "AntiKt4EMTopoJets", "the jet collection to run on"};
+
+    /// \brief the preselection we apply to our input
+  private:
+    SelectionReadHandle m_preselection {
+      this, "preselection", "", "the preselection to apply"};
+
+    /// \brief the helper for OutOfValidity results
+  private:
+    OutOfValidityHelper m_outOfValidity {this};
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/JetAnalysisAlgorithms/JvtEfficiencyAlg.h b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/JetAnalysisAlgorithms/JvtEfficiencyAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..e4850b108274b8e8c8410c59079b1478e794b707
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/JetAnalysisAlgorithms/JvtEfficiencyAlg.h
@@ -0,0 +1,98 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef JET_ANALYSIS_ALGORITHMS__JVT_EFFICIENCY_ALG_H
+#define JET_ANALYSIS_ALGORITHMS__JVT_EFFICIENCY_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <JetAnalysisInterfaces/IJetJvtEfficiency.h>
+#include <SelectionHelpers/ISelectionAccessor.h>
+#include <SelectionHelpers/OutOfValidityHelper.h>
+#include <SelectionHelpers/SelectionReadHandle.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <SystematicsHandles/SysDecorationHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+#include <xAODJet/JetContainer.h>
+
+namespace CP
+{
+  /// \brief an algorithm for calling \ref IJEREfficiencyTool
+
+  class JvtEfficiencyAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    JvtEfficiencyAlg (const std::string& name, 
+                         ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+    
+
+
+    /// \brief the efficiency tool
+  private:
+    ToolHandle<CP::IJetJvtEfficiency> m_efficiencyTool;
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the jet collection we run on
+  private:
+    SysCopyHandle<xAOD::JetContainer> m_jetHandle {
+      this, "jets", "AntiKt4EMTopoJets", "the jet collection to run on"};
+
+    /// \brief the preselection we apply to our input
+  private:
+    SelectionReadHandle m_preselection {
+      this, "preselection", "", "the preselection to apply"};
+
+    /// \brief the truth jet collection to use
+  private:
+    std::string m_truthJetsName;
+
+    /// \brief differenciate between JVT and fJVT
+  private:
+    bool  m_dofJVT = false;
+
+    /// \brief the decoration for the fJVT selection
+  private:
+    std::string m_fJVTStatus;
+
+    /// \brief the accessor for \ref m_fJVTStatus
+  private:
+    std::unique_ptr<ISelectionAccessor> m_fJVTStatusAccessor;
+
+    /// \brief the decoration for the JVT selection
+  private:
+    std::string m_selection;
+
+    /// \brief the accessor for \ref m_selection
+  private:
+    std::unique_ptr<ISelectionAccessor> m_selectionAccessor;
+
+    /// \brief the decoration for the JVT scale factor
+  private:
+    SysDecorationHandle<float> m_scaleFactorDecoration {
+      this, "scaleFactorDecoration", "", "the decoration for the JVT efficiency scale factor"};
+
+    /// \brief whether to skip efficiency calculation if the selection failed
+  private:
+    bool m_skipBadEfficiency = false;
+
+    /// \brief the helper for OutOfValidity results
+  private:
+    OutOfValidityHelper m_outOfValidity {this};
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/JetAnalysisAlgorithms/JvtUpdateAlg.h b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/JetAnalysisAlgorithms/JvtUpdateAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..bc3b8c1c8fd4936259fe342c50a1763e71644528
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/JetAnalysisAlgorithms/JvtUpdateAlg.h
@@ -0,0 +1,66 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef JET_ANALYSIS_ALGORITHMS__JVT_UPDATE_ALG_H
+#define JET_ANALYSIS_ALGORITHMS__JVT_UPDATE_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <JetInterface/IJetUpdateJvt.h>
+#include <SelectionHelpers/SelectionReadHandle.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+#include <xAODJet/JetContainer.h>
+
+namespace CP
+{
+  /// \brief an algorithm for calling \ref IJetUpdateJvt
+
+  class JvtUpdateAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    JvtUpdateAlg (const std::string& name, 
+                  ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+    
+
+
+    /// \brief the update tool
+  private:
+    ToolHandle<IJetUpdateJvt> m_jvtTool;
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the jet collection we run on
+  private:
+    SysCopyHandle<xAOD::JetContainer> m_jetHandle {
+      this, "jets", "AntiKt4EMTopoJets", "the jet collection to run on"};
+
+    /// \brief the preselection we apply to our input
+  private:
+    SelectionReadHandle m_preselection {
+      this, "preselection", "", "the preselection to apply"};
+
+    /// \brief the name of the decoration we create
+  private:
+    std::string m_decorationName {"Jvt"};
+
+    /// \brief the decoration accessor we use
+  private:
+    std::unique_ptr<SG::AuxElement::Accessor<float> > m_decorationAccessor;
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/JetAnalysisAlgorithms/selection.xml b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/JetAnalysisAlgorithms/selection.xml
new file mode 100644
index 0000000000000000000000000000000000000000..a851d1f967b420f8c737f111984a2d02834e751c
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/JetAnalysisAlgorithms/selection.xml
@@ -0,0 +1,12 @@
+<lcgdict>
+
+   <class name="CP::JetCalibrationAlg" />
+   <class name="CP::JetGhostMuonAssociationAlg" />
+   <class name="CP::JetModifierAlg" />
+   <class name="CP::JetSelectionAlg" />
+   <class name="CP::JetSmearingAlg" />
+   <class name="CP::JetUncertaintiesAlg" />
+   <class name="CP::JvtEfficiencyAlg" />
+   <class name="CP::JvtUpdateAlg" />
+   
+</lcgdict>
diff --git a/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/Root/JetCalibrationAlg.cxx b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/Root/JetCalibrationAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..3f820c72a55a216a44e3f77cf45ce9cf7631d353
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/Root/JetCalibrationAlg.cxx
@@ -0,0 +1,60 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <JetAnalysisAlgorithms/JetCalibrationAlg.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  JetCalibrationAlg ::
+  JetCalibrationAlg (const std::string& name, 
+                     ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+    , m_calibrationTool ("JetCalibrationTool", this)
+  {
+    declareProperty ("calibrationTool", m_calibrationTool, "the calibration tool we apply");
+  }
+
+
+
+  StatusCode JetCalibrationAlg ::
+  initialize ()
+  {
+    ANA_CHECK (m_calibrationTool.retrieve());
+    m_systematicsList.addHandle (m_jetHandle);
+    ANA_CHECK (m_systematicsList.initialize());
+    ANA_CHECK (m_preselection.initialize());
+    ANA_CHECK (m_outOfValidity.initialize());
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode JetCalibrationAlg ::
+  execute ()
+  {
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode {
+        xAOD::JetContainer *jets = nullptr;
+        ANA_CHECK (m_jetHandle.getCopy (jets, sys));
+        for (xAOD::Jet *jet : *jets)
+        {
+          if (m_preselection.getBool (*jet))
+          {
+            ANA_CHECK_CORRECTION (m_outOfValidity, *jet, m_calibrationTool->applyCorrection (*jet));
+          }
+        }
+        return StatusCode::SUCCESS;
+      });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/Root/JetGhostMuonAssociationAlg.cxx b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/Root/JetGhostMuonAssociationAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..278eac5c369a0ce868e484a6b68ceb0a37823fb8
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/Root/JetGhostMuonAssociationAlg.cxx
@@ -0,0 +1,53 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Tadej Novak
+
+//
+// includes
+//
+
+#include <JetAnalysisAlgorithms/JetGhostMuonAssociationAlg.h>
+#include <METUtilities/METHelpers.h>
+#include <xAODMuon/MuonContainer.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  JetGhostMuonAssociationAlg ::
+  JetGhostMuonAssociationAlg (const std::string& name, 
+                              ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+  {
+  }
+
+
+  StatusCode JetGhostMuonAssociationAlg ::
+  initialize ()
+  {
+    m_systematicsList.addHandle (m_jetHandle);
+    ANA_CHECK (m_systematicsList.initialize());
+    return StatusCode::SUCCESS;
+  }
+
+
+  StatusCode JetGhostMuonAssociationAlg ::
+  execute ()
+  {
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode {
+      xAOD::JetContainer *jets = nullptr;
+      ANA_CHECK (m_jetHandle.getCopy (jets, sys));
+
+      // associate the ghost muons to the jets (needed by MET muon-jet OR later)
+      const xAOD::MuonContainer* muons = nullptr;
+      ATH_CHECK( evtStore()->retrieve(muons, "Muons") );
+      met::addGhostMuonsToJets(*muons, *jets);
+
+      return StatusCode::SUCCESS;
+    });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/Root/JetModifierAlg.cxx b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/Root/JetModifierAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..9ebd150c9db8794bb8d5d8130e23e32fb5c2e596
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/Root/JetModifierAlg.cxx
@@ -0,0 +1,57 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <JetAnalysisAlgorithms/JetModifierAlg.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  JetModifierAlg ::
+  JetModifierAlg (const std::string& name, 
+                     ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+    , m_modifierTool ("JetForwardJvtTool", this)
+  {
+    declareProperty ("modifierTool", m_modifierTool, "the modifier tool we apply");
+  }
+
+
+
+  StatusCode JetModifierAlg ::
+  initialize ()
+  {
+    ANA_CHECK (m_modifierTool.retrieve());
+    m_systematicsList.addHandle (m_jetHandle);
+    ANA_CHECK (m_systematicsList.initialize());
+    ANA_CHECK (m_outOfValidity.initialize());
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode JetModifierAlg ::
+  execute ()
+  {
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode {
+        xAOD::JetContainer *jets = nullptr;
+        ANA_CHECK (m_jetHandle.getCopy (jets, sys));
+        if (m_modifierTool->modify (*jets) != 0)
+        {
+          ANA_MSG_ERROR ("Failed to call \"m_modifierTool->modify (*jets)\"");
+          return StatusCode::FAILURE;
+        }
+        return StatusCode::SUCCESS;
+      });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/Root/JetSelectionAlg.cxx b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/Root/JetSelectionAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..575adc9b7d29baa6467cc0bb5690de358094bad0
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/Root/JetSelectionAlg.cxx
@@ -0,0 +1,71 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <JetAnalysisAlgorithms/JetSelectionAlg.h>
+
+#include <SelectionHelpers/SelectionHelpers.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  JetSelectionAlg ::
+  JetSelectionAlg (const std::string& name, 
+                     ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+    , m_selectionTool ("", this)
+  {
+    declareProperty ("selectionTool", m_selectionTool, "the selection tool we apply");
+    declareProperty ("selectionDecoration", m_selectionDecoration, "the decoration for the jet selection");
+  }
+
+
+
+  StatusCode JetSelectionAlg ::
+  initialize ()
+  {
+    ANA_CHECK (m_selectionTool.retrieve());
+    m_systematicsList.addHandle (m_jetHandle);
+    ANA_CHECK (m_systematicsList.initialize());
+    ANA_CHECK (m_preselection.initialize());
+
+    if (m_selectionDecoration.empty())
+    {
+      ANA_MSG_ERROR ("no selection decoration name set");
+      return StatusCode::FAILURE;
+    }
+    ANA_CHECK (makeSelectionAccessor (m_selectionDecoration, m_selectionAccessor));
+
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode JetSelectionAlg ::
+  execute ()
+  {
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode {
+        xAOD::JetContainer *jets = nullptr;
+        ANA_CHECK (m_jetHandle.getCopy (jets, sys));
+        for (xAOD::Jet *jet : *jets)
+        {
+          if (m_preselection.getBool (*jet))
+          {
+            m_selectionAccessor->setBool
+              (*jet, m_selectionTool->keep(*jet));
+          }
+        }
+        return StatusCode::SUCCESS;
+      });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/Root/JetSmearingAlg.cxx b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/Root/JetSmearingAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..55438e290a57f925ac07b9b2c8814eb6c95bdc63
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/Root/JetSmearingAlg.cxx
@@ -0,0 +1,62 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <JetAnalysisAlgorithms/JetSmearingAlg.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  JetSmearingAlg ::
+  JetSmearingAlg (const std::string& name, 
+                     ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+    , m_smearingTool ("JERSmearingTool", this)
+  {
+    declareProperty ("smearingTool", m_smearingTool, "the smearing tool we apply");
+  }
+
+
+
+  StatusCode JetSmearingAlg ::
+  initialize ()
+  {
+    ANA_CHECK (m_smearingTool.retrieve());
+    m_systematicsList.addHandle (m_jetHandle);
+    ANA_CHECK (m_systematicsList.addAffectingSystematics (m_smearingTool->affectingSystematics()));
+    ANA_CHECK (m_systematicsList.initialize());
+    ANA_CHECK (m_preselection.initialize());
+    ANA_CHECK (m_outOfValidity.initialize());
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode JetSmearingAlg ::
+  execute ()
+  {
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode {
+        ANA_CHECK (m_smearingTool->applySystematicVariation (sys));
+        xAOD::JetContainer *jets = nullptr;
+        ANA_CHECK (m_jetHandle.getCopy (jets, sys));
+        for (xAOD::Jet *jet : *jets)
+        {
+          if (m_preselection.getBool (*jet))
+          {
+            ANA_CHECK_CORRECTION (m_outOfValidity, *jet, m_smearingTool->applyCorrection (*jet));
+          }
+        }
+        return StatusCode::SUCCESS;
+      });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/Root/JetUncertaintiesAlg.cxx b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/Root/JetUncertaintiesAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..ed1f72889d6956a90ea0cda6f58ef1da5c518fff
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/Root/JetUncertaintiesAlg.cxx
@@ -0,0 +1,62 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <JetAnalysisAlgorithms/JetUncertaintiesAlg.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  JetUncertaintiesAlg ::
+  JetUncertaintiesAlg (const std::string& name, 
+                     ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+    , m_uncertaintiesTool ("JetUncertaintiesTool", this)
+  {
+    declareProperty ("uncertaintiesTool", m_uncertaintiesTool, "the uncertainties tool we apply");
+  }
+
+
+
+  StatusCode JetUncertaintiesAlg ::
+  initialize ()
+  {
+    ANA_CHECK (m_uncertaintiesTool.retrieve());
+    m_systematicsList.addHandle (m_jetHandle);
+    ANA_CHECK (m_systematicsList.addAffectingSystematics (m_uncertaintiesTool->affectingSystematics()));
+    ANA_CHECK (m_systematicsList.initialize());
+    ANA_CHECK (m_preselection.initialize());
+    ANA_CHECK (m_outOfValidity.initialize());
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode JetUncertaintiesAlg ::
+  execute ()
+  {
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode {
+        ANA_CHECK (m_uncertaintiesTool->applySystematicVariation (sys));
+        xAOD::JetContainer *jets = nullptr;
+        ANA_CHECK (m_jetHandle.getCopy (jets, sys));
+        for (xAOD::Jet *jet : *jets)
+        {
+          if (m_preselection.getBool (*jet))
+          {
+            ANA_CHECK_CORRECTION (m_outOfValidity, *jet, m_uncertaintiesTool->applyCorrection (*jet));
+          }
+        }
+        return StatusCode::SUCCESS;
+      });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/Root/JvtEfficiencyAlg.cxx b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/Root/JvtEfficiencyAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..765a6f635149c3fc7262926af512e207c100cb69
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/Root/JvtEfficiencyAlg.cxx
@@ -0,0 +1,114 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <JetAnalysisAlgorithms/JvtEfficiencyAlg.h>
+
+#include <SelectionHelpers/SelectionHelpers.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  JvtEfficiencyAlg ::
+  JvtEfficiencyAlg (const std::string& name, 
+                     ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+    , m_efficiencyTool ("", this)
+    , m_truthJetsName("AntiKt4TruthJets")
+  {
+    declareProperty ("efficiencyTool", m_efficiencyTool, "the efficiency tool we apply");
+    declareProperty ("dofJVT", m_dofJVT, "differenciate between JVT and fJVT");
+    declareProperty ("fJVTStatus", m_fJVTStatus, "the decoration for the fJVT status");
+    declareProperty ("selection", m_selection, "the decoration for the JVT selection");
+    declareProperty ("skipBadEfficiency", m_skipBadEfficiency, "whether to skip efficiency calculation if the selection failed");
+    declareProperty ("truthJetCollection", m_truthJetsName, "the truth jet collection to use for truth tagging");
+  }
+
+
+
+  StatusCode JvtEfficiencyAlg ::
+  initialize ()
+  {
+    if (m_dofJVT && m_fJVTStatus.empty())
+    {
+      ANA_MSG_ERROR ("fJVTStatus decoration needs to be configured when running fJVT");
+      return StatusCode::FAILURE;
+    }
+
+    ANA_CHECK (m_efficiencyTool.retrieve());
+    m_systematicsList.addHandle (m_jetHandle);
+    ANA_CHECK (m_systematicsList.addAffectingSystematics (m_efficiencyTool->affectingSystematics()));
+    ANA_CHECK (m_systematicsList.initialize());
+    ANA_CHECK (m_preselection.initialize());
+    ANA_CHECK (m_outOfValidity.initialize());
+
+    if (m_dofJVT && !m_fJVTStatus.empty())
+      ANA_CHECK (makeSelectionAccessor (m_fJVTStatus, m_fJVTStatusAccessor));
+
+    if (!m_selection.empty())
+      ANA_CHECK (makeSelectionAccessor (m_selection, m_selectionAccessor));
+
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode JvtEfficiencyAlg ::
+  execute ()
+  {
+    ANA_CHECK (m_scaleFactorDecoration.preExecute (m_systematicsList));
+
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode {
+        ANA_CHECK (m_efficiencyTool->applySystematicVariation (sys));
+        xAOD::JetContainer *jets = nullptr;
+        ANA_CHECK (m_jetHandle.getCopy (jets, sys));
+
+        const xAOD::JetContainer *truthjets = nullptr;
+	if(!m_truthJetsName.empty()) {
+	  ANA_CHECK(evtStore()->retrieve(truthjets,m_truthJetsName));
+	  ANA_CHECK(m_efficiencyTool->tagTruth(jets,truthjets));
+	}
+
+        for (xAOD::Jet *jet : *jets)
+        {
+          if (m_preselection.getBool (*jet))
+          {
+            bool goodJet = true;
+            if (m_selectionAccessor || m_skipBadEfficiency)
+            {
+              goodJet = m_dofJVT ? m_fJVTStatusAccessor->getBool (*jet) : m_efficiencyTool->passesJvtCut (*jet);
+              if (m_selectionAccessor)
+                m_selectionAccessor->setBool (*jet, goodJet);
+            }
+            if (m_scaleFactorDecoration)
+            {
+              float sf = 1;
+              if (goodJet) {
+                ANA_CHECK_CORRECTION (m_outOfValidity, *jet, m_efficiencyTool->getEfficiencyScaleFactor (*jet, sf));
+              } else if (!m_skipBadEfficiency) {
+                ANA_CHECK_CORRECTION (m_outOfValidity, *jet, m_efficiencyTool->getInefficiencyScaleFactor (*jet, sf));
+              }
+              m_scaleFactorDecoration.set (*jet, sf, sys);
+            }
+          } else {
+            if (m_selectionAccessor)
+              m_selectionAccessor->setBool (*jet, false);
+
+            if (m_scaleFactorDecoration)
+              m_scaleFactorDecoration.set (*jet, invalidScaleFactor(), sys);
+          }
+        }
+        return StatusCode::SUCCESS;
+      });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/Root/JvtUpdateAlg.cxx b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/Root/JvtUpdateAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..15df9bf8c98a34454cbf952b3e7d99e8b75bb094
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/Root/JvtUpdateAlg.cxx
@@ -0,0 +1,70 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <JetAnalysisAlgorithms/JvtUpdateAlg.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  JvtUpdateAlg ::
+  JvtUpdateAlg (const std::string& name, 
+                ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+    , m_jvtTool ("", this)
+  {
+    declareProperty ("jvtTool", m_jvtTool, "the jvt tool we apply");
+    declareProperty ("decorationName", m_decorationName, "the decoration name to use");
+  }
+
+
+
+  StatusCode JvtUpdateAlg ::
+  initialize ()
+  {
+    if (m_decorationName.empty())
+    {
+      ANA_MSG_ERROR ("decoration name set to empty string, not allowed");
+      return StatusCode::FAILURE;
+    }
+    m_decorationAccessor = std::make_unique
+      <SG::AuxElement::Accessor<float> > (m_decorationName);
+
+    ANA_CHECK (m_jvtTool.retrieve());
+    m_systematicsList.addHandle (m_jetHandle);
+    ANA_CHECK (m_systematicsList.initialize());
+    ANA_CHECK (m_preselection.initialize());
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode JvtUpdateAlg ::
+  execute ()
+  {
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode {
+        xAOD::JetContainer *jets = nullptr;
+        ANA_CHECK (m_jetHandle.getCopy (jets, sys));
+        for (xAOD::Jet *jet : *jets)
+        {
+          if (m_preselection.getBool (*jet))
+          {
+            // manually update jvt decoration using the tool
+            const float jvt = m_jvtTool->updateJvt (*jet);
+            (*m_decorationAccessor) (*jet) = jvt;
+          }
+        }
+        return StatusCode::SUCCESS;
+      });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/python/JetAnalysisAlgorithmsTest.py b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/python/JetAnalysisAlgorithmsTest.py
new file mode 100644
index 0000000000000000000000000000000000000000..6d04dd9bd5da853a4c566f00de62dca09a75343b
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/python/JetAnalysisAlgorithmsTest.py
@@ -0,0 +1,71 @@
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+#
+# @author Nils Krumnack
+
+from AnaAlgorithm.AlgSequence import AlgSequence
+from AnaAlgorithm.DualUseConfig import createAlgorithm
+
+def makeSequence (dataType, jetContainer="AntiKt4EMPFlowJets") :
+
+    # config
+    
+
+    algSeq = AlgSequence()
+
+    # Set up the systematics loader/handler algorithm:
+    sysLoader = createAlgorithm( 'CP::SysListLoaderAlg', 'SysLoaderAlg' )
+    sysLoader.sigmaRecommended = 1
+    algSeq += sysLoader
+
+    # Include, and then set up the pileup analysis sequence:
+    from AsgAnalysisAlgorithms.PileupAnalysisSequence import \
+        makePileupAnalysisSequence
+    pileupSequence = makePileupAnalysisSequence( dataType )
+    pileupSequence.configure( inputName = 'EventInfo', outputName = 'EventInfo_%SYS%' )
+    print( pileupSequence ) # For debugging
+
+    # Include, and then set up the jet analysis algorithm sequence:
+    from JetAnalysisAlgorithms.JetAnalysisSequence import makeJetAnalysisSequence
+    jetSequence = makeJetAnalysisSequence( dataType, jetContainer, enableCutflow=True, enableKinematicHistograms=True )
+    jetSequence.configure( inputName = jetContainer, outputName = 'AnalysisJetsBase_%SYS%' )
+    print( jetSequence ) # For debugging
+
+    # Include, and then set up the jet analysis algorithm sequence:
+    from JetAnalysisAlgorithms.JetJvtAnalysisSequence import makeJetJvtAnalysisSequence
+    jvtSequence = makeJetJvtAnalysisSequence( dataType, jetContainer, enableCutflow=True )
+    jvtSequence.configure( inputName = { 'eventInfo' : 'EventInfo_%SYS%',
+                                         'jets'      : 'AnalysisJetsBase_%SYS%' },
+                           outputName = { 'jets'      : 'AnalysisJets_%SYS%' },
+                           affectingSystematics = { 'jets' : jetSequence.affectingSystematics() } )
+    print( jvtSequence ) # For debugging
+
+    # Add the sequences to the job:
+    algSeq += pileupSequence
+    algSeq += jetSequence
+    algSeq += jvtSequence
+
+    # Set up an ntuple to check the job with:
+    treeMaker = createAlgorithm( 'CP::TreeMakerAlg', 'TreeMaker' )
+    treeMaker.TreeName = 'jets'
+    algSeq += treeMaker
+    ntupleMaker = createAlgorithm( 'CP::AsgxAODNTupleMakerAlg', 'NTupleMaker' )
+    ntupleMaker.TreeName = 'jets'
+    ntupleMaker.Branches = [
+        'EventInfo.runNumber   -> runNumber',
+        'EventInfo.eventNumber -> eventNumber',
+        'AnalysisJets_%SYS%.pt -> jet_%SYS%_pt',
+        ]
+    if dataType != 'data':
+        ntupleMaker.Branches += [
+            # 'EventInfo.jvt_effSF_%SYS% -> jvtSF_%SYS%',
+            # 'EventInfo.fjvt_effSF_%SYS% -> fjvtSF_%SYS%',
+            'AnalysisJets_%SYS%.jvt_effSF_NOSYS -> jet_%SYS%_jvtEfficiency',
+            # 'AnalysisJets_%SYS%.fjvt_effSF_NOSYS -> jet_%SYS%_fjvtEfficiency',
+            ]
+        ntupleMaker.systematicsRegex = '(^$)|(^JET_.*)'
+        algSeq += ntupleMaker
+    treeFiller = createAlgorithm( 'CP::TreeFillerAlg', 'TreeFiller' )
+    treeFiller.TreeName = 'jets'
+    algSeq += treeFiller
+
+    return algSeq
diff --git a/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/python/JetAnalysisSequence.py b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/python/JetAnalysisSequence.py
new file mode 100644
index 0000000000000000000000000000000000000000..5dae4f300888839fc01a6e0268a3b097dfb5ceca
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/python/JetAnalysisSequence.py
@@ -0,0 +1,399 @@
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+
+from __future__ import print_function
+
+# AnaAlgorithm import(s):
+from AnaAlgorithm.AnaAlgSequence import AnaAlgSequence
+from AnaAlgorithm.DualUseConfig import createAlgorithm, addPrivateTool
+import re
+
+# These algorithms set up the jet recommendations as-of 04/02/2019.
+# Jet calibration recommendations
+# https://twiki.cern.ch/twiki/bin/viewauth/AtlasProtected/ApplyJetCalibrationR21
+# Jet uncertainties recommendations
+# Small-R
+# https://twiki.cern.ch/twiki/bin/view/AtlasProtected/JetUncertaintiesRel21Summer2018SmallR
+# Large-R
+# https://twiki.cern.ch/twiki/bin/viewauth/AtlasProtected/JetUncertaintiesRel21Moriond2018LargeR
+# JVT recommendations
+# https://twiki.cern.ch/twiki/bin/view/AtlasProtected/JVTCalibrationRel21
+
+# Keep the different possible sets of systematics here.
+# All possible large-R jet systematics
+largeRSysts = "|".join([
+    "(^JET_Rtrk_.*)",
+    "(^JET_TAM_.*)",
+    "(^JET_MassRes_.*)",
+    "(^JET_Comb_.*_mass.*)"])
+smallRSysts = "|".join([
+    "(^JET_BJES_Response$)",
+    "(^JET_EtaIntercalibration_.*)",
+    "(^JET_Flavor_.*)",
+    "(^JET_Gjet_.*)",
+    "(^JET_JER_.*)",
+    "(^JET_MJB_.*)",
+    "(^JET_Pileup_.*)",
+    "(^JET_PunchThrough_.*)",
+    "(^JET_RelativeNonClosure_.*)",
+    "(^JET_SingleParticle_HighPt$)",
+    "(^JET_Zjet_.*)",
+    "(^JET_EffectiveNP_.*)",
+    "(^JET_GroupedNP_.*)"])
+jvtSysts = "|".join([
+    "(^JET_JvtEfficiency$)"])
+fjvtSysts = "|".join([
+    "(^JET_fJvtEfficiency$)"])
+
+def makeJetAnalysisSequence( dataType, jetCollection, postfix = '',
+                             deepCopyOutput = False,
+                             shallowViewOutput = True,
+                             runGhostMuonAssociation = True,
+                             enableCutflow = False,
+                             enableKinematicHistograms = False,
+                             **kwargs):
+    """Create a jet analysis algorithm sequence
+      The jet collection is interpreted and selects the correct function to call, 
+      makeSmallRJetAnalysisSequence, makeRScanJetAnalysisSequence or 
+      makeLargeRJetAnalysisSequence
+
+      Keyword arguments
+        dataType -- The data type to run on ("data", "mc" or "afii")
+        jetCollection -- The jet container to run on.
+        postfix -- String to be added to the end of all public names.
+        deepCopyOutput -- Whether or not to deep copy the output
+        shallowViewOutput -- Whether or not to output a shallow view as the output
+        enableCutflow -- Whether or not to dump the cutflow
+        enableKinematicHistograms -- Whether or not to dump the kinematic histograms
+        Other keyword arguments are forwarded to the other functions.
+    """
+    if dataType not in ["data", "mc", "afii"]:
+        raise ValueError ("invalid data type: " + dataType )
+
+    # Setup the postfix
+    if postfix != '':
+        postfix = "_" + postfix
+
+    # Make sure selection options make sense
+    if deepCopyOutput and shallowViewOutput:
+        raise ValueError ("deepCopyOutput and shallowViewOutput can't both be true!")
+
+    # Remove b-tagging calibration from the container name
+    btIndex = jetCollection.find('_BTagging')
+    if btIndex != -1:
+        jetCollection = jetCollection[:btIndex]
+
+    # interpret the jet collection
+    collection_pattern = re.compile(
+        r"AntiKt(\d+)(EMTopo|EMPFlow|LCTopo|TrackCaloCluster)(TrimmedPtFrac5SmallR20)?Jets")
+    match = collection_pattern.match(jetCollection)
+    if not match:
+        raise ValueError(
+            "Jet collection {0} does not match expected pattern!".format(jetCollection) )
+    radius = int(match.group(1) )
+    if radius not in [2, 4, 6, 10]:
+        raise ValueError("Jet collection has an unsupported radius '{0}'!".format(radius) )
+    jetInput = match.group(2)
+
+    # Create the analysis algorithm sequence object.
+    seq = AnaAlgSequence( "JetAnalysisSequence"+postfix )
+    # Relink original jets in case of b-tagging calibration
+    if btIndex != -1:
+        alg = createAlgorithm( 'CP::AsgOriginalObjectLinkAlg',
+            'JetOriginalObjectLinkAlg'+postfix )
+        alg.baseContainerName = jetCollection
+        seq.append( alg, inputPropName = 'particles', outputPropName = 'particlesOut', stageName = 'calibration' )
+
+    # Set up the jet ghost muon association algorithm:
+    if runGhostMuonAssociation:
+        alg = createAlgorithm( 'CP::JetGhostMuonAssociationAlg', 
+            'JetGhostMuonAssociationAlg'+postfix )
+        seq.append( alg, inputPropName = 'jets', outputPropName = 'jetsOut', stageName = 'calibration' )
+
+    # record all the selections each subfunction makes
+    cutlist = []
+    cutlength = []
+
+    if radius == 4:
+        makeSmallRJetAnalysisSequence(seq, cutlist, cutlength,
+            dataType, jetCollection, jetInput=jetInput, postfix=postfix, **kwargs)
+    elif radius in [2, 6]:
+        makeRScanJetAnalysisSequence(seq, cutlist, cutlength,
+            dataType, jetCollection, jetInput=jetInput, radius=radius, 
+            postfix=postfix, **kwargs)
+    else:
+        trim = match.group(3)
+        if trim == "":
+            raise ValueError("Untrimmed large-R jets are not supported!")
+        makeLargeRJetAnalysisSequence(seq, cutlist, cutlength,
+            dataType, jetCollection, jetInput=jetInput, postfix=postfix, **kwargs)
+
+    # Set up an algorithm used to create jet selection cutflow:
+    if enableCutflow:
+        alg = createAlgorithm( 'CP::ObjectCutFlowHistAlg', 'JetCutFlowDumperAlg'+postfix )
+        alg.histPattern = 'jet_cflow_%SYS%'+postfix
+        alg.selection = cutlist
+        alg.selectionNCuts = cutlength
+        seq.append( alg, inputPropName = 'input', stageName = 'selection' )
+
+    # Set up an algorithm dumping the kinematic properties of the jets:
+    if enableKinematicHistograms:
+        alg = createAlgorithm( 'CP::KinematicHistAlg', 'JetKinematicDumperAlg'+postfix )
+        alg.preselection = "&&".join (cutlist)
+        alg.histPattern = 'jet_%VAR%_%SYS%'+postfix
+        seq.append( alg, inputPropName = 'input', stageName = 'selection' )
+
+    if shallowViewOutput:
+      # Set up an algorithm that makes a view container using the selections
+      # performed previously:
+      alg = createAlgorithm( 'CP::AsgViewFromSelectionAlg', 'JetViewFromSelectionAlg'+postfix )
+      alg.selection = cutlist
+      seq.append( alg, inputPropName = 'input', outputPropName = 'output',
+                  stageName = 'selection' )
+
+    # Set up a final deep copy making algorithm if requested:
+    if deepCopyOutput:
+        alg = createAlgorithm( 'CP::AsgViewFromSelectionAlg', 'JetDeepCopyMaker'+postfix )
+        alg.deepCopy = True
+        seq.append( alg, inputPropName = 'input', outputPropName = 'output',
+                    stageName = 'selection' )
+    
+    return seq
+
+def makeSmallRJetAnalysisSequence( seq, cutlist, cutlength, dataType, jetCollection,
+                                   jetInput, postfix = '', 
+                                   runJvtUpdate = True, runFJvtUpdate = True,
+                                   runJvtSelection = True, runFJvtSelection = True,
+                                   runJvtEfficiency = True, runFJvtEfficiency = True,
+                                   reduction = "Global", JEROption = "Simple"):
+    """Add algorithms for the R=0.4 jets.
+
+      Keyword arguments
+        seq -- The sequence to add the algorithms to
+        cutlist -- Insert any cuts into this
+        cutlength -- Insert the lengths of any cuts into this
+        dataType -- The data type to run on ("data", "mc" or "afii")
+        jetCollection -- The jet container to run on.
+        jetInput -- The type of input used, read from the collection name.
+        postfix -- String to be added to the end of all public names.
+        runJvtUpdate -- Determines whether or not to update JVT on the jets
+        runFJvtUpdate -- Determines whether or not to update forward JVT on the jets
+        runJvtSelection -- Determines whether or not to run JVT selection on the jets
+        runFJvtSelection -- Determines whether or not to run forward JVT selection on the jets
+        runJvtEfficiency -- Determines whether or not to calculate the JVT efficiency
+        runFJvtEfficiency -- Determines whether or not to calculate the forward JVT efficiency
+        reduction -- Which NP reduction scheme should be used (All, Global, Category, Scenario)
+        JEROption -- Which variant of the reduction should be used (All, Full, Simple). Note that not all combinations of reduction and JEROption are valid!
+    """
+    if jetInput not in ["EMTopo", "EMPFlow"]:
+        raise ValueError(
+            "Unsupported input type '{0}' for R=0.4 jets!".format(jetInput) )
+
+    # Prepare the jet calibration algorithm
+    alg = createAlgorithm( 'CP::JetCalibrationAlg', 'JetCalibrationAlg'+postfix )
+    addPrivateTool( alg, 'calibrationTool', 'JetCalibrationTool' )
+    alg.calibrationTool.JetCollection = jetCollection[:-4]
+    # Get the correct string to use in the config file name
+    if dataType == 'afii':
+        configFile = "JES_MC16Recommendation_AFII_{0}_Apr2019_Rel21.config"
+    else:
+        configFile = "JES_MC16Recommendation_Consolidated_{0}_Apr2019_Rel21.config"
+    if jetInput == "EMPFlow":
+        configFile = configFile.format("PFlow")
+    else:
+        configFile = configFile.format(jetInput)
+    alg.calibrationTool.ConfigFile = configFile
+    if dataType == 'data':
+        alg.calibrationTool.CalibSequence = 'JetArea_Residual_EtaJES_GSC_Insitu'
+    else:
+        alg.calibrationTool.CalibSequence = 'JetArea_Residual_EtaJES_GSC_Smear'
+    alg.calibrationTool.IsData = (dataType == 'data')
+    seq.append( alg, inputPropName = 'jets', outputPropName = 'jetsOut', stageName = 'calibration')
+
+    # Jet uncertainties
+    # Prepare the config file
+    if reduction == "All" and JEROption == "All":
+        alg.uncertaintiesTool.ConfigFile = "R4_AllNuisanceParameters_AllJERNP.config"
+    elif "Scenario" in reduction:
+        if JEROption != "Simple":
+            raise ValueError(
+                "Invalid uncertainty configuration - Scenario* reductions can "
+                "only be used together with the Simple JEROption")
+        configFile = "R4_{0}_SimpleJER.config".format(reduction)
+    elif reduction in ["Global", "Category"] and JEROption in ["Simple", "Full"]:
+        configFile = "R4_{0}Reduction_{1}JER.config".format(reduction, JEROption)
+    else:
+        raise ValueError(
+            "Invalid combination of reduction and JEROption settings: "
+            "reduction: {0}, JEROption: {1}".format(reduction, JEROption) )
+
+    alg = createAlgorithm( 'CP::JetUncertaintiesAlg', 'JetUncertaintiesTool'+postfix )
+    addPrivateTool( alg, 'uncertaintiesTool', 'JetUncertaintiesTool' )
+    alg.uncertaintiesTool.JetDefinition = jetCollection[:-4]
+    # Add the correct directory on the front
+    alg.uncertaintiesTool.ConfigFile = "rel21/Fall2018/"+configFile
+    alg.uncertaintiesTool.MCType = "AFII" if dataType == "afii" else "MC16"
+    alg.uncertaintiesTool.IsData = (dataType == 'data')
+    seq.append( alg, inputPropName = 'jets', outputPropName = 'jetsOut',
+                affectingSystematics = smallRSysts, stageName = 'calibration' )
+
+    # Set up the JVT update algorithm:
+    if runJvtUpdate :
+        alg = createAlgorithm( 'CP::JvtUpdateAlg', 'JvtUpdateAlg'+postfix )
+        addPrivateTool( alg, 'jvtTool', 'JetVertexTaggerTool' )
+        seq.append( alg, inputPropName = 'jets', outputPropName = 'jetsOut', stageName = 'selection' )
+
+    if runFJvtUpdate :
+        alg = createAlgorithm( 'CP::JetModifierAlg', 'JetModifierAlg'+postfix )
+        addPrivateTool( alg, 'modifierTool', 'JetForwardJvtTool')
+        alg.modifierTool.OutputDec = "passFJVT" #Output decoration
+        # fJVT WPs depend on the MET WP
+        # see https://twiki.cern.ch/twiki/bin/view/AtlasProtected/EtmissRecommendationsRel21p2#fJVT_and_MET
+        alg.modifierTool.UseTightOP = 1 # 1 = Tight, 0 = Loose
+        alg.modifierTool.EtaThresh = 2.5 # Eta dividing central from forward jets
+        alg.modifierTool.ForwardMaxPt = 120.0e3 #Max Pt to define fwdJets for JVT
+        seq.append( alg, inputPropName = 'jets', outputPropName = 'jetsOut', stageName = 'selection' )
+        pass
+
+    # Set up the jet efficiency scale factor calculation algorithm
+    # Change the truthJetCollection property to AntiKt4TruthWZJets if preferred
+    if runJvtSelection :
+        alg = createAlgorithm( 'CP::JvtEfficiencyAlg', 'JvtEfficiencyAlg'+postfix )
+        addPrivateTool( alg, 'efficiencyTool', 'CP::JetJvtEfficiency' )
+        if jetInput == 'EMPFlow':
+            alg.efficiencyTool.SFFile = 'JetJvtEfficiency/Moriond2018/JvtSFFile_EMPFlow.root'
+            alg.efficiencyTool.MaxPtForJvt = 60e3
+        else:
+            alg.efficiencyTool.SFFile = 'JetJvtEfficiency/Moriond2018/JvtSFFile_EMTopoJets.root'
+            alg.efficiencyTool.MaxPtForJvt = 120e3
+        alg.efficiencyTool.WorkingPoint = 'Tight' if jetInput == 'EMPFlow' else 'Medium'
+        alg.selection = 'jvt_selection'
+        alg.scaleFactorDecoration = 'jvt_effSF_%SYS%'
+        alg.scaleFactorDecorationRegex = jvtSysts
+        # Disable scale factor decorations if running on data
+        # We still want to run the JVT selection
+        if not runJvtEfficiency or dataType == 'data':
+            alg.scaleFactorDecoration = ''
+            alg.truthJetCollection = ''
+        alg.outOfValidity = 2
+        alg.outOfValidityDeco = 'no_jvt'
+        alg.skipBadEfficiency = 0
+        seq.append( alg, inputPropName = 'jets',
+                    affectingSystematics = jvtSysts, stageName = 'selection' )
+
+    if runFJvtSelection :
+        alg = createAlgorithm( 'CP::JvtEfficiencyAlg', 'ForwardJvtEfficiencyAlg' )
+        addPrivateTool( alg, 'efficiencyTool', 'CP::JetJvtEfficiency' )
+        alg.efficiencyTool.SFFile = 'JetJvtEfficiency/Moriond2018/fJvtSFFile.root'
+        alg.efficiencyTool.WorkingPoint = 'Tight'
+        alg.dofJVT = True
+        alg.fJVTStatus = 'passFJVT,as_char'
+        alg.selection = 'fjvt_selection'
+        alg.scaleFactorDecoration = 'fjvt_effSF_%SYS%'
+        alg.scaleFactorDecorationRegex = fjvtSysts
+        # Disable scale factor decorations if running on data
+        # We still want to run the JVT selection
+        if not runFJvtEfficiency or dataType == 'data':
+            alg.scaleFactorDecoration = ''
+            alg.truthJetCollection = ''
+        alg.outOfValidity = 2
+        alg.outOfValidityDeco = 'no_fjvt'
+        alg.skipBadEfficiency = 0
+        seq.append( alg, inputPropName = 'jets',
+                    affectingSystematics = fjvtSysts, stageName = 'selection')
+
+    # Return the sequence:
+    return seq, cutlist, cutlength
+
+def makeRScanJetAnalysisSequence( seq, cutlist, cutlength, dataType, jetCollection,
+                                  jetInput, radius, postfix = '' ):
+    """Add algorithms for the R-scan jets.
+
+      Keyword arguments
+        seq -- The sequence to add the algorithms to
+        cutlist -- Insert any cuts into this
+        cutlength -- Insert the lengths of any cuts into this
+        dataType -- The data type to run on ("data", "mc" or "afii")
+        jetCollection -- The jet container to run on.
+        jetInput -- The type of input used, read from the collection name.
+        radius -- The radius of the r-scan jets.
+        postfix -- String to be added to the end of all public names.
+    """
+    if jetInput != "LCTopo":
+        raise ValueError(
+            "Unsupported input type '{0}' for R-scan jets!".format(jetInput) )
+    # Prepare the jet calibration algorithm
+    alg = createAlgorithm( 'CP::JetCalibrationAlg', 'JetCalibrationAlg'+postfix )
+    addPrivateTool( alg, 'calibrationTool', 'JetCalibrationTool' )
+    alg.calibrationTool.JetCollection = jetCollection[:-4]
+    alg.calibrationTool.ConfigFile = \
+        "JES_MC16Recommendation_Rscan{0}LC_18Dec2018_R21.config".format(radius)
+    if dataType == 'data':
+        alg.calibrationTool.CalibSequence = "JetArea_Residual_EtaJES_GSC_Insitu"
+    else:
+        alg.calibrationTool.CalibSequence = "JetArea_Residual_EtaJES_GSC"
+    alg.calibrationTool.IsData = (dataType == 'data')
+    seq.append( alg, inputPropName = 'jets', outputPropName = 'jetsOut', stageName = 'calibration' )
+    # Logging would be good
+    print("WARNING: uncertainties for R-Scan jets are not yet released!")
+
+def makeLargeRJetAnalysisSequence( seq, cutlist, cutlength, dataType, jetCollection,
+                                   jetInput, postfix = '', largeRMass = "Comb"):
+    """Add algorithms for the R=1.0 jets.
+
+      Keyword arguments
+        seq -- The sequence to add the algorithms to
+        cutlist -- Insert any cuts into this
+        cutlength -- Insert the lengths of any cuts into this
+        dataType -- The data type to run on ("data", "mc" or "afii")
+        jetCollection -- The jet container to run on.
+        jetInput -- The type of input used, read from the collection name.
+        postfix -- String to be added to the end of all public names.
+        largeRMass -- Which large-R mass definition to use. Ignored if not running on large-R jets ("Comb", "Calo", "TCC", "TA")
+    """
+
+    if largeRMass not in ["Comb", "Calo", "TCC", "TA"]:
+        raise ValueError ("Invalid large-R mass defintion {0}!".format(largeRMass) )
+
+    if jetInput not in ["LCTopo", "TrackCaloCluster"]:
+        raise ValueError (
+            "Unsupported input type '{0}' for large-R jets!".format(jetInput) )
+    if jetInput == "TrackCaloCluster":
+        # Only one mass defintion supported
+        if largeRMass != "Calo":
+            raise ValueError(
+                "Unsupported large-R TCC jet mass '{0}'!".format(largeRMass) )
+        configFile = "JES_MC16recommendation_FatJet_TCC_JMS_calo_30Oct2018.config"
+    else:
+        if largeRMass == "Comb":
+            configFile = "JES_MC16recommendation_FatJet_Trimmed_JMS_comb_17Oct2018.config"
+        elif largeRMass == "Calo":
+            configFile = "JES_MC16recommendation_FatJet_Trimmed_JMS_calo_12Oct2018.config"
+        elif largeRMass == "TCC":
+            configFile = "JES_MC16recommendation_FatJet_TCC_JMS_calo_30Oct2018.config"
+        else:
+            configFile = "JES_MC16recommendation_FatJet_Trimmed_JMS_TA_12Oct2018.config"
+    # Prepare the jet calibration algorithm
+    alg = createAlgorithm( 'CP::JetCalibrationAlg', 'JetCalibrationAlg'+postfix )
+    addPrivateTool( alg, 'calibrationTool', 'JetCalibrationTool' )
+    alg.calibrationTool.JetCollection = jetCollection[:-4]
+    alg.calibrationTool.ConfigFile = configFile
+    alg.calibrationTool.CalibSequence = "EtaJES_JMS"
+    alg.calibrationTool.IsData = 0
+    seq.append( alg, inputPropName = 'jets', outputPropName = 'jetsOut', stageName = 'calibration' )
+
+    # Jet uncertainties
+    alg = createAlgorithm( 'CP::JetUncertaintiesAlg', 'JetUncertaintiesAlg'+postfix )
+    # R=1.0 jets have a validity range 
+    alg.outOfValidity = 2 # SILENT
+    alg.outOfValidityDeco = 'outOfValidity'
+    addPrivateTool( alg, 'uncertaintiesTool', 'JetUncertaintiesTool' )
+    alg.uncertaintiesTool.JetDefinition = jetCollection[:-4]
+    alg.uncertaintiesTool.ConfigFile = \
+        "rel21/Moriond2018/R10_{0}Mass_all.config".format(largeRMass)
+    alg.uncertaintiesTool.MCType = "MC16a"
+    alg.uncertaintiesTool.IsData = (dataType == "data")
+    seq.append( alg, inputPropName = 'jets', outputPropName = 'jetsOut',
+                affectingSystematics = largeRSysts, stageName = 'calibration' )
+
+    cutlist.append('outOfValidity')
+    cutlength.append(1)
diff --git a/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/python/JetJvtAnalysisSequence.py b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/python/JetJvtAnalysisSequence.py
new file mode 100644
index 0000000000000000000000000000000000000000..8ebee22a2ab30f3bd48b25d641f3792a9ca7dcc7
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/python/JetJvtAnalysisSequence.py
@@ -0,0 +1,89 @@
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+
+# AnaAlgorithm import(s):
+from AnaAlgorithm.AnaAlgSequence import AnaAlgSequence
+from AnaAlgorithm.DualUseConfig import createAlgorithm
+
+def makeJetJvtAnalysisSequence( dataType, jetCollection,
+                                preselection = '',
+                                disableFJvt = False,
+                                globalSF = True,
+                                runSelection = True,
+                                enableCutflow = False ):
+    """Create a jet JVT analysis algorithm sequence
+
+    Keyword arguments:
+      dataType -- The data type to run on ("data", "mc" or "afii")
+      jetCollection -- The jet container to run on
+      disableFJvt -- Whether to disable forward JVT calculations
+      globalSF -- Whether to calculate per event scale factors
+      runSelection -- Whether to run selection
+      enableCutflow -- Whether or not to dump the cutflow
+    """
+
+    if dataType not in ["data", "mc", "afii"] :
+        raise ValueError ("invalid data type: " + dataType)
+
+    if runSelection and not globalSF :
+        raise ValueError ("per-event scale factors needs to be computed when doing a JVT selection")
+
+    # Create the analysis algorithm sequence object:
+    seq = AnaAlgSequence( "JetJVTAnalysisSequence" )
+
+    # Define a list of cuts to apply later on and the
+    # number of bits in the corresponding TAccept
+    cutlist = []
+    cutlength = []
+
+    # Set up the per-event jet efficiency scale factor calculation algorithm
+    if dataType != 'data' and globalSF:
+        from JetAnalysisSequence import jvtSysts, fjvtSysts
+
+        alg = createAlgorithm( 'CP::AsgEventScaleFactorAlg', 'JvtEventScaleFactorAlg' )
+        alg.preselection = preselection + '&&no_jvt' if preselection else 'no_jvt'
+        alg.scaleFactorInputDecoration = 'jvt_effSF_%SYS%'
+        alg.scaleFactorInputDecorationRegex = jvtSysts
+        alg.scaleFactorOutputDecoration = 'jvt_effSF_%SYS%'
+
+        seq.append( alg,
+                    affectingSystematics = jvtSysts,
+                    inputPropName = { 'jets' : 'particles',
+                                      'eventInfo' : 'eventInfo' } )
+
+        if not disableFJvt:
+            alg = createAlgorithm( 'CP::AsgEventScaleFactorAlg', 'ForwardJvtEventScaleFactorAlg' )
+            alg.preselection = preselection + '&&no_fjvt' if preselection else 'no_fjvt'
+            alg.scaleFactorInputDecoration = 'fjvt_effSF_%SYS%'
+            alg.scaleFactorInputDecorationRegex = fjvtSysts
+            alg.scaleFactorOutputDecoration = 'fjvt_effSF_%SYS%'
+
+            seq.append( alg,
+                        affectingSystematics = fjvtSysts,
+                        inputPropName = { 'jets' : 'particles',
+                                          'eventInfo' : 'eventInfo' } )
+
+    if runSelection:
+        cutlist.append('jvt_selection')
+        cutlength.append(1)
+
+        if not disableFJvt:
+            cutlist.append('fjvt_selection')
+            cutlength.append(1)
+
+        # Set up an algorithm used to create jet JVT selection cutflow:
+        if enableCutflow:
+            alg = createAlgorithm( 'CP::ObjectCutFlowHistAlg', 'JetJvtCutFlowDumperAlg' )
+            alg.histPattern = 'jet_cflow_jvt_%SYS%'
+            alg.selection = cutlist
+            alg.selectionNCuts = cutlength
+            seq.append( alg, inputPropName = { 'jets' : 'input' })
+
+        # Set up an algorithm that makes a view container using the selections
+        # performed previously:
+        alg = createAlgorithm( 'CP::AsgViewFromSelectionAlg', 'JetJvtViewFromSelectionAlg' )
+        alg.selection = cutlist
+        seq.append( alg, inputPropName = { 'jets' : 'input' },
+                    outputPropName = { 'jets' : 'output' } )
+
+    # Return the sequence:
+    return seq
diff --git a/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/python/__init__.py b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/python/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..5ccb446532599c01cc3844e5b1b18db06f721002
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/python/__init__.py
@@ -0,0 +1,3 @@
+# Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+
+__version__ = '1.0.0'
diff --git a/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/share/JetAnalysisAlgorithmsTest_EMTopo_eljob.py b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/share/JetAnalysisAlgorithmsTest_EMTopo_eljob.py
new file mode 100755
index 0000000000000000000000000000000000000000..c114d0ad7608511074292f704493e289441e7d74
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/share/JetAnalysisAlgorithmsTest_EMTopo_eljob.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+#
+# @author Nils Krumnack
+
+
+# Read the submission directory as a command line argument. You can
+# extend the list of arguments with your private ones later on.
+import optparse
+parser = optparse.OptionParser()
+parser.add_option( '-d', '--data-type', dest = 'data_type',
+                   action = 'store', type = 'string', default = 'data',
+                   help = 'Type of data to run over. Valid options are data, mc, afii' )
+parser.add_option( '-s', '--submission-dir', dest = 'submission_dir',
+                   action = 'store', type = 'string', default = 'submitDir',
+                   help = 'Submission directory for EventLoop' )
+parser.add_option( '-u', '--unit-test', dest='unit_test',
+                   action = 'store_true', default = False,
+                   help = 'Run the job in "unit test mode"' )
+( options, args ) = parser.parse_args()
+
+# Set up (Py)ROOT.
+import ROOT
+ROOT.xAOD.Init().ignore()
+
+# this forces the jet algorithms dictionary to be loaded before
+# anything else, which works around some strange dictionary issues I
+# don't understand.
+ROOT.CP.JetCalibrationAlg ("dummy", None)
+
+# ideally we'd run over all of them, but we don't have a mechanism to
+# configure per-sample right now
+
+dataType = options.data_type
+
+inputfile = {"data": 'ASG_TEST_FILE_DATA',
+             "mc":   'ASG_TEST_FILE_MC',
+             "afii": 'ASG_TEST_FILE_MC_AFII'}
+
+jetContainer = "AntiKt4EMTopoJets"
+
+if dataType not in ["data", "mc", "afii"] :
+    raise ValueError ("invalid data type: " + dataType)
+
+# Set up the sample handler object. See comments from the C++ macro
+# for the details about these lines.
+import os
+sh = ROOT.SH.SampleHandler()
+sh.setMetaString( 'nc_tree', 'CollectionTree' )
+sample = ROOT.SH.SampleLocal (dataType)
+sample.add (os.getenv (inputfile[dataType]))
+sh.add (sample)
+sh.printContent()
+
+# Create an EventLoop job.
+job = ROOT.EL.Job()
+job.sampleHandler( sh )
+job.options().setDouble( ROOT.EL.Job.optMaxEvents, 500 )
+
+from JetAnalysisAlgorithms.JetAnalysisAlgorithmsTest import makeSequence
+algSeq = makeSequence (dataType, jetContainer)
+print algSeq # For debugging
+for alg in algSeq:
+    job.algsAdd( alg )
+    pass
+
+# Set up an output file for the job:
+job.outputAdd( ROOT.EL.OutputStream( 'ANALYSIS' ) )
+
+# Find the right output directory:
+submitDir = options.submission_dir
+if options.unit_test:
+    import os
+    import tempfile
+    submitDir = tempfile.mkdtemp( prefix = 'jetTest_'+dataType+'_EMTopo_', dir = os.getcwd() )
+    os.rmdir( submitDir )
+    pass
+
+# Run the job using the direct driver.
+driver = ROOT.EL.DirectDriver()
+driver.submit( job, submitDir )
diff --git a/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/share/JetAnalysisAlgorithmsTest_EMTopo_jobOptions.py b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/share/JetAnalysisAlgorithmsTest_EMTopo_jobOptions.py
new file mode 100644
index 0000000000000000000000000000000000000000..7b5afce3ae68197992610bcc596a53cbedfb92b9
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/share/JetAnalysisAlgorithmsTest_EMTopo_jobOptions.py
@@ -0,0 +1,46 @@
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+#
+# @author Nils Krumnack
+
+# User options, which can be set from command line after a "-" character
+# athena EgammaAlgorithmsTest_jobOptions.py - --myOption ...
+from AthenaCommon.AthArgumentParser import AthArgumentParser
+athArgsParser = AthArgumentParser()
+athArgsParser.add_argument("--data-type", action = "store", dest = "data_type",
+                           default = "data",
+                           help = "Type of input to run over. Valid options are 'data', 'mc', 'afii'")
+athArgs = athArgsParser.parse_args()
+
+dataType = athArgs.data_type
+if not dataType in ["data", "mc", "afii"] :
+    raise Exception ("invalid data type: " + dataType)
+
+print("Running on data type: " + dataType)
+
+inputfile = {"data": 'ASG_TEST_FILE_DATA',
+             "mc":   'ASG_TEST_FILE_MC',
+             "afii": 'ASG_TEST_FILE_MC_AFII'}
+
+jetContainer = "AntiKt4EMTopoJets"
+
+# Set up the reading of the input file:
+import AthenaRootComps.ReadAthenaxAODHybrid
+theApp.EvtMax = 500
+testFile = os.getenv ( inputfile[dataType] )
+svcMgr.EventSelector.InputCollections = [testFile]
+
+from JetAnalysisAlgorithms.JetAnalysisAlgorithmsTest import makeSequence
+algSeq = makeSequence (dataType, jetContainer)
+print algSeq # For debugging
+
+# Add all algorithms from the sequence to the job.
+athAlgSeq += algSeq
+
+# Set up a histogram output file for the job:
+ServiceMgr += CfgMgr.THistSvc()
+ServiceMgr.THistSvc.Output += [
+    "ANALYSIS DATAFILE='JetAnalysisAlgorithmsTestEMTopo." + dataType + ".hist.root' OPT='RECREATE'"
+    ]
+
+# Reduce the printout from Athena:
+include( "AthAnalysisBaseComps/SuppressLogging.py" )
diff --git a/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/share/JetAnalysisAlgorithmsTest_PFlow_eljob.py b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/share/JetAnalysisAlgorithmsTest_PFlow_eljob.py
new file mode 100755
index 0000000000000000000000000000000000000000..97f94a5bcd89ff802e544dc8954dec1d5a43e434
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/share/JetAnalysisAlgorithmsTest_PFlow_eljob.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+#
+# @author Nils Krumnack
+
+
+# Read the submission directory as a command line argument. You can
+# extend the list of arguments with your private ones later on.
+import optparse
+parser = optparse.OptionParser()
+parser.add_option( '-d', '--data-type', dest = 'data_type',
+                   action = 'store', type = 'string', default = 'data',
+                   help = 'Type of data to run over. Valid options are data, mc, afii' )
+parser.add_option( '-s', '--submission-dir', dest = 'submission_dir',
+                   action = 'store', type = 'string', default = 'submitDir',
+                   help = 'Submission directory for EventLoop' )
+parser.add_option( '-u', '--unit-test', dest='unit_test',
+                   action = 'store_true', default = False,
+                   help = 'Run the job in "unit test mode"' )
+( options, args ) = parser.parse_args()
+
+# Set up (Py)ROOT.
+import ROOT
+ROOT.xAOD.Init().ignore()
+
+# this forces the jet algorithms dictionary to be loaded before
+# anything else, which works around some strange dictionary issues I
+# don't understand.
+ROOT.CP.JetCalibrationAlg ("dummy", None)
+
+# ideally we'd run over all of them, but we don't have a mechanism to
+# configure per-sample right now
+
+dataType = options.data_type
+
+inputfile = {"data": 'ASG_TEST_FILE_DATA',
+             "mc":   'ASG_TEST_FILE_MC',
+             "afii": 'ASG_TEST_FILE_MC_AFII'}
+
+jetContainer = "AntiKt4EMTopoJets"
+
+if dataType not in ["data", "mc", "afii"] :
+    raise ValueError ("invalid data type: " + dataType)
+
+# Set up the sample handler object. See comments from the C++ macro
+# for the details about these lines.
+import os
+sh = ROOT.SH.SampleHandler()
+sh.setMetaString( 'nc_tree', 'CollectionTree' )
+sample = ROOT.SH.SampleLocal (dataType)
+sample.add (os.getenv (inputfile[dataType]))
+sh.add (sample)
+sh.printContent()
+
+# Create an EventLoop job.
+job = ROOT.EL.Job()
+job.sampleHandler( sh )
+job.options().setDouble( ROOT.EL.Job.optMaxEvents, 500 )
+
+from JetAnalysisAlgorithms.JetAnalysisAlgorithmsTest import makeSequence
+algSeq = makeSequence (dataType, jetContainer)
+print algSeq # For debugging
+for alg in algSeq:
+    job.algsAdd( alg )
+    pass
+
+# Set up an output file for the job:
+job.outputAdd( ROOT.EL.OutputStream( 'ANALYSIS' ) )
+
+# Find the right output directory:
+submitDir = options.submission_dir
+if options.unit_test:
+    import os
+    import tempfile
+    submitDir = tempfile.mkdtemp( prefix = 'jetTest_'+dataType+'_PFlow_', dir = os.getcwd() )
+    os.rmdir( submitDir )
+    pass
+
+# Run the job using the direct driver.
+driver = ROOT.EL.DirectDriver()
+driver.submit( job, submitDir )
diff --git a/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/share/JetAnalysisAlgorithmsTest_PFlow_jobOptions.py b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/share/JetAnalysisAlgorithmsTest_PFlow_jobOptions.py
new file mode 100644
index 0000000000000000000000000000000000000000..69d7df65f6ff762245f20ef7c3903562eca105ca
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/share/JetAnalysisAlgorithmsTest_PFlow_jobOptions.py
@@ -0,0 +1,46 @@
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+#
+# @author Nils Krumnack
+
+# User options, which can be set from command line after a "-" character
+# athena EgammaAlgorithmsTest_jobOptions.py - --myOption ...
+from AthenaCommon.AthArgumentParser import AthArgumentParser
+athArgsParser = AthArgumentParser()
+athArgsParser.add_argument("--data-type", action = "store", dest = "data_type",
+                           default = "data",
+                           help = "Type of input to run over. Valid options are 'data', 'mc', 'afii'")
+athArgs = athArgsParser.parse_args()
+
+dataType = athArgs.data_type
+if not dataType in ["data", "mc", "afii"] :
+    raise Exception ("invalid data type: " + dataType)
+
+print("Running on data type: " + dataType)
+
+inputfile = {"data": 'ASG_TEST_FILE_DATA',
+             "mc":   'ASG_TEST_FILE_MC',
+             "afii": 'ASG_TEST_FILE_MC_AFII'}
+
+jetContainer = "AntiKt4EMTopoJets"
+
+# Set up the reading of the input file:
+import AthenaRootComps.ReadAthenaxAODHybrid
+theApp.EvtMax = 500
+testFile = os.getenv ( inputfile[dataType] )
+svcMgr.EventSelector.InputCollections = [testFile]
+
+from JetAnalysisAlgorithms.JetAnalysisAlgorithmsTest import makeSequence
+algSeq = makeSequence (dataType, jetContainer)
+print algSeq # For debugging
+
+# Add all algorithms from the sequence to the job.
+athAlgSeq += algSeq
+
+# Set up a histogram output file for the job:
+ServiceMgr += CfgMgr.THistSvc()
+ServiceMgr.THistSvc.Output += [
+    "ANALYSIS DATAFILE='JetAnalysisAlgorithmsTestPFlow." + dataType + ".hist.root' OPT='RECREATE'"
+    ]
+
+# Reduce the printout from Athena:
+include( "AthAnalysisBaseComps/SuppressLogging.py" )
diff --git a/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/src/components/JetAnalysisAlgorithms_entries.cxx b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/src/components/JetAnalysisAlgorithms_entries.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..757495457d3f4a77d15d045efb528c2840434c29
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/src/components/JetAnalysisAlgorithms_entries.cxx
@@ -0,0 +1,32 @@
+// AsgExampleTools_entries.cxx
+
+#include <GaudiKernel/DeclareFactoryEntries.h>
+
+#include <JetAnalysisAlgorithms/JetCalibrationAlg.h>
+#include <JetAnalysisAlgorithms/JetGhostMuonAssociationAlg.h>
+#include <JetAnalysisAlgorithms/JetModifierAlg.h>
+#include <JetAnalysisAlgorithms/JetSelectionAlg.h>
+#include <JetAnalysisAlgorithms/JetSmearingAlg.h>
+#include <JetAnalysisAlgorithms/JetUncertaintiesAlg.h>
+#include <JetAnalysisAlgorithms/JvtEfficiencyAlg.h>
+#include <JetAnalysisAlgorithms/JvtUpdateAlg.h>
+
+DECLARE_ALGORITHM_FACTORY (CP::JetCalibrationAlg)
+DECLARE_ALGORITHM_FACTORY (CP::JetGhostMuonAssociationAlg)
+DECLARE_ALGORITHM_FACTORY (CP::JetModifierAlg)
+DECLARE_ALGORITHM_FACTORY (CP::JetSelectionAlg)
+DECLARE_ALGORITHM_FACTORY (CP::JetSmearingAlg)
+DECLARE_ALGORITHM_FACTORY (CP::JetUncertaintiesAlg)
+DECLARE_ALGORITHM_FACTORY (CP::JvtEfficiencyAlg)
+DECLARE_ALGORITHM_FACTORY (CP::JvtUpdateAlg)
+
+DECLARE_FACTORY_ENTRIES(JetAnalysisAlgorithms) {
+  DECLARE_ALGORITHM (CP::JetCalibrationAlg)
+  DECLARE_ALGORITHM (CP::JetGhostMuonAssociationAlg)
+  DECLARE_ALGORITHM (CP::JetModifierAlg)
+  DECLARE_ALGORITHM (CP::JetSelectionAlg)
+  DECLARE_ALGORITHM (CP::JetSmearingAlg)
+  DECLARE_ALGORITHM (CP::JetUncertaintiesAlg)
+  DECLARE_ALGORITHM (CP::JvtEfficiencyAlg)
+  DECLARE_ALGORITHM (CP::JvtUpdateAlg)
+}
diff --git a/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/src/components/JetAnalysisAlgorithms_load.cxx b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/src/components/JetAnalysisAlgorithms_load.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..e0d9aaf857bd6459352e06b881f084107585ac85
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/JetAnalysisAlgorithms/src/components/JetAnalysisAlgorithms_load.cxx
@@ -0,0 +1,5 @@
+// AsgExampleTools_load.cxx
+
+#include "GaudiKernel/LoadFactoryEntries.h"
+
+LOAD_FACTORY_ENTRIES(JetAnalysisAlgorithms)
diff --git a/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/CMakeLists.txt b/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..8705936bbbe40b9a13e33611ae1c9c68a8bf841b
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/CMakeLists.txt
@@ -0,0 +1,66 @@
+# Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+#
+# @author Nils Krumnack
+
+
+# The name of the package:
+atlas_subdir( MetAnalysisAlgorithms )
+
+# The package's dependencies:
+atlas_depends_on_subdirs(
+   PUBLIC
+   Event/xAOD/xAODMissingET
+   PhysicsAnalysis/Algorithms/SystematicsHandles
+   PhysicsAnalysis/D3PDTools/AnaAlgorithm
+   PhysicsAnalysis/Interfaces/METInterface
+   Reconstruction/MET/METInterface
+   Reconstruction/MET/METUtilities
+   PRIVATE )
+
+atlas_add_library( MetAnalysisAlgorithmsLib
+   MetAnalysisAlgorithms/*.h MetAnalysisAlgorithms/*.icc Root/*.cxx
+   PUBLIC_HEADERS MetAnalysisAlgorithms
+   LINK_LIBRARIES xAODMissingET
+   SystematicsHandlesLib AnaAlgorithmLib METInterface
+   SelectionHelpersLib METUtilitiesLib
+   PRIVATE_LINK_LIBRARIES  )
+
+atlas_add_dictionary( MetAnalysisAlgorithmsDict
+   MetAnalysisAlgorithms/MetAnalysisAlgorithmsDict.h
+   MetAnalysisAlgorithms/selection.xml
+   LINK_LIBRARIES MetAnalysisAlgorithmsLib )
+
+if( NOT XAOD_STANDALONE )
+   atlas_add_component( MetAnalysisAlgorithms
+      src/*.h src/*.cxx src/components/*.cxx
+      LINK_LIBRARIES GaudiKernel MetAnalysisAlgorithmsLib )
+endif()
+
+atlas_install_python_modules( python/*.py )
+atlas_install_joboptions( share/*_jobOptions.py )
+atlas_install_scripts( share/*_eljob.py )
+
+if( XAOD_STANDALONE )
+   atlas_add_test( testJobData
+      SCRIPT MetAnalysisAlgorithmsTest_eljob.py --data-type data --unit-test
+      PROPERTIES TIMEOUT 600 )
+   atlas_add_test( testJobFullSim
+      SCRIPT MetAnalysisAlgorithmsTest_eljob.py --data-type mc --unit-test
+      PROPERTIES TIMEOUT 600 )
+   atlas_add_test( testJobFastSim
+      SCRIPT MetAnalysisAlgorithmsTest_eljob.py --data-type afii --unit-test
+      PROPERTIES TIMEOUT 600 )
+elseif( NOT "${CMAKE_PROJECT_NAME}" STREQUAL "AthDerivation" )
+   atlas_add_test( testJobData
+      SCRIPT athena.py
+      MetAnalysisAlgorithms/MetAnalysisAlgorithmsTest_jobOptions.py - --data-type data
+      PROPERTIES TIMEOUT 600 )
+   atlas_add_test( testJobFullSim
+      SCRIPT athena.py
+      MetAnalysisAlgorithms/MetAnalysisAlgorithmsTest_jobOptions.py - --data-type mc
+      PROPERTIES TIMEOUT 600 )
+   atlas_add_test( testJobFastSim
+      SCRIPT athena.py
+      MetAnalysisAlgorithms/MetAnalysisAlgorithmsTest_jobOptions.py - --data-type afii
+      PROPERTIES TIMEOUT 600 )
+endif()
diff --git a/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/MetAnalysisAlgorithms/MetAnalysisAlgorithmsDict.h b/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/MetAnalysisAlgorithms/MetAnalysisAlgorithmsDict.h
new file mode 100644
index 0000000000000000000000000000000000000000..751f56e8171afc57f916267267c5cc3ffb60dc2f
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/MetAnalysisAlgorithms/MetAnalysisAlgorithmsDict.h
@@ -0,0 +1,16 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef MET_ANALYSIS_ALGORITHMS__MET_ANALYSIS_ALGORITHMS_DICT_H
+#define MET_ANALYSIS_ALGORITHMS__MET_ANALYSIS_ALGORITHMS_DICT_H
+
+#include <MetAnalysisAlgorithms/MetBuilderAlg.h>
+#include <MetAnalysisAlgorithms/MetMakerAlg.h>
+#include <MetAnalysisAlgorithms/MetSignificanceAlg.h>
+#include <MetAnalysisAlgorithms/MetSystematicsAlg.h>
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/MetAnalysisAlgorithms/MetBuilderAlg.h b/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/MetAnalysisAlgorithms/MetBuilderAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..fb9aaec2853ae602e5d5e54f1a7cb6121ebdc11c
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/MetAnalysisAlgorithms/MetBuilderAlg.h
@@ -0,0 +1,57 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+#ifndef MET_ANALYSIS_ALGORITHMS__MET_BUILDER_ALG_H
+#define MET_ANALYSIS_ALGORITHMS__MET_BUILDER_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+#include <METInterface/IMETMaker.h>
+#include <xAODMissingET/MissingETContainer.h>
+
+namespace CP
+{
+  /// \brief an algorithm for calling \ref IMetCalibrationAndSmearingTool
+
+  class MetBuilderAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    MetBuilderAlg (const std::string& name, 
+                 ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+    
+
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the met collection we run on
+  private:
+    SysCopyHandle<xAOD::MissingETContainer> m_metHandle {
+      this, "met", "MissingET_%SYS%", "the met collection we run on"};
+
+    /// \brief the key for the final met term
+  private:
+    std::string m_finalKey {"Final"};
+
+    /// \brief the key for the soft term
+  private:
+    std::string m_softTerm {"PVSoftTrk"};
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/MetAnalysisAlgorithms/MetMakerAlg.h b/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/MetAnalysisAlgorithms/MetMakerAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..da7cab77319412a73a3100b56b4b5b4f7a43882d
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/MetAnalysisAlgorithms/MetMakerAlg.h
@@ -0,0 +1,134 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+#ifndef MET_ANALYSIS_ALGORITHMS__MET_MAKER_ALG_H
+#define MET_ANALYSIS_ALGORITHMS__MET_MAKER_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <SystematicsHandles/SysReadHandle.h>
+#include <SystematicsHandles/SysWriteHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+#include <METInterface/IMETMaker.h>
+
+#include <xAODBase/IParticleContainer.h>
+#include <xAODMissingET/MissingETContainer.h>
+#include <xAODMissingET/MissingETAuxContainer.h>
+
+namespace CP
+{
+  /// \brief an algorithm for calling \ref IMETMaker
+  ///
+  /// This algorithm is fairly complex for a common CP algorithm.  The
+  /// main issue here is that the MET tools store temporary
+  /// information on the xAOD objects that gets reset on each
+  /// systematic, so a lot of actions have to happen in one go.
+  /// Despite that complexity it still can't be run on its own, you
+  /// always have to call \ref MetBuilderAlg afterwards to build the
+  /// final MET.
+
+  class MetMakerAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    MetMakerAlg (const std::string& name, 
+                 ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+    
+
+
+    /// \brief the smearing tool
+  private:
+    ToolHandle<IMETMaker> m_makerTool;
+
+    /// \brief the name of the core MissingETContainer
+  private:
+    std::string m_metCoreName;
+
+    /// \brief the name of the MissingETAssociationMap
+  private:
+    std::string m_metAssociationName;
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the electron container to use
+  private:
+    SysReadHandle<xAOD::IParticleContainer> m_electronsHandle {
+      this, "electrons", "", "the electron container to use" };
+
+    /// \brief the key for \ref m_electronsHandle
+  private:
+    std::string m_electronsKey {"RefEle"};
+
+    /// \brief the photon container to use
+  private:
+    SysReadHandle<xAOD::IParticleContainer> m_photonsHandle {
+      this, "photons", "", "the photon container to use" };
+
+    /// \brief the key for \ref m_photonsHandle
+  private:
+    std::string m_photonsKey {"RefGamma"};
+
+    /// \brief the muon container to use
+  private:
+    SysReadHandle<xAOD::IParticleContainer> m_muonsHandle {
+      this, "muons", "", "the muon container to use" };
+
+    /// \brief the key for \ref m_muonsHandle
+  private:
+    std::string m_muonsKey {"Muons"};
+
+    /// \brief the electron container to use
+  private:
+    SysReadHandle<xAOD::IParticleContainer> m_tausHandle {
+      this, "taus", "", "the tau container to use" };
+
+    /// \brief the key for \ref m_tausHandle
+  private:
+    std::string m_tausKey {"RefTau"};
+
+    /// \brief the input jet collection we run on
+  private:
+    SysReadHandle<xAOD::JetContainer> m_jetsHandle {
+      this, "jets", "", "the jet collection we use"};
+
+  private:
+    SysReadHandle<xAOD::IParticleContainer> m_invisHandle {
+      this, "invisible", "", "Any particles to treat as invisible."};
+
+    /// \brief the key for \ref m_jetsHandle
+  private:
+    std::string m_jetsKey {"RefJet"};
+
+    /// \brief the soft term key
+  private:
+    std::string m_softTermKey {"PVSoftTrk"};
+
+    /// \brief whether to use track-met instead of jet-met
+  private:
+    bool m_doTrackMet {false};
+
+    /// \brief whether to do jet JVT
+  private:
+    bool m_doJetJVT {true};
+
+    /// \brief the met collection we run on
+  private:
+    SysWriteHandle<xAOD::MissingETContainer,xAOD::MissingETAuxContainer> m_metHandle {
+      this, "met", "MissingET_%SYS%", "the met collection we produce"};
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/MetAnalysisAlgorithms/MetSignificanceAlg.h b/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/MetAnalysisAlgorithms/MetSignificanceAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..f2dd8fcc93af2f1f0f639721512c20aa88951309
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/MetAnalysisAlgorithms/MetSignificanceAlg.h
@@ -0,0 +1,74 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+#ifndef MET_ANALYSIS_ALGORITHMS__MET_SIGNIFICANCE_ALG_H
+#define MET_ANALYSIS_ALGORITHMS__MET_SIGNIFICANCE_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+#include <SystematicsHandles/SysWriteHandle.h>
+#include <METInterface/IMETSignificance.h>
+#include <xAODMissingET/MissingETContainer.h>
+
+namespace CP
+{
+  /// \brief an algorithm for calling \ref IMETSignificanceTool
+
+  class MetSignificanceAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    MetSignificanceAlg (const std::string& name, 
+                 ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+    
+
+
+    /// \brief the smearing tool
+  private:
+    ToolHandle<IMETSignificance> m_significanceTool;
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the met collection we run on
+  private:
+    SysCopyHandle<xAOD::MissingETContainer> m_metHandle {
+      this, "met", "MissingET_%SYS%", "the met collection we run on"};
+
+    /// \brief the key for the final met term
+  private:
+    std::string m_totalMETName {"Final"};
+
+    /// \brief the key for the jets term
+  private:
+    std::string m_jetTermName {"RefJet"};
+
+    /// \brief the key for the soft term
+  private:
+    std::string m_softTermName {"PVSoftTrk"};
+
+    /// \brief the decoration for the significance
+  private:
+    std::string m_significanceDecoration {"significance"};
+
+    /// \brief the accessor for \ref m_selectionDecoration
+  private:
+    std::unique_ptr<const SG::AuxElement::Accessor<float> > m_significanceAccessor;
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/MetAnalysisAlgorithms/MetSystematicsAlg.h b/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/MetAnalysisAlgorithms/MetSystematicsAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..cba1d161ef4467072008872141b653956d32489b
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/MetAnalysisAlgorithms/MetSystematicsAlg.h
@@ -0,0 +1,57 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+#ifndef MET_ANALYSIS_ALGORITHMS__MET_SYSTEMATICS_ALG_H
+#define MET_ANALYSIS_ALGORITHMS__MET_SYSTEMATICS_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+#include <METInterface/IMETSystematicsTool.h>
+#include <xAODMissingET/MissingETContainer.h>
+
+namespace CP
+{
+  /// \brief an algorithm for calling \ref IMetCalibrationAndSmearingTool
+
+  class MetSystematicsAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    MetSystematicsAlg (const std::string& name, 
+                 ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+    
+
+
+    /// \brief the smearing tool
+  private:
+    ToolHandle<IMETSystematicsTool> m_systematicsTool;
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the met collection we run on
+  private:
+    SysCopyHandle<xAOD::MissingETContainer> m_metHandle {
+      this, "met", "MissingET_%SYS%", "the met collection we run on"};
+
+    /// \brief the key for the soft term
+  private:
+    std::string m_softTerm {"PVSoftTrk"};
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/MetAnalysisAlgorithms/selection.xml b/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/MetAnalysisAlgorithms/selection.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c83c888c5044aabdc2ad83858b67ec1222229d77
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/MetAnalysisAlgorithms/selection.xml
@@ -0,0 +1,8 @@
+<lcgdict>
+
+   <class name="CP::MetBuilderAlg" />
+   <class name="CP::MetMakerAlg" />
+   <class name="CP::MetSignificanceAlg" />
+   <class name="CP::MetSystematicsAlg" />
+
+</lcgdict>
diff --git a/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/Root/MetBuilderAlg.cxx b/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/Root/MetBuilderAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..4a0f1c7c5a10de194f1beb8649e9112522b0b885
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/Root/MetBuilderAlg.cxx
@@ -0,0 +1,63 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+//
+// includes
+//
+
+#include <MetAnalysisAlgorithms/MetBuilderAlg.h>
+
+#include <METUtilities/METHelpers.h>
+#include <xAODMissingET/MissingETAuxContainer.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  MetBuilderAlg ::
+  MetBuilderAlg (const std::string& name, 
+                     ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+  {
+    declareProperty ("finalKey", m_finalKey, "the key for the final met term");
+    declareProperty ("softTerm", m_softTerm, "the key for the soft term");
+  }
+
+
+
+  StatusCode MetBuilderAlg ::
+  initialize ()
+  {
+    m_systematicsList.addHandle (m_metHandle);
+    ANA_CHECK (m_systematicsList.initialize());
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode MetBuilderAlg ::
+  execute ()
+  {
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode {
+        xAOD::MissingETContainer *met {};
+        ANA_CHECK (m_metHandle.getCopy (met, sys));
+
+        xAOD::MissingET *softTerm = (*met)[m_softTerm];
+        if (softTerm == nullptr)
+        {
+          ANA_MSG_ERROR ("could not find MET soft-term: " << m_softTerm);
+          return StatusCode::FAILURE;
+        }
+        ATH_CHECK (met::buildMETSum (m_finalKey, met, softTerm->source()));
+
+        return StatusCode::SUCCESS;
+      });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/Root/MetMakerAlg.cxx b/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/Root/MetMakerAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..6a4a76279d69e025523424b746adde48afcdb1ff
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/Root/MetMakerAlg.cxx
@@ -0,0 +1,125 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+//
+// includes
+//
+
+#include <MetAnalysisAlgorithms/MetMakerAlg.h>
+
+#include <xAODMissingET/MissingETAuxContainer.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  MetMakerAlg ::
+  MetMakerAlg (const std::string& name, 
+                     ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+    , m_makerTool ("METMaker", this)
+  {
+    declareProperty ("makerTool", m_makerTool, "the METMaker tool we apply");
+    declareProperty ("metCore", m_metCoreName, "the name of the core MissingETContainer");
+    declareProperty ("metAssociation", m_metAssociationName, "the name of the core MissingETContainer");
+    declareProperty ("electronsKey", m_electronsKey, "the key for the electrons");
+    declareProperty ("photonsKey", m_photonsKey, "the key for the photons");
+    declareProperty ("muonsKey", m_muonsKey, "the key for the muons");
+    declareProperty ("tausKey", m_tausKey, "the key for the taus");
+    declareProperty ("jetsKey", m_jetsKey, "the key for jets");
+    declareProperty ("softTermKey", m_softTermKey, "the soft term key");
+    declareProperty ("doTrackMet", m_doTrackMet, "whether to use track-met instead of jet-met");
+    declareProperty ("doJetJVT", m_doJetJVT, "whether to do jet JVT");
+  }
+
+
+
+  StatusCode MetMakerAlg ::
+  initialize ()
+  {
+    ANA_CHECK (m_makerTool.retrieve());
+    for (auto* handle : {&m_electronsHandle, &m_photonsHandle,
+                         &m_muonsHandle, &m_tausHandle, &m_invisHandle}) {
+      if (*handle) {
+        m_systematicsList.addHandle (*handle);
+      }
+    }
+    m_systematicsList.addHandle (m_jetsHandle);
+    m_systematicsList.addHandle (m_metHandle);
+    ANA_CHECK (m_systematicsList.initialize());
+
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode MetMakerAlg ::
+  execute ()
+  {
+    const xAOD::MissingETContainer* metcore {nullptr};
+    ANA_CHECK (evtStore()->retrieve(metcore, m_metCoreName));
+
+    const xAOD::MissingETAssociationMap* metMap {nullptr};
+    ANA_CHECK (evtStore()->retrieve(metMap, m_metAssociationName));
+
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode {
+        auto met = std::make_unique<xAOD::MissingETContainer> ();
+        auto aux = std::make_unique<xAOD::MissingETAuxContainer> ();
+        met->setStore (aux.get());
+
+        metMap->resetObjSelectionFlags();
+
+        if (m_invisHandle) {
+          const xAOD::IParticleContainer* invisible = nullptr;
+          ATH_CHECK( m_invisHandle.retrieve(invisible, sys) );
+          ATH_CHECK( m_makerTool->markInvisible(invisible, metMap, met.get() ) );
+        }
+
+        // Lambda helping with calculating the MET terms coming from the leptons
+        // (and photons).
+        auto processParticles =
+          [&] (SysReadHandle<xAOD::IParticleContainer>& handle,
+               xAOD::Type::ObjectType type,
+               const std::string& term) -> StatusCode {
+            if (!handle) {
+              return StatusCode::SUCCESS;
+            }
+            const xAOD::IParticleContainer* particles = nullptr;
+            ANA_CHECK (handle.retrieve (particles, sys));
+            ANA_CHECK (m_makerTool->rebuildMET (term, type, met.get(),
+                                                particles, metMap));
+            return StatusCode::SUCCESS;
+          };
+
+        // Calculate the terms coming from the user's selected objects.
+        ANA_CHECK (processParticles (m_electronsHandle, xAOD::Type::Electron,
+                                     m_electronsKey));
+        ANA_CHECK (processParticles (m_photonsHandle, xAOD::Type::Photon,
+                                     m_photonsKey));
+        ANA_CHECK (processParticles (m_tausHandle, xAOD::Type::Tau, m_tausKey));
+        ANA_CHECK (processParticles (m_muonsHandle, xAOD::Type::Muon,
+                                     m_muonsKey));
+
+        const xAOD::JetContainer *jets {nullptr};
+        ANA_CHECK (m_jetsHandle.retrieve (jets, sys));
+	
+        if (m_doTrackMet)
+        {
+          ANA_CHECK (m_makerTool->rebuildTrackMET (m_jetsKey, m_softTermKey, met.get(), jets, metcore, metMap, m_doJetJVT));
+        } else
+        {
+          ANA_CHECK (m_makerTool->rebuildJetMET (m_jetsKey, m_softTermKey, met.get(), jets, metcore, metMap, m_doJetJVT));
+        }
+
+        ANA_CHECK (m_metHandle.record (std::move (met), std::move (aux), sys));
+        return StatusCode::SUCCESS;
+      });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/Root/MetSignificanceAlg.cxx b/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/Root/MetSignificanceAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..e2a7c72855e96eb177669e0dff2c2acaf5702bde
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/Root/MetSignificanceAlg.cxx
@@ -0,0 +1,75 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+//
+// includes
+//
+
+#include <MetAnalysisAlgorithms/MetSignificanceAlg.h>
+
+#include <xAODMissingET/MissingETAuxContainer.h>
+#include "xAODEventInfo/EventInfo.h"
+
+//
+// method implementations
+//
+namespace CP
+{
+  MetSignificanceAlg ::
+  MetSignificanceAlg (const std::string& name, 
+                     ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+    , m_significanceTool ("METMaker", this)
+  {
+    declareProperty ("significanceTool", m_significanceTool, "the significance tool we apply");
+    declareProperty ("significanceDecoration", m_significanceDecoration, "the decoration to use for the significance");
+    declareProperty ("totalMETName", m_totalMETName, "the key for the final met term");
+    declareProperty ("jetTermName", m_jetTermName, "the key for the jets term");
+    declareProperty ("softTermName", m_softTermName, "the key for the soft term");
+  }
+
+
+
+  StatusCode MetSignificanceAlg ::
+  initialize ()
+  {
+    if (m_significanceDecoration.empty())
+    {
+      ANA_MSG_ERROR ("no significance decoration name set");
+      return StatusCode::FAILURE;
+    }
+    m_significanceAccessor = std::make_unique<SG::AuxElement::Accessor<float> > (m_significanceDecoration);
+
+    ANA_CHECK (m_significanceTool.retrieve());
+    m_systematicsList.addHandle (m_metHandle);
+    ANA_CHECK (m_systematicsList.initialize());
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode MetSignificanceAlg ::
+  execute ()
+  {
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode {
+        // I'm not sure why this can't be const, but the interface
+        // requires a non-const object
+        xAOD::MissingETContainer *met {};
+        ANA_CHECK (m_metHandle.getCopy (met, sys));
+	
+	const xAOD::EventInfo* evtInfo = 0;
+	ANA_CHECK( evtStore()->retrieve( evtInfo, "EventInfo" ) );
+
+        ANA_CHECK (m_significanceTool->varianceMET (met, evtInfo->averageInteractionsPerCrossing(), m_jetTermName, m_softTermName, m_totalMETName));
+        const float significance = m_significanceTool->GetSignificance();
+        (*m_significanceAccessor) (*(*met)[m_totalMETName]) = significance;
+
+        return StatusCode::SUCCESS;
+      });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/Root/MetSystematicsAlg.cxx b/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/Root/MetSystematicsAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..1e255b6581d66bc07cda3bfc34b1b94bc59cff90
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/Root/MetSystematicsAlg.cxx
@@ -0,0 +1,72 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+//
+// includes
+//
+
+#include <MetAnalysisAlgorithms/MetSystematicsAlg.h>
+
+#include <xAODMissingET/MissingETAuxContainer.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  MetSystematicsAlg ::
+  MetSystematicsAlg (const std::string& name, 
+                     ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+    , m_systematicsTool ("met::METSystematicsTool", this)
+  {
+    declareProperty ("systematicsTool", m_systematicsTool, "the systematics tool we apply");
+    declareProperty ("softTerm", m_softTerm, "the key for the soft term");
+  }
+
+
+
+  StatusCode MetSystematicsAlg ::
+  initialize ()
+  {
+    ANA_CHECK (m_systematicsTool.retrieve());
+    m_systematicsList.addHandle (m_metHandle);
+    ANA_CHECK (m_systematicsList.addAffectingSystematics (m_systematicsTool->affectingSystematics()));
+    ANA_CHECK (m_systematicsList.initialize());
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode MetSystematicsAlg ::
+  execute ()
+  {
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode {
+        ANA_CHECK (m_systematicsTool->applySystematicVariation (sys));
+
+        xAOD::MissingETContainer *met {};
+        ANA_CHECK (m_metHandle.getCopy (met, sys));
+
+        xAOD::MissingET *softTerm = (*met)[m_softTerm];
+        if (softTerm == nullptr)
+        {
+          ANA_MSG_ERROR ("failed to find MET soft-term \"" << m_softTerm << "\"");
+          return StatusCode::FAILURE;
+        }
+
+        // This returns a `CorrectionCode`, so in principle this could
+        // return an `OutOfValidity` result, but I have no idea what
+        // that would mean or how to handle it, so I'm implicitly
+        // converting it into a `FAILURE` instead.
+        ANA_CHECK (m_systematicsTool->applyCorrection (*softTerm));
+
+        return StatusCode::SUCCESS;
+      });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/python/MetAnalysisAlgorithmsTest.py b/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/python/MetAnalysisAlgorithmsTest.py
new file mode 100644
index 0000000000000000000000000000000000000000..3086cdaabaf0c198098f53212494051e8c4c7a8a
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/python/MetAnalysisAlgorithmsTest.py
@@ -0,0 +1,75 @@
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+
+from AnaAlgorithm.AlgSequence import AlgSequence
+from AnaAlgorithm.DualUseConfig import addPrivateTool, createAlgorithm
+
+def makeSequence (dataType) :
+    algSeq = AlgSequence()
+
+    # Set up the systematics loader/handler algorithm:
+    sysLoader = createAlgorithm( 'CP::SysListLoaderAlg', 'SysLoaderAlg' )
+    sysLoader.sigmaRecommended = 1
+    algSeq += sysLoader
+
+    # Include, and then set up the jet analysis algorithm sequence:
+    from JetAnalysisAlgorithms.JetAnalysisSequence import makeJetAnalysisSequence
+    jetContainer = 'AntiKt4EMPFlowJets'
+    jetSequence = makeJetAnalysisSequence( dataType, jetContainer )
+    jetSequence.configure( inputName = jetContainer, outputName = 'AnalysisJets_%SYS%' )
+
+    # Add all algorithms to the job:
+    algSeq += jetSequence
+
+    # Set up a selection alg for demonstration purposes
+    # Also to avoid warnings from building MET with very soft electrons
+    selalg = createAlgorithm( 'CP::AsgSelectionAlg', 'METEleSelAlg' )
+    addPrivateTool( selalg, 'selectionTool', 'CP::AsgPtEtaSelectionTool' )
+    selalg.selectionTool.minPt = 10e3
+    selalg.selectionTool.maxEta = 2.47
+    selalg.selectionDecoration = 'selectPtEta'
+    selalg.particles = 'Electrons'
+    # We need to copy here, because w/o an output container, it's assumed
+    # that the input container is non-const
+    selalg.particlesOut = 'DecorElectrons_%SYS%'
+    algSeq += selalg
+
+    # Now make a view container holding only the electrons for the MET calculation
+    viewalg = createAlgorithm( 'CP::AsgViewFromSelectionAlg','METEleViewAlg' )
+    viewalg.selection = [ 'selectPtEta' ]
+    viewalg.input = 'DecorElectrons_%SYS%'
+    viewalg.output = 'METElectrons_%SYS%'
+    algSeq += viewalg
+
+    # Include, and then set up the met analysis algorithm sequence:
+    from MetAnalysisAlgorithms.MetAnalysisSequence import makeMetAnalysisSequence
+    metSequence = makeMetAnalysisSequence( dataType, metSuffix = jetContainer[:-4] )
+    metSequence.configure( inputName = { 'jets'      : 'AnalysisJets_%SYS%',
+                                         'muons'     : 'Muons',
+                                         'electrons' : 'METElectrons_%SYS%' },
+                           outputName = 'AnalysisMET_%SYS%',
+                           affectingSystematics = { 'jets'      : jetSequence.affectingSystematics(),
+                                                    'muons'     : '(^$)',
+                                                    'electrons' : '(^$)' } )
+
+    # Add the sequence to the job:
+    algSeq += metSequence
+
+    # Write the freshly produced MET object(s) to an output file:
+    treeMaker = createAlgorithm( 'CP::TreeMakerAlg', 'TreeMaker' )
+    treeMaker.TreeName = 'met'
+    algSeq += treeMaker
+    ntupleMaker = createAlgorithm( 'CP::AsgxAODNTupleMakerAlg', 'NTupleMaker' )
+    ntupleMaker.TreeName = 'met'
+    ntupleMaker.Branches = [ 'EventInfo.runNumber     -> runNumber',
+                             'EventInfo.eventNumber   -> eventNumber',
+                             'AnalysisMET_%SYS%.mpx   -> met_%SYS%_mpx',
+                             'AnalysisMET_%SYS%.mpy   -> met_%SYS%_mpy',
+                             'AnalysisMET_%SYS%.sumet -> met_%SYS%_sumet',
+                             'AnalysisMET_%SYS%.name  -> met_%SYS%_name', ]
+    ntupleMaker.systematicsRegex = '.*'
+    algSeq += ntupleMaker
+    treeFiller = createAlgorithm( 'CP::TreeFillerAlg', 'TreeFiller' )
+    treeFiller.TreeName = 'met'
+    algSeq += treeFiller
+
+    return algSeq
diff --git a/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/python/MetAnalysisSequence.py b/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/python/MetAnalysisSequence.py
new file mode 100644
index 0000000000000000000000000000000000000000..a20926c5c835ec62b6471c6ea8c1027b3ebcd6b2
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/python/MetAnalysisSequence.py
@@ -0,0 +1,95 @@
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+
+# AnaAlgorithm import(s):
+from AnaAlgorithm.AnaAlgSequence import AnaAlgSequence
+from AnaAlgorithm.DualUseConfig import createAlgorithm, addPrivateTool
+
+def makeMetAnalysisSequence( dataType, metSuffix,
+                             postfix = '',
+                             useFJVT = True,
+                             treatPUJets = True ):
+    """Create a met analysis algorithm sequence
+
+    After creating the sequence object, it needs to be configured with a call
+    like:
+
+       metSequence.configure( inputName = {
+                                 'jets'      : 'AntiKt4EMPFlowJets_%SYS%',
+                                 'electrons' : 'AnalysisElectrons_%SYS%',
+                                 'photons'   : 'AnalysisPhotons_%SYS%',
+                                 'muons'     : 'AnalysisMuons_%SYS%',
+                                 'taus'      : 'AnalysisTaus_%STS%',
+                              },
+                              outputName = 'AnalysisMET_%SYS%',
+                              affectingSystematics = {
+                                 'jets'      : '(^$)|(^JET_.*)',
+                                 'electrons' : '(^$)|(^EG_.*)|(^EL_.*)',
+                                 'photons'   : '(^$)|(^EG_.*)|(^PH_.*)',
+                                 'muons'     : '(^$)|(^MUON_.*)',
+                                 'taus'      : '(^$)|(^TAUS_.*)',
+                              } )
+
+    Note that defining a jet container is mandatory, but all other input
+    containers are optional.
+
+    Keyword arguments:
+      dataType -- The data type to run on ("data", "mc" or "afii")
+      metSuffix -- Suffix for the (core) MET objects to use from the input
+                   (file)
+      useFJVT -- Use FJVT decision for the calculation
+      treatPUJets -- Treat pile-up jets in the MET significance calculation
+    """
+
+    if dataType not in ["data", "mc", "afii"] :
+        raise ValueError ("invalid data type: " + dataType)
+
+    if not useFJVT and treatPUJets:
+        raise ValueError ("MET significance pile-up treatment requires fJVT")
+
+    # Remove b-tagging calibration from the MET suffix name
+    btIndex = metSuffix.find('_BTagging')
+    if btIndex != -1:
+        metSuffix = metSuffix[:btIndex]
+
+    # Create the analysis algorithm sequence object:
+    seq = AnaAlgSequence( "MetAnalysisSequence" + postfix )
+
+    # Set up the met maker algorithm:
+    alg = createAlgorithm( 'CP::MetMakerAlg', 'MetMakerAlg' + postfix)
+    addPrivateTool( alg, 'makerTool', 'met::METMaker' )
+    alg.makerTool.DoPFlow = 'PFlow' in metSuffix
+    if useFJVT:
+        alg.makerTool.JetRejectionDec = 'passFJVT'
+    alg.metCore = 'MET_Core_' + metSuffix
+    alg.metAssociation = 'METAssoc_' + metSuffix
+    seq.append( alg,
+                inputPropName = { 'jets'      : 'jets',
+                                  'electrons' : 'electrons',
+                                  'photons'   : 'photons',
+                                  'muons'     : 'muons',
+                                  'taus'      : 'taus',
+                                  'invisible' : 'invisible'},
+                outputPropName = 'met',
+                affectingSystematics = '(^MET_.*)' )
+
+    if dataType != "data" :
+        alg = createAlgorithm( 'CP::MetSystematicsAlg', 'MetSystematicsAlg' + postfix )
+        addPrivateTool( alg, 'systematicsTool', 'met::METSystematicsTool' )
+        seq.append( alg, inputPropName = 'met',
+                    affectingSystematics = '(^MET_.*)' )
+        pass
+
+    # Set up the met builder algorithm:
+    alg = createAlgorithm( 'CP::MetBuilderAlg', 'MetBuilderAlg' + postfix )
+    seq.append( alg, inputPropName = 'met' )
+
+    # Set up the met significance algorithm:
+    alg = createAlgorithm( 'CP::MetSignificanceAlg', 'MetSignificanceAlg' + postfix )
+    addPrivateTool( alg, 'significanceTool', 'met::METSignificance' )
+    alg.significanceTool.SoftTermParam = 0
+    alg.significanceTool.TreatPUJets = treatPUJets
+    alg.significanceTool.IsAFII = dataType == "afii"
+    seq.append( alg, inputPropName = 'met' )
+
+    # Return the sequence:
+    return seq
diff --git a/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/python/__init__.py b/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/python/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..5ccb446532599c01cc3844e5b1b18db06f721002
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/python/__init__.py
@@ -0,0 +1,3 @@
+# Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+
+__version__ = '1.0.0'
diff --git a/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/share/MetAnalysisAlgorithmsTest_eljob.py b/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/share/MetAnalysisAlgorithmsTest_eljob.py
new file mode 100755
index 0000000000000000000000000000000000000000..bdcc1d76fda56b9cd117006de3d1082dbc82a771
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/share/MetAnalysisAlgorithmsTest_eljob.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+#
+# @author Nils Krumnack
+
+
+# Read the submission directory as a command line argument. You can
+# extend the list of arguments with your private ones later on.
+import optparse
+parser = optparse.OptionParser()
+parser.add_option( '-d', '--data-type', dest = 'data_type',
+                   action = 'store', type = 'string', default = 'data',
+                   help = 'Type of data to run over. Valid options are data, mc, afii' )
+parser.add_option( '-s', '--submission-dir', dest = 'submission_dir',
+                   action = 'store', type = 'string', default = 'submitDir',
+                   help = 'Submission directory for EventLoop' )
+parser.add_option( '-u', '--unit-test', dest='unit_test',
+                   action = 'store_true', default = False,
+                   help = 'Run the job in "unit test mode"' )
+
+( options, args ) = parser.parse_args()
+
+# Set up (Py)ROOT.
+import ROOT
+ROOT.xAOD.Init().ignore()
+
+# ideally we'd run over all of them, but we don't have a mechanism to
+# configure per-sample right now
+
+dataType = options.data_type
+
+if dataType not in ["data", "mc", "afii"] :
+    raise Exception ("invalid data type: " + dataType)
+
+# Set up the sample handler object. See comments from the C++ macro
+# for the details about these lines.
+import os
+sh = ROOT.SH.SampleHandler()
+sh.setMetaString( 'nc_tree', 'CollectionTree' )
+sample = ROOT.SH.SampleLocal (dataType)
+if dataType == "data" :
+    sample.add (os.getenv ('ASG_TEST_FILE_DATA'))
+    pass
+if dataType == "mc" :
+    sample.add (os.getenv ('ASG_TEST_FILE_MC'))
+    pass
+if dataType == "afii" :
+    sample.add (os.getenv ('ASG_TEST_FILE_MC_AFII'))
+    pass
+sh.add (sample)
+sh.printContent()
+
+# Create an EventLoop job.
+job = ROOT.EL.Job()
+job.sampleHandler( sh )
+job.options().setDouble( ROOT.EL.Job.optMaxEvents, 500 )
+
+job.outputAdd( ROOT.EL.OutputStream( 'ANALYSIS' ) )
+
+from MetAnalysisAlgorithms.MetAnalysisAlgorithmsTest import makeSequence
+algSeq = makeSequence (dataType)
+print algSeq # For debugging
+for alg in algSeq:
+    job.algsAdd( alg )
+    pass
+
+# Find the right output directory:                                                                                      
+submitDir = options.submission_dir
+if options.unit_test:
+    import os
+    import tempfile
+    submitDir = tempfile.mkdtemp( prefix = 'metTest_'+dataType+'_', dir = os.getcwd() )
+    os.rmdir( submitDir )
+    pass
+
+# Run the job using the direct driver.
+driver = ROOT.EL.DirectDriver()
+driver.submit( job, submitDir )
diff --git a/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/share/MetAnalysisAlgorithmsTest_jobOptions.py b/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/share/MetAnalysisAlgorithmsTest_jobOptions.py
new file mode 100644
index 0000000000000000000000000000000000000000..b78287fabb906189f45f9a9bbae951fcf0a36ad2
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/share/MetAnalysisAlgorithmsTest_jobOptions.py
@@ -0,0 +1,44 @@
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+#
+# @author Nils Krumnack
+
+# User options, which can be set from command line after a "-" character
+# athena EgammaAlgorithmsTest_jobOptions.py - --myOption ...
+from AthenaCommon.AthArgumentParser import AthArgumentParser
+athArgsParser = AthArgumentParser()
+athArgsParser.add_argument("--data-type", action = "store", dest = "data_type",
+                           default = "data",
+                           help = "Type of input to run over. Valid options are 'data', 'mc', 'afii'")
+athArgs = athArgsParser.parse_args()
+
+dataType = athArgs.data_type
+if not dataType in ["data", "mc", "afii"] :
+    raise Exception ("invalid data type: " + dataType)
+
+print("Running on data type: " + dataType)
+
+inputfile = {"data": 'ASG_TEST_FILE_DATA',
+             "mc":   'ASG_TEST_FILE_MC',
+             "afii": 'ASG_TEST_FILE_MC_AFII'}
+
+# Set up the reading of the input file:
+import AthenaRootComps.ReadAthenaxAODHybrid
+theApp.EvtMax = 500
+testFile = os.getenv ( inputfile[dataType] )
+svcMgr.EventSelector.InputCollections = [testFile]
+
+from MetAnalysisAlgorithms.MetAnalysisAlgorithmsTest import makeSequence
+algSeq = makeSequence (dataType)
+print algSeq # For debugging
+
+# Add all algorithms from the sequence to the job.
+athAlgSeq += algSeq
+
+# Set up a histogram output file for the job:
+ServiceMgr += CfgMgr.THistSvc()
+ServiceMgr.THistSvc.Output += [
+    "ANALYSIS DATAFILE='MetAnalysisAlgorithmsTest." + dataType + ".hist.root' OPT='RECREATE'"
+    ]
+
+# Reduce the printout from Athena:
+include( "AthAnalysisBaseComps/SuppressLogging.py" )
diff --git a/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/src/components/MetAnalysisAlgorithms_entries.cxx b/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/src/components/MetAnalysisAlgorithms_entries.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..0c6538358cc1095944ab4edeb8285e84e0c49d4f
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/src/components/MetAnalysisAlgorithms_entries.cxx
@@ -0,0 +1,20 @@
+// AsgExampleTools_entries.cxx
+
+#include <GaudiKernel/DeclareFactoryEntries.h>
+
+#include <MetAnalysisAlgorithms/MetBuilderAlg.h>
+#include <MetAnalysisAlgorithms/MetMakerAlg.h>
+#include <MetAnalysisAlgorithms/MetSignificanceAlg.h>
+#include <MetAnalysisAlgorithms/MetSystematicsAlg.h>
+
+DECLARE_ALGORITHM_FACTORY (CP::MetBuilderAlg)
+DECLARE_ALGORITHM_FACTORY (CP::MetMakerAlg)
+DECLARE_ALGORITHM_FACTORY (CP::MetSignificanceAlg)
+DECLARE_ALGORITHM_FACTORY (CP::MetSystematicsAlg)
+
+DECLARE_FACTORY_ENTRIES(MetAnalysisAlgorithms) {
+  DECLARE_ALGORITHM (CP::MetBuilderAlg)
+  DECLARE_ALGORITHM (CP::MetMakerAlg)
+  DECLARE_ALGORITHM (CP::MetSignificanceAlg)
+  DECLARE_ALGORITHM (CP::MetSystematicsAlg)
+}
diff --git a/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/src/components/MetAnalysisAlgorithms_load.cxx b/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/src/components/MetAnalysisAlgorithms_load.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..9bcc930733e1332169b3504f4a0ac213ae81221c
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/src/components/MetAnalysisAlgorithms_load.cxx
@@ -0,0 +1,5 @@
+// AsgExampleTools_load.cxx
+
+#include "GaudiKernel/LoadFactoryEntries.h"
+
+LOAD_FACTORY_ENTRIES(MetAnalysisAlgorithms)
diff --git a/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/CMakeLists.txt b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a43af6b8437e1c40611133a73b0d9a0c5fe0f585
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/CMakeLists.txt
@@ -0,0 +1,68 @@
+# Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+#
+# @author Nils Krumnack
+
+
+# The name of the package:
+atlas_subdir( MuonAnalysisAlgorithms )
+
+# The package's dependencies:
+atlas_depends_on_subdirs(
+   PUBLIC
+   Event/xAOD/xAODEventInfo
+   Event/xAOD/xAODMuon
+   PhysicsAnalysis/Algorithms/SelectionHelpers
+   PhysicsAnalysis/Algorithms/SystematicsHandles
+   PhysicsAnalysis/AnalysisCommon/IsolationSelection
+   PhysicsAnalysis/D3PDTools/AnaAlgorithm
+   PhysicsAnalysis/Interfaces/MuonAnalysisInterfaces
+   PRIVATE
+   PhysicsAnalysis/D3PDTools/RootCoreUtils )
+
+atlas_add_library( MuonAnalysisAlgorithmsLib
+   MuonAnalysisAlgorithms/*.h MuonAnalysisAlgorithms/*.icc Root/*.cxx
+   PUBLIC_HEADERS MuonAnalysisAlgorithms
+   LINK_LIBRARIES xAODEventInfo xAODMuon SelectionHelpersLib
+   SystematicsHandlesLib MuonAnalysisInterfacesLib IsolationSelectionLib
+   AnaAlgorithmLib
+   PRIVATE_LINK_LIBRARIES RootCoreUtils )
+
+atlas_add_dictionary( MuonAnalysisAlgorithmsDict
+   MuonAnalysisAlgorithms/MuonAnalysisAlgorithmsDict.h
+   MuonAnalysisAlgorithms/selection.xml
+   LINK_LIBRARIES MuonAnalysisAlgorithmsLib )
+
+if( NOT XAOD_STANDALONE )
+   atlas_add_component( MuonAnalysisAlgorithms
+      src/*.h src/*.cxx src/components/*.cxx
+      LINK_LIBRARIES GaudiKernel MuonAnalysisAlgorithmsLib )
+endif()
+
+atlas_install_python_modules( python/*.py )
+atlas_install_joboptions( share/*_jobOptions.py )
+atlas_install_scripts( share/*_eljob.py )
+
+if( XAOD_STANDALONE )
+   atlas_add_test( testJobData
+      SCRIPT MuonAnalysisAlgorithmsTest_eljob.py --data-type data --unit-test
+      PROPERTIES TIMEOUT 600 )
+   atlas_add_test( testJobFullSim
+      SCRIPT MuonAnalysisAlgorithmsTest_eljob.py --data-type mc --unit-test
+      PROPERTIES TIMEOUT 600 )
+   atlas_add_test( testJobFastSim
+      SCRIPT MuonAnalysisAlgorithmsTest_eljob.py --data-type afii --unit-test
+      PROPERTIES TIMEOUT 600 )
+else()
+   atlas_add_test( testJobData
+      SCRIPT athena.py
+      MuonAnalysisAlgorithms/MuonAnalysisAlgorithmsTest_jobOptions.py --evtMax=500 - --data-type data
+      PROPERTIES TIMEOUT 600 )
+   atlas_add_test( testJobFullSim
+      SCRIPT athena.py
+      MuonAnalysisAlgorithms/MuonAnalysisAlgorithmsTest_jobOptions.py --evtMax=500 - --data-type mc
+      PROPERTIES TIMEOUT 600 )
+   atlas_add_test( testJobFastSim
+      SCRIPT athena.py
+      MuonAnalysisAlgorithms/MuonAnalysisAlgorithmsTest_jobOptions.py --evtMax=500 - --data-type afii
+      PROPERTIES TIMEOUT 600 )
+endif()
diff --git a/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/MuonAnalysisAlgorithms/MuonAnalysisAlgorithmsDict.h b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/MuonAnalysisAlgorithms/MuonAnalysisAlgorithmsDict.h
new file mode 100644
index 0000000000000000000000000000000000000000..7f54f49de8ad94a10d33a5a94599b32b294f77fc
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/MuonAnalysisAlgorithms/MuonAnalysisAlgorithmsDict.h
@@ -0,0 +1,17 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef MUON_ANALYSIS_ALGORITHMS__MUON_ANALYSIS_ALGORITHMS_DICT_H
+#define MUON_ANALYSIS_ALGORITHMS__MUON_ANALYSIS_ALGORITHMS_DICT_H
+
+#include <MuonAnalysisAlgorithms/MuonCalibrationAndSmearingAlg.h>
+#include <MuonAnalysisAlgorithms/MuonEfficiencyScaleFactorAlg.h>
+#include <MuonAnalysisAlgorithms/MuonIsolationAlg.h>
+#include <MuonAnalysisAlgorithms/MuonSelectionAlg.h>
+#include <MuonAnalysisAlgorithms/MuonTriggerEfficiencyScaleFactorAlg.h>
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/MuonAnalysisAlgorithms/MuonCalibrationAndSmearingAlg.h b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/MuonAnalysisAlgorithms/MuonCalibrationAndSmearingAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..36daa86cc57a73e44e060b3f5fd6d59b95ea5fdf
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/MuonAnalysisAlgorithms/MuonCalibrationAndSmearingAlg.h
@@ -0,0 +1,64 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+#ifndef MUON_ANALYSIS_ALGORITHMS__MUON_CALIBRATION_AND_SMEARING_ALG_H
+#define MUON_ANALYSIS_ALGORITHMS__MUON_CALIBRATION_AND_SMEARING_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <MuonAnalysisInterfaces/IMuonCalibrationAndSmearingTool.h>
+#include <SelectionHelpers/OutOfValidityHelper.h>
+#include <SelectionHelpers/SelectionReadHandle.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+#include <xAODMuon/MuonContainer.h>
+
+namespace CP
+{
+  /// \brief an algorithm for calling \ref IMuonCalibrationAndSmearingTool
+
+  class MuonCalibrationAndSmearingAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    MuonCalibrationAndSmearingAlg (const std::string& name, 
+                                   ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+    
+
+
+    /// \brief the smearing tool
+  private:
+    ToolHandle<IMuonCalibrationAndSmearingTool> m_calibrationAndSmearingTool;
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the muon collection we run on
+  private:
+    SysCopyHandle<xAOD::MuonContainer> m_muonHandle {
+      this, "muons", "Muons", "the muon collection to run on"};
+
+    /// \brief the preselection we apply to our input
+  private:
+    SelectionReadHandle m_preselection {
+      this, "preselection", "", "the preselection to apply"};
+
+    /// \brief the helper for OutOfValidity results
+  private:
+    OutOfValidityHelper m_outOfValidity {this};
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/MuonAnalysisAlgorithms/MuonEfficiencyScaleFactorAlg.h b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/MuonAnalysisAlgorithms/MuonEfficiencyScaleFactorAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..cec9c4ad817b0d23399871da6ee19889327d3355
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/MuonAnalysisAlgorithms/MuonEfficiencyScaleFactorAlg.h
@@ -0,0 +1,86 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef MUON_ANALYSIS_ALGORITHMS__MUON_EFFICIENCY_SCALE_FACTOR_ALG_H
+#define MUON_ANALYSIS_ALGORITHMS__MUON_EFFICIENCY_SCALE_FACTOR_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <MuonAnalysisInterfaces/IMuonEfficiencyScaleFactors.h>
+#include <SelectionHelpers/OutOfValidityHelper.h>
+#include <SelectionHelpers/SelectionReadHandle.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <SystematicsHandles/SysDecorationHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+#include <SystematicsHandles/SysReadHandle.h>
+#include <xAODEventInfo/EventInfo.h>
+#include <xAODMuon/MuonContainer.h>
+
+namespace CP
+{
+  /// \brief an algorithm for calling \ref IMuonEfficiencyScaleFactorTool
+
+  class MuonEfficiencyScaleFactorAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    MuonEfficiencyScaleFactorAlg (const std::string& name, 
+                                   ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+    
+
+
+    /// \brief the smearing tool
+  private:
+    ToolHandle<IMuonEfficiencyScaleFactors> m_efficiencyScaleFactorTool;
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the muon collection we run on
+  private:
+    SysCopyHandle<xAOD::MuonContainer> m_muonHandle {
+      this, "muons", "Muons", "the muon collection to run on"};
+
+    /// \brief the EventInfo collection we use
+  private:
+    SysReadHandle<xAOD::EventInfo> m_eventInfoHandle {
+      this, "eventInfo", "EventInfo", "the EventInfo we use"};
+
+    /// \brief the preselection we apply to our input
+  private:
+    SelectionReadHandle m_preselection {
+      this, "preselection", "", "the preselection to apply"};
+
+    /// \brief the helper for OutOfValidity results
+  private:
+    OutOfValidityHelper m_outOfValidity {this};
+
+    /// \brief the decoration for the muon scale factor
+  private:
+    SysDecorationHandle<float> m_scaleFactorDecoration {
+      this, "scaleFactorDecoration", "", "the decoration for the muon efficiency scale factor"};
+
+    /// \brief the decoration for the muon mc efficiency
+  private:
+    SysDecorationHandle<float> m_mcEfficiencyDecoration {
+      this, "mcEfficiencyDecoration", "", "the decoration for the muon MC efficiency"};
+
+    /// \brief the decoration for the muon data efficiency
+  private:
+    SysDecorationHandle<float> m_dataEfficiencyDecoration {
+      this, "dataEfficiencyDecoration", "", "the decoration for the muon data efficiency"};
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/MuonAnalysisAlgorithms/MuonIsolationAlg.h b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/MuonAnalysisAlgorithms/MuonIsolationAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..3f8b83f301f2d85b32067698be7c7250a6d6d8e8
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/MuonAnalysisAlgorithms/MuonIsolationAlg.h
@@ -0,0 +1,72 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+#ifndef MUON_ANALYSIS_ALGORITHMS__MUON_ISOLATION_ALG_H
+#define MUON_ANALYSIS_ALGORITHMS__MUON_ISOLATION_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <IsolationSelection/IIsolationSelectionTool.h>
+#include <SelectionHelpers/ISelectionAccessor.h>
+#include <SelectionHelpers/SelectionReadHandle.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+#include <xAODMuon/MuonContainer.h>
+
+namespace CP
+{
+  /// \brief an algorithm for calling \ref IMuonSelectionTool
+
+  class MuonIsolationAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    MuonIsolationAlg (const std::string& name, 
+                                   ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+    
+
+
+    /// \brief the smearing tool
+  private:
+    ToolHandle<IIsolationSelectionTool> m_isolationTool;
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the muon collection we run on
+  private:
+    SysCopyHandle<xAOD::MuonContainer> m_muonHandle {
+      this, "muons", "Muons", "the muon collection to run on"};
+
+    /// \brief the preselection we apply to our input
+  private:
+    SelectionReadHandle m_preselection {
+      this, "preselection", "", "the preselection to apply"};
+
+    /// \brief the decoration for the muon isolation
+  private:
+    std::string m_isolationDecoration;
+
+    /// \brief the accessor for \ref m_isolationDecoration
+  private:
+    std::unique_ptr<ISelectionAccessor> m_isolationAccessor;
+
+    /// \brief the bits to set for an object failing the preselection
+  private:
+    SelectionType m_setOnFail;
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/MuonAnalysisAlgorithms/MuonSelectionAlg.h b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/MuonAnalysisAlgorithms/MuonSelectionAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..7537d0ce572af66642f7309355f6627db3519dd6
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/MuonAnalysisAlgorithms/MuonSelectionAlg.h
@@ -0,0 +1,78 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Tadej Novak
+
+#ifndef MUON_ANALYSIS_ALGORITHMS__MUON_SELECTION_ALG_H
+#define MUON_ANALYSIS_ALGORITHMS__MUON_SELECTION_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <MuonAnalysisInterfaces/IMuonSelectionTool.h>
+#include <SelectionHelpers/ISelectionAccessor.h>
+#include <SelectionHelpers/SelectionReadHandle.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+#include <xAODMuon/MuonContainer.h>
+
+namespace CP
+{
+  /// \brief an algorithm for calling \ref IMuonSelectionTool
+
+  class MuonSelectionAlgV2 final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    MuonSelectionAlgV2 (const std::string& name, 
+                        ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+    
+
+
+    /// \brief the selection tool
+  private:
+    ToolHandle<IMuonSelectionTool> m_selectionTool;
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the preselection we apply to our input
+  private:
+    SelectionReadHandle m_preselection {
+      this, "preselection", "", "the preselection to apply"};
+
+    /// \brief the particle continer we run on
+  private:
+    SysCopyHandle<xAOD::MuonContainer> m_muonsHandle {
+      this, "muons", "Muons", "the muons collection to run on"};
+
+    /// \brief the decoration for the quality selection
+  private:
+    std::string m_selectionDecoration;
+
+    /// \brief the accessor for \ref m_selectionDecoration
+  private:
+    std::unique_ptr<ISelectionAccessor> m_selectionAccessor;
+
+    /// \brief the decoration for the bad muon veto
+  private:
+    std::string m_badMuonVetoDecoration;
+
+    /// \brief the accessor for \ref m_selectionDecoration
+  private:
+    std::unique_ptr<ISelectionAccessor> m_badMuonVetoAccessor;
+
+    /// \brief the bits to set for an object failing the preselection
+  private:
+    SelectionType m_setOnFail;
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/MuonAnalysisAlgorithms/MuonTriggerEfficiencyScaleFactorAlg.h b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/MuonAnalysisAlgorithms/MuonTriggerEfficiencyScaleFactorAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..934ba8d5859b2c6bbda0339fddba1979e13eb5cf
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/MuonAnalysisAlgorithms/MuonTriggerEfficiencyScaleFactorAlg.h
@@ -0,0 +1,98 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Tadej Novak
+
+
+#ifndef MUON_ANALYSIS_ALGORITHMS__MUON_TRIGGER_EFFICIENCY_SCALE_FACTOR_ALG_H
+#define MUON_ANALYSIS_ALGORITHMS__MUON_TRIGGER_EFFICIENCY_SCALE_FACTOR_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <MuonAnalysisInterfaces/IMuonTriggerScaleFactors.h>
+#include <SelectionHelpers/OutOfValidityHelper.h>
+#include <SelectionHelpers/SelectionReadHandle.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <SystematicsHandles/SysDecorationHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+#include <SystematicsHandles/SysReadHandle.h>
+#include <xAODEventInfo/EventInfo.h>
+#include <xAODMuon/MuonContainer.h>
+
+namespace CP
+{
+  /// \brief an algorithm for calling \ref IMuonTriggerScaleFactors
+
+  class MuonTriggerEfficiencyScaleFactorAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    MuonTriggerEfficiencyScaleFactorAlg (const std::string& name, 
+                                         ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+    
+
+
+    /// \brief the smearing tool
+  private:
+    ToolHandle<IMuonTriggerScaleFactors> m_efficiencyScaleFactorTool;
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the muon collection we run on
+  private:
+    SysCopyHandle<xAOD::MuonContainer> m_muonHandle {
+      this, "muons", "Muons", "the muon collection to run on"};
+
+    /// \brief the EventInfo collection we use
+  private:
+    SysReadHandle<xAOD::EventInfo> m_eventInfoHandle {
+      this, "eventInfo", "EventInfo", "the EventInfo we use"};
+
+    /// \brief the preselection we apply to our input
+  private:
+    SelectionReadHandle m_preselection {
+      this, "preselection", "", "the preselection to apply"};
+
+    /// \brief the helper for OutOfValidity results
+  private:
+    OutOfValidityHelper m_outOfValidity {this};
+
+    /// \brief trigger to run efficiency for
+  private:
+    std::string m_trigger;
+    
+    /// \brief minimum run number this trigger is valid for
+  private:
+    uint32_t m_minRunNumber;
+
+    /// \brief maximum run number this trigger is valid for
+  private:
+    uint32_t m_maxRunNumber;
+
+    /// \brief the decoration for the muon scale factor
+  private:
+    SysDecorationHandle<float> m_scaleFactorDecoration {
+      this, "scaleFactorDecoration", "", "the decoration for the muon efficiency scale factor"};
+
+    /// \brief the decoration for the muon mc efficiency
+  private:
+    SysDecorationHandle<float> m_mcEfficiencyDecoration {
+      this, "mcEfficiencyDecoration", "", "the decoration for the muon MC efficiency"};
+
+    /// \brief the decoration for the muon data efficiency
+  private:
+    SysDecorationHandle<float> m_dataEfficiencyDecoration {
+      this, "dataEfficiencyDecoration", "", "the decoration for the muon data efficiency"};
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/MuonAnalysisAlgorithms/selection.xml b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/MuonAnalysisAlgorithms/selection.xml
new file mode 100644
index 0000000000000000000000000000000000000000..f86b52b89753924208abb153adbc2eadbeb83558
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/MuonAnalysisAlgorithms/selection.xml
@@ -0,0 +1,9 @@
+<lcgdict>
+
+   <class name="CP::MuonCalibrationAndSmearingAlg" />
+   <class name="CP::MuonEfficiencyScaleFactorAlg" />
+   <class name="CP::MuonIsolationAlg" />
+   <class name="CP::MuonSelectionAlgV2" />
+   <class name="CP::MuonTriggerEfficiencyScaleFactorAlg" />
+   
+</lcgdict>
diff --git a/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/Root/MuonCalibrationAndSmearingAlg.cxx b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/Root/MuonCalibrationAndSmearingAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..055a56b507eb6790e1561f7abf26e3b8304fcc19
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/Root/MuonCalibrationAndSmearingAlg.cxx
@@ -0,0 +1,65 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+//
+// includes
+//
+
+#include <MuonAnalysisAlgorithms/MuonCalibrationAndSmearingAlg.h>
+
+#include <RootCoreUtils/Assert.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  MuonCalibrationAndSmearingAlg ::
+  MuonCalibrationAndSmearingAlg (const std::string& name, 
+                     ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+    , m_calibrationAndSmearingTool ("CP::MuonCalibrationAndSmearingTool", this)
+  {
+    declareProperty ("calibrationAndSmearingTool", m_calibrationAndSmearingTool, "the calibration and smearing tool we apply");
+  }
+
+
+
+  StatusCode MuonCalibrationAndSmearingAlg ::
+  initialize ()
+  {
+    ANA_CHECK (m_calibrationAndSmearingTool.retrieve());
+    m_systematicsList.addHandle (m_muonHandle);
+    ANA_CHECK (m_systematicsList.addAffectingSystematics (m_calibrationAndSmearingTool->affectingSystematics()));
+    ANA_CHECK (m_systematicsList.initialize());
+    ANA_CHECK (m_preselection.initialize());
+    ANA_CHECK (m_outOfValidity.initialize());
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode MuonCalibrationAndSmearingAlg ::
+  execute ()
+  {
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode {
+        ANA_CHECK (m_calibrationAndSmearingTool->applySystematicVariation (sys));
+        xAOD::MuonContainer *muons = nullptr;
+        ANA_CHECK (m_muonHandle.getCopy (muons, sys));
+        for (xAOD::Muon *muon : *muons)
+        {
+          if (m_preselection.getBool (*muon))
+          {
+            ANA_CHECK_CORRECTION (m_outOfValidity, *muon, m_calibrationAndSmearingTool->applyCorrection (*muon));
+          }
+        }
+        return StatusCode::SUCCESS;
+      });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/Root/MuonEfficiencyScaleFactorAlg.cxx b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/Root/MuonEfficiencyScaleFactorAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..8a2decda46a764853a45635f9a5e34ed84cc4f93
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/Root/MuonEfficiencyScaleFactorAlg.cxx
@@ -0,0 +1,113 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+//
+// includes
+//
+
+#include <MuonAnalysisAlgorithms/MuonEfficiencyScaleFactorAlg.h>
+
+#include <RootCoreUtils/Assert.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  MuonEfficiencyScaleFactorAlg ::
+  MuonEfficiencyScaleFactorAlg (const std::string& name, 
+                     ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+    , m_efficiencyScaleFactorTool ("CP::MuonEfficiencyScaleFactors", this)
+  {
+    declareProperty ("efficiencyScaleFactorTool", m_efficiencyScaleFactorTool, "the calibration and smearing tool we apply");
+  }
+
+
+
+  StatusCode MuonEfficiencyScaleFactorAlg ::
+  initialize ()
+  {
+    if (m_scaleFactorDecoration.empty() && m_mcEfficiencyDecoration.empty() && m_dataEfficiencyDecoration.empty())
+    {
+      ANA_MSG_ERROR ("no scale factor or efficiency decoration name set");
+      return StatusCode::FAILURE;
+    }
+
+    ANA_CHECK (m_efficiencyScaleFactorTool.retrieve());
+    m_systematicsList.addHandle (m_muonHandle);
+    ANA_CHECK (m_systematicsList.addAffectingSystematics (m_efficiencyScaleFactorTool->affectingSystematics()));
+    ANA_CHECK (m_systematicsList.initialize());
+    ANA_CHECK (m_preselection.initialize());
+    ANA_CHECK (m_outOfValidity.initialize());
+
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode MuonEfficiencyScaleFactorAlg ::
+  execute ()
+  {
+    if (m_scaleFactorDecoration) {
+      ANA_CHECK (m_scaleFactorDecoration.preExecute (m_systematicsList));
+    }
+    if (m_mcEfficiencyDecoration) {
+      ANA_CHECK (m_mcEfficiencyDecoration.preExecute (m_systematicsList));
+    }
+    if (m_dataEfficiencyDecoration) {
+      ANA_CHECK (m_dataEfficiencyDecoration.preExecute (m_systematicsList));
+    }
+
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode {
+        ANA_CHECK (m_efficiencyScaleFactorTool->applySystematicVariation (sys));
+        xAOD::MuonContainer *muons = nullptr;
+        ANA_CHECK (m_muonHandle.getCopy (muons, sys));
+        const xAOD::EventInfo *eventInfo = nullptr;
+        ANA_CHECK (m_eventInfoHandle.retrieve (eventInfo, sys));
+        for (xAOD::Muon *muon : *muons)
+        {
+          if (m_preselection.getBool (*muon))
+          {
+            if (m_scaleFactorDecoration) {
+              float sf = 0;
+              ANA_CHECK_CORRECTION (m_outOfValidity, *muon, m_efficiencyScaleFactorTool->getEfficiencyScaleFactor (*muon, sf, eventInfo));
+              m_scaleFactorDecoration.set (*muon, sf, sys);
+            }
+
+            if (m_mcEfficiencyDecoration) {
+              float eff = 0;
+              ANA_CHECK_CORRECTION (m_outOfValidity, *muon, m_efficiencyScaleFactorTool->getMCEfficiency (*muon, eff, eventInfo));
+              m_mcEfficiencyDecoration.set (*muon, eff, sys);
+            }
+
+            if (m_dataEfficiencyDecoration) {
+              float eff = 0;
+              ANA_CHECK_CORRECTION (m_outOfValidity, *muon, m_efficiencyScaleFactorTool->getDataEfficiency (*muon, eff, eventInfo));
+              m_dataEfficiencyDecoration.set (*muon, eff, sys);
+            }
+          } else
+          {
+            if (m_scaleFactorDecoration) {
+              m_scaleFactorDecoration.set (*muon, invalidScaleFactor(), sys);
+            }
+
+            if (m_mcEfficiencyDecoration) {
+              m_mcEfficiencyDecoration.set (*muon, invalidEfficiency(), sys);
+            }
+
+            if (m_dataEfficiencyDecoration) {
+              m_dataEfficiencyDecoration.set (*muon, invalidEfficiency(), sys);
+            }
+          }
+        }
+        return StatusCode::SUCCESS;
+      });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/Root/MuonIsolationAlg.cxx b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/Root/MuonIsolationAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..cc4481ec6b09373543563a8945888bf094334cd5
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/Root/MuonIsolationAlg.cxx
@@ -0,0 +1,80 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+//
+// includes
+//
+
+#include <MuonAnalysisAlgorithms/MuonIsolationAlg.h>
+
+#include <RootCoreUtils/Assert.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  MuonIsolationAlg ::
+  MuonIsolationAlg (const std::string& name, 
+                     ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+    , m_isolationTool ("CP::IsolationSelectionTool", this)
+  {
+    declareProperty ("isolationTool", m_isolationTool, "the isolation tool we apply");
+    declareProperty ("isolationDecoration", m_isolationDecoration, "the decoration for the muon isolation");
+  }
+
+
+
+  StatusCode MuonIsolationAlg ::
+  initialize ()
+  {
+    if (m_isolationDecoration.empty())
+    {
+      ANA_MSG_ERROR ("no isolation decoration name set");
+      return StatusCode::FAILURE;
+    }
+    ANA_CHECK (makeSelectionAccessor (m_isolationDecoration, m_isolationAccessor));
+
+    ANA_CHECK (m_isolationTool.retrieve());
+    m_systematicsList.addHandle (m_muonHandle);
+    ANA_CHECK (m_systematicsList.initialize());
+    ANA_CHECK (m_preselection.initialize());
+
+    Root::TAccept blankAccept = m_isolationTool->getObjTAccept();
+    // Just in case this isn't initially set up as a failure clear it this one
+    // time. This only calls reset on the bitset
+    blankAccept.clear();
+    m_setOnFail = selectionFromAccept(blankAccept);
+
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode MuonIsolationAlg ::
+  execute ()
+  {
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode {
+        xAOD::MuonContainer *muons = nullptr;
+        ANA_CHECK (m_muonHandle.getCopy (muons, sys));
+        for (xAOD::Muon *muon : *muons)
+        {
+          if (m_preselection.getBool (*muon))
+          {
+            m_isolationAccessor->setBits
+              (*muon, selectionFromAccept (m_isolationTool->accept (*muon)));
+          } else {
+            m_isolationAccessor->setBits (*muon, m_setOnFail);
+          }
+        }
+        return StatusCode::SUCCESS;
+      });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/Root/MuonSelectionAlg.cxx b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/Root/MuonSelectionAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..a586bbcbb8c4df56121832e5e6bc18655d90a136
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/Root/MuonSelectionAlg.cxx
@@ -0,0 +1,96 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Tadej Novak
+
+//
+// includes
+//
+
+#include <MuonAnalysisAlgorithms/MuonSelectionAlg.h>
+#include <PATCore/IAsgSelectionTool.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  MuonSelectionAlgV2 ::
+  MuonSelectionAlgV2 (const std::string& name, 
+                      ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+    , m_selectionTool ("", this)
+  {
+    declareProperty ("selectionTool", m_selectionTool, "the selection tool we apply");
+    declareProperty ("selectionDecoration", m_selectionDecoration, "the decoration for the quality selection");
+    declareProperty ("badMuonVetoDecoration", m_badMuonVetoDecoration, "the decoration for the bad muon veto");
+  }
+
+
+
+  StatusCode MuonSelectionAlgV2 ::
+  initialize ()
+  {
+    if (m_selectionDecoration.empty())
+    {
+      ANA_MSG_ERROR ("no selection decoration name set");
+      return StatusCode::FAILURE;
+    }
+    ANA_CHECK (makeSelectionAccessor (m_selectionDecoration, m_selectionAccessor));
+    ANA_CHECK (m_selectionTool.retrieve());
+
+    if (!m_badMuonVetoDecoration.empty())
+    {
+      ANA_CHECK (makeSelectionAccessor (m_badMuonVetoDecoration, m_badMuonVetoAccessor));
+      ANA_CHECK (m_selectionTool.retrieve());
+    }
+
+    m_systematicsList.addHandle (m_muonsHandle);
+    ANA_CHECK (m_systematicsList.initialize());
+    ANA_CHECK (m_preselection.initialize());
+
+    auto *selectionTool = dynamic_cast<IAsgSelectionTool *>(&*m_selectionTool);
+    Root::TAccept blankAccept = selectionTool->getTAccept();
+    // Just in case this isn't initially set up as a failure clear it this one
+    // time. This only calls reset on the bitset
+    blankAccept.clear();
+    m_setOnFail = selectionFromAccept(blankAccept);
+
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode MuonSelectionAlgV2 ::
+  execute ()
+  {
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode
+    {
+      xAOD::MuonContainer *muons = nullptr;
+      ANA_CHECK (m_muonsHandle.getCopy (muons, sys));
+      for (xAOD::Muon *muon : *muons)
+      {
+        if (m_preselection.getBool (*muon))
+        {
+          m_selectionAccessor->setBits
+            (*muon, selectionFromAccept (m_selectionTool->accept (*muon)));
+          
+          if (m_badMuonVetoAccessor != nullptr)
+          {
+            m_badMuonVetoAccessor->setBool (*muon, m_selectionTool->isBadMuon (*muon));
+          }
+        } else {
+          m_selectionAccessor->setBits (*muon, m_setOnFail);
+
+          if (m_badMuonVetoAccessor != nullptr)
+          {
+            m_badMuonVetoAccessor->setBool (*muon, false);
+          }
+        }
+      }
+      return StatusCode::SUCCESS;
+    });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/Root/MuonTriggerEfficiencyScaleFactorAlg.cxx b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/Root/MuonTriggerEfficiencyScaleFactorAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..a5795bf12295fdb41b23474d0f15c2bda1349de4
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/Root/MuonTriggerEfficiencyScaleFactorAlg.cxx
@@ -0,0 +1,127 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Tadej Novak
+
+
+
+//
+// includes
+//
+
+#include <MuonAnalysisAlgorithms/MuonTriggerEfficiencyScaleFactorAlg.h>
+
+#include <AthContainers/ConstDataVector.h>
+#include <RootCoreUtils/Assert.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  MuonTriggerEfficiencyScaleFactorAlg ::
+  MuonTriggerEfficiencyScaleFactorAlg (const std::string& name, ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+    , m_efficiencyScaleFactorTool ("CP::MuonTriggerScaleFactors", this)
+  {
+    declareProperty ("efficiencyScaleFactorTool", m_efficiencyScaleFactorTool, "the trigger efficiency scale factor tool we apply");
+    declareProperty ("trigger", m_trigger, "trigger or trigger leg to calculate efficiency for");
+    declareProperty ("minRunNumber", m_minRunNumber = 0, "trigger or trigger leg to calculate efficiency for");
+    declareProperty ("maxRunNumber", m_maxRunNumber = 999999, "trigger or trigger leg to calculate efficiency for");
+  }
+
+
+
+  StatusCode MuonTriggerEfficiencyScaleFactorAlg ::
+  initialize ()
+  {
+    if (m_trigger.empty())
+    {
+      ANA_MSG_ERROR ("trigger name needs to be set");
+      return StatusCode::FAILURE;
+    }
+
+    if (m_scaleFactorDecoration.empty() && m_mcEfficiencyDecoration.empty() && m_dataEfficiencyDecoration.empty())
+    {
+      ANA_MSG_ERROR ("no scale factor or efficiency decoration name set");
+      return StatusCode::FAILURE;
+    }
+
+    ANA_CHECK (m_efficiencyScaleFactorTool.retrieve());
+    m_systematicsList.addHandle (m_muonHandle);
+    ANA_CHECK (m_systematicsList.addAffectingSystematics (m_efficiencyScaleFactorTool->affectingSystematics()));
+    ANA_CHECK (m_systematicsList.initialize());
+    ANA_CHECK (m_preselection.initialize());
+    ANA_CHECK (m_outOfValidity.initialize());
+
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode MuonTriggerEfficiencyScaleFactorAlg ::
+  execute ()
+  {
+    if (m_scaleFactorDecoration) {
+      ANA_CHECK (m_scaleFactorDecoration.preExecute (m_systematicsList));
+    }
+    if (m_mcEfficiencyDecoration) {
+      ANA_CHECK (m_mcEfficiencyDecoration.preExecute (m_systematicsList));
+    }
+    if (m_dataEfficiencyDecoration) {
+      ANA_CHECK (m_dataEfficiencyDecoration.preExecute (m_systematicsList));
+    }
+
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode {
+        ANA_CHECK (m_efficiencyScaleFactorTool->applySystematicVariation (sys));
+        xAOD::MuonContainer *muons = nullptr;
+        ANA_CHECK (m_muonHandle.getCopy (muons, sys));
+        const xAOD::EventInfo *eventInfo = nullptr;
+        ANA_CHECK (m_eventInfoHandle.retrieve (eventInfo, sys));
+
+        unsigned int randomRunNumber = eventInfo->auxdecor<unsigned int>("RandomRunNumber");
+        bool validEvent = m_minRunNumber <= randomRunNumber && m_maxRunNumber >= randomRunNumber;
+
+        for (xAOD::Muon *muon : *muons)
+        {
+          if (validEvent && m_preselection.getBool (*muon))
+          {
+            if (m_scaleFactorDecoration) {
+              double sf = 0;
+              ConstDataVector<xAOD::MuonContainer> singleMuonContainer(SG::VIEW_ELEMENTS);
+              singleMuonContainer.push_back(muon);
+              ANA_CHECK_CORRECTION (m_outOfValidity, *muon, m_efficiencyScaleFactorTool->getTriggerScaleFactor (*singleMuonContainer.asDataVector(), sf, m_trigger));
+              m_scaleFactorDecoration.set (*muon, sf, sys);
+            }
+
+            if (m_mcEfficiencyDecoration) {
+              double eff = 0;
+              ANA_CHECK_CORRECTION (m_outOfValidity, *muon, m_efficiencyScaleFactorTool->getTriggerEfficiency (*muon, eff, m_trigger, false));
+              m_mcEfficiencyDecoration.set (*muon, eff, sys);
+            }
+
+            if (m_dataEfficiencyDecoration) {
+              double eff = 0;
+              ANA_CHECK_CORRECTION (m_outOfValidity, *muon, m_efficiencyScaleFactorTool->getTriggerEfficiency (*muon, eff, m_trigger, true));
+              m_dataEfficiencyDecoration.set (*muon, eff, sys);
+            }
+          } else {
+            if (m_scaleFactorDecoration) {
+              m_scaleFactorDecoration.set (*muon, invalidScaleFactor(), sys);
+            }
+
+            if (m_mcEfficiencyDecoration) {
+              m_mcEfficiencyDecoration.set (*muon, invalidEfficiency(), sys);
+            }
+
+            if (m_dataEfficiencyDecoration) {
+              m_dataEfficiencyDecoration.set (*muon, invalidEfficiency(), sys);
+            }
+          }
+        }
+        return StatusCode::SUCCESS;
+      });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/python/MuonAnalysisAlgorithmsTest.py b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/python/MuonAnalysisAlgorithmsTest.py
new file mode 100644
index 0000000000000000000000000000000000000000..eee5a797da60513626a7c26d3dc54b41761abacb
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/python/MuonAnalysisAlgorithmsTest.py
@@ -0,0 +1,70 @@
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+#
+# @author Nils Krumnack
+
+from AnaAlgorithm.AlgSequence import AlgSequence
+from AnaAlgorithm.DualUseConfig import createAlgorithm
+
+def makeSequence (dataType) :
+
+    algSeq = AlgSequence()
+
+    # Set up the systematics loader/handler algorithm:
+    sysLoader = createAlgorithm( 'CP::SysListLoaderAlg', 'SysLoaderAlg' )
+    sysLoader.sigmaRecommended = 1
+    algSeq += sysLoader
+
+
+    # Include, and then set up the pileup analysis sequence:
+    from AsgAnalysisAlgorithms.PileupAnalysisSequence import \
+        makePileupAnalysisSequence
+    pileupSequence = makePileupAnalysisSequence( dataType )
+    pileupSequence.configure( inputName = 'EventInfo', outputName = 'EventInfo_%SYS%' )
+
+    # Add the pileup sequence to the job:
+    algSeq += pileupSequence
+
+    # Include, and then set up the muon analysis algorithm sequence:
+    from MuonAnalysisAlgorithms.MuonAnalysisSequence import makeMuonAnalysisSequence
+    muonSequenceMedium = makeMuonAnalysisSequence( dataType, deepCopyOutput = True, shallowViewOutput = False,
+                                                   workingPoint = 'Medium.Iso', postfix = 'medium',
+                                                   enableCutflow=True, enableKinematicHistograms=True )
+    muonSequenceMedium.configure( inputName = 'Muons',
+                                  outputName = 'AnalysisMuonsMedium_%SYS%' )
+
+    # Add the sequence to the job:
+    algSeq += muonSequenceMedium
+
+    muonSequenceTight = makeMuonAnalysisSequence( dataType, deepCopyOutput = True, shallowViewOutput = False,
+                                                  workingPoint = 'Tight.Iso', postfix = 'tight',
+                                                  enableCutflow=True, enableKinematicHistograms=True )
+    muonSequenceTight.removeStage ("calibration")
+    muonSequenceTight.configure( inputName = 'AnalysisMuonsMedium_%SYS%',
+                                 outputName = 'AnalysisMuons_%SYS%',
+                                 affectingSystematics = muonSequenceMedium.affectingSystematics())
+
+    # Add the sequence to the job:
+    algSeq += muonSequenceTight
+
+    # Add an ntuple dumper algorithm:
+    treeMaker = createAlgorithm( 'CP::TreeMakerAlg', 'TreeMaker' )
+    treeMaker.TreeName = 'muons'
+    algSeq += treeMaker
+    ntupleMaker = createAlgorithm( 'CP::AsgxAODNTupleMakerAlg', 'NTupleMakerEventInfo' )
+    ntupleMaker.TreeName = 'muons'
+    ntupleMaker.Branches = [ 'EventInfo.runNumber     -> runNumber',
+                             'EventInfo.eventNumber   -> eventNumber', ]
+    ntupleMaker.systematicsRegex = '(^$)'
+    algSeq += ntupleMaker
+    ntupleMaker = createAlgorithm( 'CP::AsgxAODNTupleMakerAlg', 'NTupleMakerMuons' )
+    ntupleMaker.TreeName = 'muons'
+    ntupleMaker.Branches = [ 'AnalysisMuons_NOSYS.eta -> mu_eta',
+                             'AnalysisMuons_NOSYS.phi -> mu_phi',
+                             'AnalysisMuons_%SYS%.pt  -> mu_%SYS%_pt', ]
+    ntupleMaker.systematicsRegex = '(^MUON_.*)'
+    algSeq += ntupleMaker
+    treeFiller = createAlgorithm( 'CP::TreeFillerAlg', 'TreeFiller' )
+    treeFiller.TreeName = 'muons'
+    algSeq += treeFiller
+
+    return algSeq
diff --git a/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/python/MuonAnalysisSequence.py b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/python/MuonAnalysisSequence.py
new file mode 100644
index 0000000000000000000000000000000000000000..2d3b893b8b27878c7c752f107ddd04914a4d036d
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/python/MuonAnalysisSequence.py
@@ -0,0 +1,224 @@
+# Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+
+# AnaAlgorithm import(s):
+from AnaAlgorithm.AnaAlgSequence import AnaAlgSequence
+from AnaAlgorithm.DualUseConfig import createAlgorithm, addPrivateTool
+import ROOT
+
+def makeMuonAnalysisSequence( dataType, workingPoint,
+                              deepCopyOutput = False,
+                              shallowViewOutput = True,
+                              postfix = '',
+                              ptSelectionOutput = False,
+                              qualitySelectionOutput = True,
+                              enableCutflow = False,
+                              enableKinematicHistograms = False ):
+    """Create a muon analysis algorithm sequence
+
+    Keyword arguments:
+      dataType -- The data type to run on ("data", "mc" or "afii")
+      workingPoint -- The working point to use
+      deepCopyOutput -- If set to 'True', the output containers will be
+                        standalone, deep copies (slower, but needed for xAOD
+                        output writing)
+      shallowViewOutput -- Create a view container if required
+      postfix -- a postfix to apply to decorations and algorithm
+                 names.  this is mostly used/needed when using this
+                 sequence with multiple working points to ensure all
+                 names are unique.
+      ptSelectionOutput -- Whether or not to apply pt selection when creating
+                           output containers.
+      qualitySelectionOutput -- Whether or not to apply muon quality selection
+                                when creating output containers.
+      enableCutflow -- Whether or not to dump the cutflow
+      enableKinematicHistograms -- Whether or not to dump the kinematic histograms
+    """
+
+    if dataType not in ["data", "mc", "afii"] :
+        raise ValueError ("invalid data type: " + dataType)
+
+    if postfix != '' :
+        postfix = '_' + postfix
+        pass
+
+    # Make sure selection options make sense
+    if deepCopyOutput and shallowViewOutput:
+        raise ValueError ("deepCopyOutput and shallowViewOutput can't both be true!")
+
+    splitWP = workingPoint.split ('.')
+    if len (splitWP) != 2 :
+        raise ValueError ('working point should be of format "quality.isolation", not ' + workingPoint)
+
+    sfWorkingPoint = splitWP[0]
+    if splitWP[0] == 'Tight' :
+        quality = ROOT.xAOD.Muon.Tight
+        pass
+    elif splitWP[0] == 'Medium' :
+        quality = ROOT.xAOD.Muon.Medium        
+        pass
+    elif splitWP[0] == 'Loose' :
+        quality = ROOT.xAOD.Muon.Loose        
+        pass
+    elif splitWP[0] == 'VeryLoose' :
+        quality = ROOT.xAOD.Muon.VeryLoose        
+        pass
+    elif splitWP[0] == 'HighPt' :
+        quality = 4
+        pass
+    elif splitWP[0] == 'LowPtEfficiency' :
+        quality = 5
+        pass
+    else :
+        raise ValueError ("invalid muon quality: \"" + splitWP[0] +
+                          "\", allowed values are Tight, Medium, Loose, " +
+                          "VeryLoose, HighPt, LowPtEfficiency")
+
+    if not splitWP[1] in ["Iso", "NonIso"] :
+        raise ValueError ('invalid muon isolation \"' + splitWP[1] +
+                          '\", allowed values are Iso, NonIso')
+
+    # Create the analysis algorithm sequence object:
+    seq = AnaAlgSequence( "MuonAnalysisSequence" + postfix )
+
+    seq.addMetaConfigDefault ("selectionDecorNames", [])
+    seq.addMetaConfigDefault ("selectionDecorNamesOutput", [])
+    seq.addMetaConfigDefault ("selectionDecorCount", [])
+
+    # Set up the eta-cut on all muons prior to everything else
+    alg = createAlgorithm( 'CP::AsgSelectionAlg',
+                           'MuonEtaCutAlg' + postfix )
+    addPrivateTool( alg, 'selectionTool', 'CP::AsgPtEtaSelectionTool' )
+    alg.selectionTool.maxEta = 2.5
+    alg.selectionDecoration = 'selectEta' + postfix + ',as_bits'
+    seq.append( alg, inputPropName = 'particles',
+                outputPropName = 'particlesOut',
+                stageName = 'selection',
+                metaConfig = {'selectionDecorNames' : [alg.selectionDecoration],
+                              'selectionDecorNamesOutput' : [alg.selectionDecoration],
+                              'selectionDecorCount' : [2]},
+                dynConfig = {'preselection' : lambda meta : "&&".join (meta["selectionDecorNames"])})
+
+    # Set up the track selection algorithm:
+    alg = createAlgorithm( 'CP::AsgLeptonTrackSelectionAlg',
+                           'MuonTrackSelectionAlg' + postfix )
+    alg.selectionDecoration = 'trackSelection' + postfix + ',as_bits'
+    alg.maxD0Significance = 3
+    alg.maxDeltaZ0SinTheta = 0.5
+    seq.append( alg, inputPropName = 'particles',
+                stageName = 'selection',
+                metaConfig = {'selectionDecorNames' : [alg.selectionDecoration],
+                              'selectionDecorNamesOutput' : [alg.selectionDecoration],
+                              'selectionDecorCount' : [3]},
+                dynConfig = {'preselection' : lambda meta : "&&".join (meta["selectionDecorNames"])})
+
+    # Set up the muon calibration and smearing algorithm:
+    alg = createAlgorithm( 'CP::MuonCalibrationAndSmearingAlg',
+                           'MuonCalibrationAndSmearingAlg' + postfix )
+    addPrivateTool( alg, 'calibrationAndSmearingTool',
+                    'CP::MuonCalibrationPeriodTool' )
+    seq.append( alg, inputPropName = 'muons', outputPropName = 'muonsOut',
+                affectingSystematics = '(^MUON_ID$)|(^MUON_MS$)|(^MUON_SAGITTA_.*)|(^MUON_SCALE$)',
+                stageName = 'calibration',
+                dynConfig = {'preselection' : lambda meta : "&&".join (meta["selectionDecorNames"])})
+
+    # Set up the the pt selection
+    alg = createAlgorithm( 'CP::AsgSelectionAlg', 'MuonPtCutAlg' + postfix )
+    alg.selectionDecoration = 'selectPt' + postfix + ',as_bits'
+    addPrivateTool( alg, 'selectionTool', 'CP::AsgPtEtaSelectionTool' )
+    alg.selectionTool.minPt = 3e3
+    seq.append( alg, inputPropName = 'particles',
+                stageName = 'selection',
+                metaConfig = {'selectionDecorNames' : [alg.selectionDecoration],
+                              'selectionDecorNamesOutput' : [alg.selectionDecoration] if ptSelectionOutput else [],
+                              'selectionDecorCount' : [2]},
+                dynConfig = {'preselection' : lambda meta : "&&".join (meta["selectionDecorNames"])})
+
+    # Setup the muon quality selection
+    alg = createAlgorithm( 'CP::MuonSelectionAlgV2',
+                           'MuonSelectionAlg' + postfix )
+    addPrivateTool( alg, 'selectionTool', 'CP::MuonSelectionTool' )
+    alg.selectionTool.MuQuality = quality
+    alg.selectionDecoration = 'good_muon' + postfix + ',as_bits'
+    alg.badMuonVetoDecoration = 'is_bad' + postfix + ',as_char'
+    seq.append( alg, inputPropName = 'muons',
+                stageName = 'selection',
+                metaConfig = {'selectionDecorNames' : [alg.selectionDecoration],
+                              'selectionDecorNamesOutput' : [alg.selectionDecoration] if qualitySelectionOutput else [],
+                              'selectionDecorCount' : [4]},
+                dynConfig = {'preselection' : lambda meta : "&&".join (meta["selectionDecorNames"])})
+
+    # Set up the isolation calculation algorithm:
+    if splitWP[1] != 'NonIso' :
+        alg = createAlgorithm( 'CP::MuonIsolationAlg',
+                               'MuonIsolationAlg' + postfix )
+        addPrivateTool( alg, 'isolationTool', 'CP::IsolationSelectionTool' )
+        alg.isolationDecoration = 'isolated_muon' + postfix + ',as_bits'
+        seq.append( alg, inputPropName = 'muons', outputPropName = 'muonsOut',
+                    stageName = 'selection',
+                    metaConfig = {'selectionDecorNames' : [alg.isolationDecoration],
+                                  'selectionDecorNamesOutput' : [alg.isolationDecoration],
+                                  'selectionDecorCount' : [1]},
+                    dynConfig = {'preselection' : lambda meta : "&&".join (meta["selectionDecorNames"])})
+        pass
+
+    # Set up an algorithm used for decorating baseline muon selection:
+    alg = createAlgorithm( 'CP::AsgSelectionAlg',
+                           'MuonSelectionSummary' + postfix )
+    addPrivateTool( alg, 'selectionTool', 'CP::AsgFlagSelectionTool' )
+    alg.selectionDecoration = 'baselineSelection' + postfix + ',as_char'
+    seq.append( alg, inputPropName = 'particles',
+                stageName = 'selection',
+                dynConfig = {'selectionTool.selectionFlags' : lambda meta : meta["selectionDecorNames"]})
+
+    # Set up an algorithm used to create muon selection cutflow:
+    if enableCutflow:
+        alg = createAlgorithm( 'CP::ObjectCutFlowHistAlg', 'MuonCutFlowDumperAlg' + postfix )
+        alg.histPattern = 'muon' + postfix + '_cflow_%SYS%'
+        seq.append( alg, inputPropName = 'input', stageName = 'selection',
+                    dynConfig = {'selection' : lambda meta : meta["selectionDecorNames"],
+                                 'selectionNCuts' : lambda meta : meta["selectionDecorCount"]} )
+
+    # Set up an algorithm that makes a view container using the selections
+    # performed previously:
+    if shallowViewOutput:
+        alg = createAlgorithm( 'CP::AsgViewFromSelectionAlg',
+                            'MuonViewFromSelectionAlg' + postfix )
+        seq.append( alg, inputPropName = 'input', outputPropName = 'output',
+                    stageName = 'selection',
+                    dynConfig = {'selection' : lambda meta : meta["selectionDecorNamesOutput"]} )
+
+    # Set up the efficiency scale factor calculation algorithm:
+    alg = createAlgorithm( 'CP::MuonEfficiencyScaleFactorAlg',
+                           'MuonEfficiencyScaleFactorAlg' + postfix )
+    addPrivateTool( alg, 'efficiencyScaleFactorTool',
+                    'CP::MuonEfficiencyScaleFactors' )
+    alg.scaleFactorDecoration = 'muon_effSF' + postfix + "_%SYS%"
+    alg.scaleFactorDecorationRegex = '(^MUON_EFF_RECO.*)'
+    alg.outOfValidity = 2 #silent
+    alg.outOfValidityDeco = 'bad_eff' + postfix
+    alg.efficiencyScaleFactorTool.WorkingPoint = sfWorkingPoint
+    if dataType != 'data':
+        seq.append( alg, inputPropName = 'muons',
+                    affectingSystematics = '(^MUON_EFF_RECO.*)',
+                    stageName = 'efficiency',
+                    dynConfig = {'preselection' : lambda meta : "&&".join (meta["selectionDecorNames"])})
+
+    # Set up an algorithm dumping the kinematic properties of the muons:
+    if enableKinematicHistograms:
+        alg = createAlgorithm( 'CP::KinematicHistAlg', 'MuonKinematicDumperAlg' + postfix )
+        alg.histPattern = 'muon' + postfix + '_%VAR%_%SYS%'
+        seq.append( alg, inputPropName = 'input', stageName = 'selection',
+                    dynConfig = {'preselection' : lambda meta : "&&".join (meta["selectionDecorNames"])})
+
+    # Set up a final deep copy making algorithm if requested:
+    if deepCopyOutput:
+        alg = createAlgorithm( 'CP::AsgViewFromSelectionAlg',
+                               'MuonDeepCopyMaker' + postfix )
+        alg.deepCopy = True
+        seq.append( alg, inputPropName = 'input', outputPropName = 'output',
+                    stageName = 'selection',
+                    dynConfig = {'selection' : lambda meta : meta["selectionDecorNamesOutput"]} )
+        pass
+
+    # Return the sequence:
+    return seq
diff --git a/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/python/__init__.py b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/python/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..5ccb446532599c01cc3844e5b1b18db06f721002
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/python/__init__.py
@@ -0,0 +1,3 @@
+# Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+
+__version__ = '1.0.0'
diff --git a/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/share/MuonAnalysisAlgorithmsTest_eljob.py b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/share/MuonAnalysisAlgorithmsTest_eljob.py
new file mode 100755
index 0000000000000000000000000000000000000000..70d19f314de8a487a024e8600523f713172360e7
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/share/MuonAnalysisAlgorithmsTest_eljob.py
@@ -0,0 +1,80 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+#
+# @author Nils Krumnack
+
+
+# Read the submission directory as a command line argument. You can
+# extend the list of arguments with your private ones later on.
+import optparse
+parser = optparse.OptionParser()
+parser.add_option( '-d', '--data-type', dest = 'data_type',
+                   action = 'store', type = 'string', default = 'data',
+                   help = 'Type of data to run over. Valid options are data, mc, afii' )
+parser.add_option( '-s', '--submission-dir', dest = 'submission_dir',
+                   action = 'store', type = 'string', default = 'submitDir',
+                   help = 'Submission directory for EventLoop' )
+parser.add_option( '-u', '--unit-test', dest='unit_test',
+                   action = 'store_true', default = False,
+                   help = 'Run the job in "unit test mode"' )
+( options, args ) = parser.parse_args()
+
+# Set up (Py)ROOT.
+import ROOT
+ROOT.xAOD.Init().ignore()
+
+# ideally we'd run over all of them, but we don't have a mechanism to
+# configure per-sample right now
+
+dataType = options.data_type
+
+if dataType not in ["data", "mc", "afii"] :
+    raise Exception ("invalid data type: " + dataType)
+
+# Set up the sample handler object. See comments from the C++ macro
+# for the details about these lines.
+import os
+sh = ROOT.SH.SampleHandler()
+sh.setMetaString( 'nc_tree', 'CollectionTree' )
+sample = ROOT.SH.SampleLocal (dataType)
+if dataType == "data" :
+    sample.add (os.getenv ('ASG_TEST_FILE_DATA'))
+    pass
+if dataType == "mc" :
+    sample.add (os.getenv ('ASG_TEST_FILE_MC'))
+    pass
+if dataType == "afii" :
+    sample.add (os.getenv ('ASG_TEST_FILE_MC_AFII'))
+    pass
+sh.add (sample)
+sh.printContent()
+
+# Create an EventLoop job.
+job = ROOT.EL.Job()
+job.sampleHandler( sh )
+job.options().setDouble( ROOT.EL.Job.optMaxEvents, 500 )
+
+
+from MuonAnalysisAlgorithms.MuonAnalysisAlgorithmsTest import makeSequence
+algSeq = makeSequence (dataType)
+print algSeq # For debugging
+for alg in algSeq:
+    job.algsAdd( alg )
+    pass
+
+# Make sure that both the ntuple and the xAOD dumper have a stream to write to.
+job.outputAdd( ROOT.EL.OutputStream( 'ANALYSIS' ) )
+
+# Find the right output directory:
+submitDir = options.submission_dir
+if options.unit_test:
+    import os
+    import tempfile
+    submitDir = tempfile.mkdtemp( prefix = 'muonTest_'+dataType+'_', dir = os.getcwd() )
+    os.rmdir( submitDir )
+    pass
+
+# Run the job using the direct driver.
+driver = ROOT.EL.DirectDriver()
+driver.submit( job, submitDir )
diff --git a/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/share/MuonAnalysisAlgorithmsTest_jobOptions.py b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/share/MuonAnalysisAlgorithmsTest_jobOptions.py
new file mode 100644
index 0000000000000000000000000000000000000000..3b627b920c1e263f34279c65bfffc9c767d61681
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/share/MuonAnalysisAlgorithmsTest_jobOptions.py
@@ -0,0 +1,66 @@
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+#
+# @author Nils Krumnack, Will Buttinger
+
+#User options, which can be set from command line after a "-" character
+#athena MuonAnalysisAlgorithmsTest_jobOptions.py - --myOption ...
+from AthenaCommon.AthArgumentParser import AthArgumentParser
+athArgsParser = AthArgumentParser()
+athArgsParser.add_argument("--data-type",action="store",dest="data_type",
+                           default="data",
+                           help="Type of data to run over. Valid options are data, mc, afii")
+athArgsParser.add_argument("--write-xaod",action="store",dest="write_xaod",
+                           default=False,
+                           help="Specify if you want xaod writing to happen (which means slower access mode for now)")
+athArgs = athArgsParser.parse_args()
+
+
+if athArgs.write_xaod:
+    #currently we must use POOLAccess mode when writing an xAOD
+    jps.AthenaCommonFlags.AccessMode = "POOLAccess"
+else:
+    #ClassAccess is much faster than POOLAccess
+    jps.AthenaCommonFlags.AccessMode = "ClassAccess"
+
+dataType = athArgs.data_type
+
+# Set up a histogram/tree output file for the job:
+jps.AthenaCommonFlags.HistOutputs = ["ANALYSIS:MuonAnalysisAlgorithmsTest." + dataType + ".hist.root"]
+svcMgr.THistSvc.MaxFileSize=-1 #make job run faster by disabling file size check
+
+#set a default file and number of events to process
+#can override with standard athena command line options (--evtMax and --filesInput)
+jps.AthenaCommonFlags.EvtMax = 500
+
+if dataType=="data":
+    testFile = os.getenv ('ASG_TEST_FILE_DATA')
+elif dataType=="mc":
+    testFile = os.getenv ('ASG_TEST_FILE_MC')
+elif dataType=="afii":
+    testFile = os.getenv ('ASG_TEST_FILE_MC_AFII')
+
+jps.AthenaCommonFlags.FilesInput = [testFile]
+
+
+
+from MuonAnalysisAlgorithms.MuonAnalysisAlgorithmsTest import makeSequence
+algSeq = makeSequence (dataType)
+print algSeq # For debugging
+
+# Add all algorithms from the sequence to the job.
+athAlgSeq += algSeq
+
+# Write a mini-xAOD if requested:
+if athArgs.write_xaod:
+    from OutputStreamAthenaPool.MultipleStreamManager import MSMgr
+    minixAOD = MSMgr.NewPoolRootStream( 'AAOD_MUON',
+                   FileName = 'MuonAnalysisAlgorithmsTest.AAOD_MUON.pool.root' )
+    minixAOD.AddItem(
+        [ 'xAOD::EventInfo#EventInfo',
+     'xAOD::EventAuxInfo#EventInfoAux.-',
+     'xAOD::MuonContainer#AnalysisMuons_NOSYS',
+     'xAOD::AuxContainerBase#AnalysisMuons_NOSYSAux.eta.phi.pt' ] )
+
+
+# Reduce the printout from Athena:
+include( "AthAnalysisBaseComps/SuppressLogging.py" )
diff --git a/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/src/components/MuonAnalysisAlgorithms_entries.cxx b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/src/components/MuonAnalysisAlgorithms_entries.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..6bc2608c046013817c6377f0ab1c700c1d26e2b6
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/src/components/MuonAnalysisAlgorithms_entries.cxx
@@ -0,0 +1,23 @@
+// AsgExampleTools_entries.cxx
+
+#include <GaudiKernel/DeclareFactoryEntries.h>
+
+#include <MuonAnalysisAlgorithms/MuonCalibrationAndSmearingAlg.h>
+#include <MuonAnalysisAlgorithms/MuonIsolationAlg.h>
+#include <MuonAnalysisAlgorithms/MuonEfficiencyScaleFactorAlg.h>
+#include <MuonAnalysisAlgorithms/MuonSelectionAlg.h>
+#include <MuonAnalysisAlgorithms/MuonTriggerEfficiencyScaleFactorAlg.h>
+
+DECLARE_ALGORITHM_FACTORY (CP::MuonCalibrationAndSmearingAlg)
+DECLARE_ALGORITHM_FACTORY (CP::MuonIsolationAlg)
+DECLARE_ALGORITHM_FACTORY (CP::MuonEfficiencyScaleFactorAlg)
+DECLARE_ALGORITHM_FACTORY (CP::MuonSelectionAlgV2)
+DECLARE_ALGORITHM_FACTORY (CP::MuonTriggerEfficiencyScaleFactorAlg)
+
+DECLARE_FACTORY_ENTRIES(MuonAnalysisAlgorithms) {
+  DECLARE_ALGORITHM (CP::MuonCalibrationAndSmearingAlg)
+  DECLARE_ALGORITHM (CP::MuonIsolationAlg)
+  DECLARE_ALGORITHM (CP::MuonEfficiencyScaleFactorAlg)
+  DECLARE_ALGORITHM (CP::MuonSelectionAlgV2)
+  DECLARE_ALGORITHM (CP::MuonTriggerEfficiencyScaleFactorAlg)
+}
diff --git a/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/src/components/MuonAnalysisAlgorithms_load.cxx b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/src/components/MuonAnalysisAlgorithms_load.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..309e5caa4afeb84c25c870d33e02edd174549f8f
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/src/components/MuonAnalysisAlgorithms_load.cxx
@@ -0,0 +1,5 @@
+// AsgExampleTools_load.cxx
+
+#include "GaudiKernel/LoadFactoryEntries.h"
+
+LOAD_FACTORY_ENTRIES(MuonAnalysisAlgorithms)
diff --git a/PhysicsAnalysis/Algorithms/SelectionHelpers/CMakeLists.txt b/PhysicsAnalysis/Algorithms/SelectionHelpers/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2b7655fb9e8cffb998a5b7966f175fa7eb8b9981
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SelectionHelpers/CMakeLists.txt
@@ -0,0 +1,36 @@
+# Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+#
+# @author Nils Krumnack
+
+
+atlas_subdir( SelectionHelpers )
+
+atlas_depends_on_subdirs(
+   PUBLIC
+   Control/AthContainers
+   Control/AthToolSupport/AsgTools
+   Event/xAOD/xAODBase
+   PhysicsAnalysis/D3PDTools/AnaAlgorithm
+   PRIVATE
+   Control/AthToolSupport/AsgTesting
+   Event/xAOD/xAODJet
+   PhysicsAnalysis/AnalysisCommon/PATCore
+   PhysicsAnalysis/AnalysisCommon/PATInterfaces )
+
+atlas_add_library( SelectionHelpersLib
+   SelectionHelpers/*.h SelectionHelpers/*.icc Root/*.cxx
+   PUBLIC_HEADERS SelectionHelpers
+   LINK_LIBRARIES AthContainers AsgTools xAODBase AnaAlgorithmLib
+   PRIVATE_LINK_LIBRARIES PATCoreLib PATInterfaces )
+
+atlas_add_dictionary( SelectionHelpersDict
+   SelectionHelpers/SelectionHelpersDict.h
+   SelectionHelpers/selection.xml
+   LINK_LIBRARIES SelectionHelpersLib )
+
+find_package( GTest )
+
+atlas_add_test( gt_ISelectionAccessor
+   SOURCES test/gt_ISelectionAccessor.cxx
+   INCLUDE_DIRS ${GTEST_INCLUDE_DIRS}
+   LINK_LIBRARIES ${GTEST_LIBRARIES} AsgTestingLib SelectionHelpersLib xAODJet )
diff --git a/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/ISelectionAccessor.cxx b/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/ISelectionAccessor.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..d3c2ecb7743795111968eef1b2b09428a45d6407
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/ISelectionAccessor.cxx
@@ -0,0 +1,140 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <SelectionHelpers/ISelectionAccessor.h>
+
+#include <AsgTools/StatusCode.h>
+#include <SelectionHelpers/SelectionAccessorBits.h>
+#include <SelectionHelpers/SelectionAccessorChar.h>
+#include <SelectionHelpers/SelectionAccessorInvert.h>
+#include <SelectionHelpers/SelectionAccessorList.h>
+#include <SelectionHelpers/SelectionAccessorNull.h>
+#include <SelectionHelpers/SelectionExprParser.h>
+#include <exception>
+#include <unordered_map>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  namespace
+  {
+    std::vector<std::string>
+    splitString (const std::string& input, const std::string& separator)
+    {
+      std::vector<std::string> result;
+      std::string::size_type start = 0, split = 0;
+      while ((split = input.find (separator, start)) != std::string::npos)
+      {
+        result.emplace_back (input.substr (start, split - start));
+        start = split + separator.size();
+      }
+      result.emplace_back (input.substr (start));
+      return result;
+    }
+
+  }
+  
+    StatusCode 
+    makeSelectionAccessor(const std::string& expr, 
+                                      std::unique_ptr<ISelectionAccessor>& accessor,
+                                      bool defaultToChar)
+    {
+      using namespace msgSelectionHelpers;
+
+      if (expr.empty())
+      {
+        accessor = std::make_unique<SelectionAccessorNull> (true);
+        return StatusCode::SUCCESS;
+      }
+
+      try {
+        SelectionExprParser parser(expr, defaultToChar);
+        ANA_CHECK(parser.build(accessor));
+      } catch (const std::exception& e) {
+        ANA_MSG_FATAL("Failure to parse expression: '" << expr << "': " << e.what());
+        return StatusCode::FAILURE;
+      }
+
+      return StatusCode::SUCCESS;
+    }
+
+
+  StatusCode
+  makeSelectionAccessorVar (const std::string& name,
+                         std::unique_ptr<ISelectionAccessor>& accessor,
+                         bool defaultToChar)
+  {
+    using namespace msgSelectionHelpers;
+
+    std::string var;
+    bool asChar = false;
+    bool asBits = false;
+    bool invert = false;
+
+    for (const std::string& option : splitString (name, ","))
+    {
+      if (var.empty())
+      {
+        // this is a bit of a hack, it will pick up the first
+        // component as the decoration name
+        var = option;
+      } else if (option == "as_char")
+      {
+        // using ",as_char" as a postfix to indicate char decorations
+        // (and ",as_bits" as a postfix to indicate bitset
+        // decorations).  I chose that suffix as it should make it
+        // easier to read in configuration files and allows for future
+        // extensions if needed.
+        asChar = true;
+      } else if (option == "as_bits")
+      {
+        asBits = true;
+      } else if (option == "invert")
+      {
+        invert = true;
+      } else
+      {
+        ANA_MSG_ERROR ("invalid option " << option << "for selection decoration");
+        return StatusCode::FAILURE;
+      }
+    }
+
+    if (asChar && asBits)
+    {
+      ANA_MSG_ERROR ("can't specify both 'as_bits' and 'as_char' for the same selection decoration, pick one!!!");
+      return StatusCode::FAILURE;
+    }
+
+    if (!asChar && !asBits)
+    {
+      if (defaultToChar)
+        asChar = true;
+      else
+        asBits = true;
+    }
+
+    if (asChar)
+      accessor = std::make_unique<SelectionAccessorChar> (var);
+    else
+      accessor = std::make_unique<SelectionAccessorBits> (var);
+
+    if (invert)
+    {
+      accessor = std::make_unique<SelectionAccessorInvert>
+        (std::move (accessor));
+    }
+
+    return StatusCode::SUCCESS;
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/OutOfValidityEventHelper.cxx b/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/OutOfValidityEventHelper.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..5eaa8d02501ee2db70daf6547c73d242fb604201
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/OutOfValidityEventHelper.cxx
@@ -0,0 +1,70 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <SelectionHelpers/OutOfValidityEventHelper.h>
+
+#include <AsgTools/MessageCheck.h>
+#include <AsgTools/StatusCode.h>
+#include <PATInterfaces/CorrectionCode.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  ::StatusCode OutOfValidityEventHelper ::
+  initialize ()
+  {
+    m_isInitialized = true;
+    return StatusCode::SUCCESS;
+  }
+
+
+  ::StatusCode OutOfValidityEventHelper ::
+  check (const CP::CorrectionCode& code,
+         const char *context) const
+  {
+    assert (m_isInitialized);
+
+    switch (code)
+    {
+    case CP::CorrectionCode::Ok:
+      return StatusCode::SUCCESS;
+    case CP::CorrectionCode::Error:
+      return StatusCode::FAILURE;
+    case CP::CorrectionCode::OutOfValidityRange:
+      switch (OutOfValidityAction (m_action))
+      {
+      case OutOfValidityAction::ABORT:
+        ANA_MSG_ERROR ("encountered OutOfValidity: " << context);
+        return StatusCode::FAILURE;
+      case OutOfValidityAction::WARNING:
+        ANA_MSG_WARNING ("encountered OutOfValidity: " << context);
+        return StatusCode::SUCCESS;
+      case OutOfValidityAction::SILENT:
+        return StatusCode::SUCCESS;
+      }
+    }
+    ANA_MSG_ERROR (__FILE__ << ":" << __LINE__ << ": invalid enum value encountered " << code << " " << int (m_action));
+    return StatusCode::FAILURE;
+  }
+
+
+
+  MsgStream& OutOfValidityEventHelper ::
+  msg (MSG::Level lvl) const
+  {
+    assert (m_msg != nullptr);
+    *m_msg << lvl;
+    return *m_msg;
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/OutOfValidityHelper.cxx b/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/OutOfValidityHelper.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..3069eb56eb09b4f6699369da3b29ef55f6a0de98
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/OutOfValidityHelper.cxx
@@ -0,0 +1,78 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <SelectionHelpers/OutOfValidityHelper.h>
+
+#include <AsgTools/MessageCheck.h>
+#include <AsgTools/StatusCode.h>
+#include <PATInterfaces/CorrectionCode.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  ::StatusCode OutOfValidityHelper ::
+  initialize ()
+  {
+    if (!m_decorationName.empty())
+    {
+      ANA_CHECK (makeSelectionAccessor (m_decorationName, m_accessor));
+    }
+      
+    m_isInitialized = true;
+    return StatusCode::SUCCESS;
+  }
+
+
+  ::StatusCode OutOfValidityHelper ::
+  check (xAOD::IParticle& particle,
+         const CP::CorrectionCode& code,
+         const char *context) const
+  {
+    assert (m_isInitialized);
+
+    switch (code)
+    {
+    case CP::CorrectionCode::Ok:
+      if (m_accessor) m_accessor->setBool (particle, true);
+      return StatusCode::SUCCESS;
+    case CP::CorrectionCode::Error:
+      return StatusCode::FAILURE;
+    case CP::CorrectionCode::OutOfValidityRange:
+      if (m_accessor) m_accessor->setBool (particle, false);
+      switch (OutOfValidityAction (m_action))
+      {
+      case OutOfValidityAction::ABORT:
+        ANA_MSG_ERROR ("encountered OutOfValidity: " << context);
+        return StatusCode::FAILURE;
+      case OutOfValidityAction::WARNING:
+        ANA_MSG_WARNING ("encountered OutOfValidity: " << context);
+        return StatusCode::SUCCESS;
+      case OutOfValidityAction::SILENT:
+        return StatusCode::SUCCESS;
+      }
+    }
+    ANA_MSG_ERROR (__FILE__ << ":" << __LINE__ << ": invalid enum value encountered " << code << " " << int (m_action));
+    return StatusCode::FAILURE;
+  }
+
+
+
+  MsgStream& OutOfValidityHelper ::
+  msg (MSG::Level lvl) const
+  {
+    assert (m_msg != nullptr);
+    *m_msg << lvl;
+    return *m_msg;
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/SelectionAccessorBits.cxx b/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/SelectionAccessorBits.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..56edd0aafa4c5c20fdf254e4fc09cfa5758abe9a
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/SelectionAccessorBits.cxx
@@ -0,0 +1,69 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <SelectionHelpers/SelectionAccessorBits.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  SelectionAccessorBits ::
+  SelectionAccessorBits (const std::string& name)
+    : m_accessor (name), m_constAccessor (name), m_label(name)
+  {}
+
+
+
+  SelectionType SelectionAccessorBits ::
+  getBits (const SG::AuxElement& element) const
+  {
+    return m_constAccessor (element);
+  }
+
+
+
+  void SelectionAccessorBits ::
+  setBits (SG::AuxElement& element,
+           SelectionType selection) const
+  {
+    m_accessor (element) = selection;
+  }
+
+
+
+  bool SelectionAccessorBits ::
+  getBool (const SG::AuxElement& element) const
+  {
+    return m_constAccessor (element) == selectionAccept();
+  }
+
+
+
+  void SelectionAccessorBits ::
+  setBool (SG::AuxElement& element,
+           bool selection) const
+  {
+    if (selection)
+      m_accessor (element) = selectionAccept();
+    else
+      m_accessor (element) = selectionAccept() ^ 0x1;
+  }
+
+
+
+  std::string SelectionAccessorBits ::
+  label () const
+  {
+    return m_label;
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/SelectionAccessorChar.cxx b/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/SelectionAccessorChar.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..c6a45b09a5199736eaebbac920b999cb2301aab1
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/SelectionAccessorChar.cxx
@@ -0,0 +1,72 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <SelectionHelpers/SelectionAccessorChar.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  SelectionAccessorChar ::
+  SelectionAccessorChar (const std::string& name)
+    : m_accessor (name), m_constAccessor (name), m_label(name)
+  {}
+
+
+
+  SelectionType SelectionAccessorChar ::
+  getBits (const SG::AuxElement& element) const
+  {
+    if (m_constAccessor (element))
+      return selectionAccept();
+    else
+      return 0;
+  }
+
+
+
+  void SelectionAccessorChar ::
+  setBits (SG::AuxElement& element,
+           SelectionType selection) const
+  {
+    if (selection == selectionAccept())
+      m_accessor (element) = 1;
+    else
+      m_accessor (element) = 0;
+  }
+
+
+
+  bool SelectionAccessorChar ::
+  getBool (const SG::AuxElement& element) const
+  {
+    return m_constAccessor (element);
+  }
+
+
+
+  void SelectionAccessorChar ::
+  setBool (SG::AuxElement& element,
+           bool selection) const
+  {
+    m_accessor (element) = selection;
+  }
+
+
+
+  std::string SelectionAccessorChar ::
+  label () const
+  {
+    return m_label;
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/SelectionAccessorExprBase.cxx b/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/SelectionAccessorExprBase.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..441396cf53c85aea2a2ec807c50de7ada269622b
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/SelectionAccessorExprBase.cxx
@@ -0,0 +1,28 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "SelectionHelpers/SelectionAccessorExprBase.h"
+
+namespace CP {
+
+SelectionType SelectionAccessorExprBase::getBits(
+    const SG::AuxElement& element) const {
+  return getBool(element) ? selectionAccept() : selectionReject();
+}
+
+void SelectionAccessorExprBase::setBool(SG::AuxElement& /*element*/,
+                                        bool /*value*/) const {
+  throw std::runtime_error(
+      "setting not supported for CP::SelectionAccessorExprBase");
+}
+
+void SelectionAccessorExprBase::setBits(SG::AuxElement& /*element*/,
+                                        SelectionType /*selection*/) const {
+  throw std::runtime_error(
+      "setting not supported for CP::SelectionAccessorExprBase");
+}
+
+}  // namespace CP
+
+
diff --git a/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/SelectionAccessorExprNot.cxx b/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/SelectionAccessorExprNot.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..2829f4f6612470c8fcd3e1daf606677659f537b6
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/SelectionAccessorExprNot.cxx
@@ -0,0 +1,21 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "SelectionHelpers/SelectionAccessorExprNot.h"
+
+namespace CP {
+
+SelectionAccessorExprNot::SelectionAccessorExprNot(
+    std::unique_ptr<ISelectionAccessor> child)
+    : m_child(std::move(child)) {}
+
+bool SelectionAccessorExprNot::getBool(const SG::AuxElement& element) const {
+  return !m_child->getBool(element);
+}
+
+std::string SelectionAccessorExprNot::label() const {
+  return "!" + m_child->label();
+}
+
+}  // namespace CP
diff --git a/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/SelectionAccessorExprOr.cxx b/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/SelectionAccessorExprOr.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..b91abe4978f0673b53cef221bf213dfe708341b5
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/SelectionAccessorExprOr.cxx
@@ -0,0 +1,23 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "SelectionHelpers/SelectionAccessorExprOr.h"
+
+namespace CP {
+
+SelectionAccessorExprOr::SelectionAccessorExprOr(
+    std::unique_ptr<ISelectionAccessor> left,
+    std::unique_ptr<ISelectionAccessor> right)
+    : m_left(std::move(left)), m_right(std::move(right)) {}
+
+bool SelectionAccessorExprOr::getBool(const SG::AuxElement& element) const {
+  return m_left->getBool(element) || m_right->getBool(element);
+}
+
+std::string SelectionAccessorExprOr::label() const {
+  return "( " + m_left->label() + " || " + m_right->label() + " )";
+}
+
+}  // namespace CP
+
diff --git a/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/SelectionAccessorInvert.cxx b/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/SelectionAccessorInvert.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..071088bbfd3f6bb693473a5bf32a39fce4c00797
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/SelectionAccessorInvert.cxx
@@ -0,0 +1,69 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <SelectionHelpers/SelectionAccessorInvert.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  SelectionAccessorInvert ::
+  SelectionAccessorInvert (std::unique_ptr<ISelectionAccessor> val_base)
+    : m_base (std::move (val_base))
+  {}
+
+
+
+  SelectionType SelectionAccessorInvert ::
+  getBits (const SG::AuxElement& element) const
+  {
+    if (m_base->getBool (element) == false)
+      return selectionAccept();
+    else
+      return 0;
+  }
+
+
+
+  void SelectionAccessorInvert ::
+  setBits (SG::AuxElement& element,
+           SelectionType selection) const
+  {
+    m_base->setBool (element, selection != selectionAccept());
+  }
+
+
+
+  bool SelectionAccessorInvert ::
+  getBool (const SG::AuxElement& element) const
+  {
+    return m_base->getBool (element) == false;
+  }
+
+
+
+  void SelectionAccessorInvert ::
+  setBool (SG::AuxElement& element,
+           bool selection) const
+  {
+    m_base->setBool (element, !selection);
+  }
+
+
+
+  std::string SelectionAccessorInvert ::
+  label () const
+  {
+    return "not " + m_base->label();
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/SelectionAccessorList.cxx b/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/SelectionAccessorList.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..3cca05d9ec56bf8ead00b6d8f97ec1b7a8dfa277
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/SelectionAccessorList.cxx
@@ -0,0 +1,106 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <SelectionHelpers/SelectionAccessorList.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  SelectionAccessorList ::
+  SelectionAccessorList (std::vector<std::unique_ptr<ISelectionAccessor> > val_list)
+    : m_list (std::move (val_list))
+  {
+    for (const std::unique_ptr<ISelectionAccessor> &acc : m_list)
+    {
+      if (!m_label.empty())
+        m_label.append(" && ");
+      
+      m_label.append(acc->label());
+    }
+
+    m_label = "( " + m_label + " )";
+  }
+
+
+
+  SelectionType SelectionAccessorList ::
+  getBits (const SG::AuxElement& element) const
+  {
+    // total number of bits in SelectionType
+    constexpr size_t SelectionTotalBits = 8 * sizeof (SelectionType);
+
+    // if we have more cuts than bits, just return true/false
+    if (m_list.size() > SelectionTotalBits)
+    {
+      if (getBool (element))
+        return selectionAccept();
+      else
+        return 0;
+    }
+
+    SelectionType result = selectionAccept();
+    for (std::size_t iter = 0; iter != m_list.size(); ++ iter)
+    {
+      if (m_list[iter]->getBool (element) == false)
+      {
+        result = result & ~(SelectionType (1) << iter);
+      }
+    }
+    return result;
+  }
+
+
+
+  void SelectionAccessorList ::
+  setBits (SG::AuxElement& /*element*/,
+           SelectionType /*selection*/) const
+  {
+    // technically we could support setting by setting all the
+    // components, but I can't think of a situation in which that
+    // would be a good idea
+    throw std::runtime_error ("setting not supported for CP::SelectionAccessorList");
+  }
+
+
+
+  bool SelectionAccessorList ::
+  getBool (const SG::AuxElement& element) const
+  {
+    for (auto& accessor : m_list)
+    {
+      if (!accessor->getBool (element))
+        return false;
+    }
+    return true;
+  }
+
+
+
+  void SelectionAccessorList ::
+  setBool (SG::AuxElement& /*element*/,
+           bool /*selection*/) const
+  {
+    // technically we could support setting by setting all the
+    // components, but I can't think of a situation in which that
+    // would be a good idea
+    throw std::runtime_error ("setting not supported for CP::SelectionAccessorList");
+  }
+
+
+  std::string SelectionAccessorList ::
+  label () const
+  {
+    return m_label;
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/SelectionAccessorNull.cxx b/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/SelectionAccessorNull.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..642d474b0ff94bbf9878643680d8591fdc882dc1
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/SelectionAccessorNull.cxx
@@ -0,0 +1,70 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <SelectionHelpers/SelectionAccessorNull.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  SelectionAccessorNull ::
+  SelectionAccessorNull (bool value) : m_value(value)
+  {}
+
+
+
+  SelectionType SelectionAccessorNull ::
+  getBits (const SG::AuxElement& /*element*/) const
+  {
+    return m_value ? selectionAccept() : selectionReject();
+  }
+
+
+
+  void SelectionAccessorNull ::
+  setBits (SG::AuxElement& /*element*/,
+           SelectionType /*selection*/) const
+  {
+    // ok, let's not do anything here, making this an easy way to
+    // ignore selection decorations an algorithm calculates.  however,
+    // the implication is that you can simply forget to set the
+    // decoration property and not get an error
+  }
+
+
+
+  bool SelectionAccessorNull ::
+  getBool (const SG::AuxElement& /*element*/) const
+  {
+    return m_value;
+  }
+
+
+
+  void SelectionAccessorNull ::
+  setBool (SG::AuxElement& /*element*/,
+           bool /*selection*/) const
+  {
+    // ok, let's not do anything here, making this an easy way to
+    // ignore selection decorations an algorithm calculates.  however,
+    // the implication is that you can simply forget to set the
+    // decoration property and not get an error
+  }
+
+
+  std::string SelectionAccessorNull ::
+  label () const
+  {
+    return m_value ? "true" : "false";
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/SelectionExprParser.cxx b/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/SelectionExprParser.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..c15925fae605b70880c247e1c3d124fd8ef040d2
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/SelectionExprParser.cxx
@@ -0,0 +1,171 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "SelectionHelpers/SelectionExprParser.h"
+#include "SelectionHelpers/SelectionAccessorExprNot.h"
+#include "SelectionHelpers/SelectionAccessorExprOr.h"
+#include "SelectionHelpers/SelectionAccessorList.h"
+#include "SelectionHelpers/SelectionAccessorNull.h"
+
+#include <iostream>
+#include <regex>
+#include <string>
+
+using CP::DetailSelectionExprParser::Lexer;
+
+namespace CP {
+using namespace msgSelectionHelpers;
+
+namespace DetailSelectionExprParser {
+bool Separator::operator()(std::string::const_iterator& next,
+                           const std::string::const_iterator& end,
+                           std::string& tok) const {
+  if (next == end) {
+    return false;
+  }
+
+  static const std::regex base_regex(
+      "^( ?((?:[^|&()! ]+)|(?:&&)|(?:!)|(?:\\()|(?:\\))|(?:\\|\\|))).*");
+  std::smatch m;
+
+  if (std::regex_match(next, end, m, base_regex)) {
+    tok = m[2].str();
+    next += m[1].length();
+  } else {
+    std::smatch m2;
+    static const std::regex space_re{"^ +$"};
+    if (std::regex_match(next, end, m, space_re)) {
+      // only spaces left, we're done
+      return false;
+    }
+    throw std::runtime_error("Cannot tokenize: '" + std::string(next, end) +
+                             "'");
+  }
+  return true;
+}
+
+Lexer::Lexer(const std::string& s) : m_string(s), m_tokenizer(s, {}) {
+  m_iterator = m_tokenizer.begin();
+}
+
+auto Lexer::nextSymbol() -> Symbol {
+  if (m_iterator == m_tokenizer.end()) {
+    return Symbol{END, ""};
+  }
+  std::string t = *m_iterator;
+  Type type;
+  if (t == "&&")
+    type = AND;
+  else if (t == "||")
+    type = OR;
+  else if (t == "(")
+    type = LEFT;
+  else if (t == ")")
+    type = RIGHT;
+  else if (t == "!")
+    type = NOT;
+  else if (t == "true")
+    type = TRUE_LITERAL;
+  else if (t == "false")
+    type = FALSE_LITERAL;
+  else {
+    // check if variable is valid
+    const std::regex base_regex("^([^|()&! ]*)$");
+    std::smatch base_match;
+
+    if (!std::regex_match(t, base_match, base_regex)) {
+      throw std::runtime_error("illegal variable encountered");
+    } else {
+      type = VAR;
+    }
+  }
+
+  ++m_iterator;
+  return Symbol{type, t};
+}
+
+}  // namespace DetailSelectionExprParser
+
+SelectionExprParser::SelectionExprParser(Lexer lexer, bool defaultToChar)
+    : m_lexer(std::move(lexer)), m_defaultToChar(defaultToChar) {}
+
+StatusCode SelectionExprParser::build(
+    std::unique_ptr<ISelectionAccessor>& accessor) {
+  std::unique_ptr<ISelectionAccessor> root{nullptr};
+  ANA_CHECK(expression(root));
+
+  if (m_symbol.type != Lexer::END) {
+    throw std::runtime_error(
+        "Not all symbols in expression were consumed. Check your expression.");
+  }
+
+  accessor = std::move(root);
+  return StatusCode::SUCCESS;
+}
+
+StatusCode SelectionExprParser::expression(
+    std::unique_ptr<ISelectionAccessor>& root) {
+  ANA_CHECK(term(root));
+  while (m_symbol.type == Lexer::OR) {
+    std::unique_ptr<ISelectionAccessor> left = std::move(root);
+    ANA_CHECK(term(root));
+    std::unique_ptr<ISelectionAccessor> right = std::move(root);
+    root = std::make_unique<SelectionAccessorExprOr>(std::move(left),
+                                                     std::move(right));
+  }
+  return StatusCode::SUCCESS;
+}
+
+StatusCode SelectionExprParser::term(
+    std::unique_ptr<ISelectionAccessor>& root) {
+  ANA_CHECK(factor(root));
+  std::vector<std::unique_ptr<ISelectionAccessor>> factors;
+  factors.push_back(std::move(root));
+
+  while (m_symbol.type == Lexer::AND) {
+    ANA_CHECK(factor(root));
+    factors.push_back(std::move(root));
+  }
+
+  if (factors.size() == 1) {
+    root = std::move(factors[0]);
+  } else {
+    root = std::make_unique<SelectionAccessorList>(std::move(factors));
+  }
+  return StatusCode::SUCCESS;
+}
+
+StatusCode SelectionExprParser::factor(
+    std::unique_ptr<ISelectionAccessor>& root) {
+  m_symbol = m_lexer.nextSymbol();
+  if (m_symbol.type == Lexer::TRUE_LITERAL) {
+    root = std::make_unique<SelectionAccessorNull>(true);
+    m_symbol = m_lexer.nextSymbol();
+  } else if (m_symbol.type == Lexer::FALSE_LITERAL) {
+    root = std::make_unique<SelectionAccessorNull>(false);
+    m_symbol = m_lexer.nextSymbol();
+  } else if (m_symbol.type == Lexer::NOT) {
+    ANA_CHECK(factor(root));
+    std::unique_ptr<ISelectionAccessor> notEx =
+        std::make_unique<SelectionAccessorExprNot>(std::move(root));
+    root = std::move(notEx);
+  } else if (m_symbol.type == Lexer::LEFT) {
+    ANA_CHECK(expression(root));
+    if (m_symbol.type != Lexer::RIGHT) {
+      throw std::runtime_error(
+          "Missing closing bracket, check your expression.");
+    }
+    m_symbol = m_lexer.nextSymbol();
+
+  } else if (m_symbol.type == Lexer::VAR) {
+    ANA_CHECK(makeSelectionAccessorVar(m_symbol.value, root, m_defaultToChar));
+    m_symbol = m_lexer.nextSymbol();
+  } else {
+    throw std::runtime_error("Malformed expression.");
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+}  // namespace CP
diff --git a/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/SelectionHelpers.cxx b/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/SelectionHelpers.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..bba7731d9650759ad487e063eaa22b53564dd17b
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/SelectionHelpers.cxx
@@ -0,0 +1,40 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <SelectionHelpers/SelectionHelpers.h>
+
+#include <PATCore/TAccept.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  SelectionType selectionFromBool (bool accept)
+  {
+    if (accept)
+      return selectionAccept();
+    else
+      return selectionReject();
+  }
+
+
+
+  SelectionType selectionFromAccept (const Root::TAccept& accept)
+  {
+    return ~SelectionType (accept.getCutResultInvertedBitSet().to_ulong());
+  }
+
+
+
+  ANA_MSG_SOURCE (msgSelectionHelpers, "selectionHelpers")
+}
diff --git a/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/SelectionReadHandle.cxx b/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/SelectionReadHandle.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..80a95355b81966379d46615809db102d2ab4b282
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SelectionHelpers/Root/SelectionReadHandle.cxx
@@ -0,0 +1,64 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <SelectionHelpers/SelectionReadHandle.h>
+
+#include <AsgTools/MessageCheck.h>
+#include <AsgTools/StatusCode.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  MsgStream& SelectionReadHandle ::
+  msg () const
+  {
+    assert (m_msg != nullptr);
+    return *m_msg;
+  }
+
+
+
+  MsgStream& SelectionReadHandle ::
+  msg (MSG::Level lvl) const
+  {
+    assert (m_msg != nullptr);
+    *m_msg << lvl;
+    return *m_msg;
+  }
+
+
+
+  bool SelectionReadHandle ::
+  empty () const noexcept
+  {
+    return m_selection.empty();
+  }
+
+
+
+  SelectionReadHandle ::
+  operator bool () const noexcept
+  {
+    return !m_selection.empty();
+  }
+
+
+
+  StatusCode SelectionReadHandle ::
+  initialize ()
+  {
+    ANA_CHECK (makeSelectionAccessor (m_selection, m_accessor));
+    return StatusCode::SUCCESS;
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/ISelectionAccessor.h b/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/ISelectionAccessor.h
new file mode 100644
index 0000000000000000000000000000000000000000..a41e66be72f021702a3813f757198e23bea16abd
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/ISelectionAccessor.h
@@ -0,0 +1,100 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef SELECTION_HELPERS__I_SELECTION_ACCESSOR_H
+#define SELECTION_HELPERS__I_SELECTION_ACCESSOR_H
+
+#include <AthContainers/AuxElement.h>
+#include <SelectionHelpers/SelectionHelpers.h>
+#include <memory>
+
+class StatusCode;
+
+namespace CP
+{
+  /// \brief a specialized accessor to read/write a selection
+  /// decoration from/to an xAOD object
+  ///
+  /// We have (at least) two conventions on how selection decorations
+  /// can look like, the one from the CP algorithms/TAccept and the
+  /// other from the derivation framework/overlap removal tool.  This
+  /// accessor is a base class with specific implementations for
+  /// different conventions.
+  ///
+  /// The actual interface has separate accessor functions for
+  /// accessing as bitsets or as booleans.  In part that is to avoid a
+  /// lot of unnecessary back and forth conversions, but mostly it is
+  /// to give us some more flexibility if we need to extend this in
+  /// the future.
+
+  class ISelectionAccessor
+  {
+    // no slicing
+    ISelectionAccessor& operator = (const ISelectionAccessor&) = delete;
+
+    /// \brief standard (virtual) destructor
+  public:
+    virtual ~ISelectionAccessor () noexcept = default;
+
+    /// \brief get the selection decoration
+  public:
+    virtual SelectionType
+    getBits (const SG::AuxElement& element) const = 0;
+
+    /// \brief set the selection decoration
+  public:
+    virtual void setBits (SG::AuxElement& element,
+                          SelectionType selection) const = 0;
+
+    /// \brief get the selection decoration
+  public:
+    virtual bool getBool (const SG::AuxElement& element) const = 0;
+
+    /// \brief set the selection decoration
+  public:
+    virtual void setBool (SG::AuxElement& element,
+                          bool selection) const = 0;
+
+  /// \brief get the label of the accessor
+  public:
+    virtual std::string label () const = 0;
+  };
+
+
+  /// \brief make the \ref ISelectionAccessor for the given name
+  ///
+  /// This will invoke @c SelectionExprParser to build a @c ISelectionAccessor
+  /// that corresponds to a given expression.
+  /// @note Passing in a simple selection name will createa plain selection accessor that
+  ///       only reads that single name.
+  /// @note Passing in an expression which only contains names and && will produce an
+  ///       instance of @c SelectionAccessorList, which has special semantics and allows
+  ///       retrievel of AND'ed accessors via a bit sequence.
+  /// @note Allowed operators are &&, || and !, grouping with () is possible.
+  ///
+  /// @param [in] expr The expression to parse.
+  /// @param [out] accessor The created accessor will be owned by this unique pointer.
+  /// @param [in] defaultToChar Whether to treat decorations as char by default
+  StatusCode
+  makeSelectionAccessor (const std::string& expr,
+                         std::unique_ptr<ISelectionAccessor>& accessor,
+                         bool defaultToChar = false);
+
+  /// @brief Produces a simple @c ISelectionAccessor accessing the given decoration
+  /// @note Through annotations like ",as_char" or ",as_bits" at the end of a name,
+  ///       different decoding strategies can be selected. These need to match the
+  ///       decoration type.
+  /// @param [in] name The name of the decoration to create an accessor for.
+  /// @param [out] accessor The created accessor will be owned by this unique pointer.
+  /// @param [in] defaultToChar Whether to treat decorations as char by default
+  StatusCode
+  makeSelectionAccessorVar (const std::string& name,
+                         std::unique_ptr<ISelectionAccessor>& accessor,
+                         bool defaultToChar = false);
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/OutOfValidityEventHelper.h b/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/OutOfValidityEventHelper.h
new file mode 100644
index 0000000000000000000000000000000000000000..4fcc1f49c8fc29fadeba584edfe4edb9ef19f7ac
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/OutOfValidityEventHelper.h
@@ -0,0 +1,91 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef SELECTION_HELPERS__OUT_OF_VALIDITY_EVENT_HELPER_H
+#define SELECTION_HELPERS__OUT_OF_VALIDITY_EVENT_HELPER_H
+
+#include <AsgTools/MsgStream.h>
+#include <AthContainers/AuxElement.h>
+#include <SelectionHelpers/ISelectionAccessor.h>
+#include <SelectionHelpers/OutOfValidityHelper.h>
+#include <xAODBase/IParticle.h>
+#include <memory>
+
+class StatusCode;
+
+namespace CP
+{
+  class CorrectionCode;
+
+
+
+  /// \brief a helper to translate a \ref CP::CorrectionCode into a
+  /// \ref ::StatusCode
+  ///
+  /// The prolem is OutOfValidityRange which does not have an
+  /// equivalent in StatusCode and which does not have a unique,
+  /// correct handling in all situations.  This helper allows to
+  /// configure a variety of behaviors via properties.
+
+  class OutOfValidityEventHelper final
+  {
+    /// \brief standard constructor
+  public:
+    template<typename T>
+    OutOfValidityEventHelper (T *owner, const std::string& propertyName = "outOfValidity",
+                         const std::string& propertyDescription = "how to handle out of validity results");
+
+
+    /// \brief standard initialize
+  public:
+    ::StatusCode initialize ();
+
+    /// \brief check the correction code and do the proper thing
+  public:
+    ::StatusCode check (const CP::CorrectionCode& code,
+                        const char *context) const;
+
+
+    /// \brief the action to take
+  private:
+    unsigned m_action {unsigned (OutOfValidityAction::ABORT)};
+
+    /// \brief the message stream we use
+  private:
+    MsgStream *m_msg {nullptr};
+
+    /// \brief whether we have been initialized
+    ///
+    /// This is only used in debug mode to indicate a programming
+    /// fault.  Otherwise it is too easy for users to forget to
+    /// initialize this object.
+  private:
+    bool m_isInitialized = false;
+
+    /// \brief helper for message macros
+  private:
+    MsgStream& msg( const MSG::Level lvl ) const;
+  };
+
+
+
+  template<typename T> OutOfValidityEventHelper ::
+  OutOfValidityEventHelper (T *owner, const std::string& propertyName,
+                       const std::string& propertyDescription)
+    : m_msg (&owner->msg())
+  {
+    owner->declareProperty (propertyName, m_action,
+                            propertyDescription);
+  }
+}
+
+/// \brief a helper check macro to work with \ref OutOfValidityEventHelper
+#define ANA_CHECK_CORRECTION_EVENT(helper,expr)       \
+  { if ((helper).check ((expr), #expr).isFailure())   \
+      return StatusCode::FAILURE; }
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/OutOfValidityHelper.h b/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/OutOfValidityHelper.h
new file mode 100644
index 0000000000000000000000000000000000000000..97787061e61164fa61c67e6f54e9b92712b00645
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/OutOfValidityHelper.h
@@ -0,0 +1,138 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef SELECTION_HELPERS__OUT_OF_VALIDITY_HELPER_H
+#define SELECTION_HELPERS__OUT_OF_VALIDITY_HELPER_H
+
+#include <AsgTools/MsgStream.h>
+#include <AthContainers/AuxElement.h>
+#include <SelectionHelpers/ISelectionAccessor.h>
+#include <xAODBase/IParticle.h>
+#include <memory>
+
+class StatusCode;
+
+namespace CP
+{
+  class CorrectionCode;
+
+
+  /// \brief the action to perform on encountering an
+  /// OutOfValidityRange in \ref OutOfValidityHelper
+  ///
+  /// This is in addition to possibly setting a selection decoration
+  /// (if that has been configured.
+  ///
+  /// This is not a member enum of \ref OutOfValidityHelper to void
+  /// problems with member enums in dictionary generation, etc.
+
+  enum class OutOfValidityAction
+  {
+    /// \brief print an error message and return a failure status code
+    /// (triggering an abort)
+    ///
+    /// This is currently the default, as it forces users to think
+    /// about what they want to do with OutOfValidityRange results.
+    /// That is fairly safe, as most tools don't actually report
+    /// OutOfValidityRange and we don't want to add a lot of
+    /// meaningless selection decorations to each object.
+    ABORT,
+
+    /// \brief print a warning message and return a success status
+    /// code.
+    ///
+    /// This should only be used if OutOfValidity is a *rare* event,
+    /// or otherwise it will completely clobber the log file.
+    WARNING,
+
+    /// \brief don't print anything and return success
+    ///
+    /// This should (normally) be combined with a selection decoration
+    /// that records OutOfValidity results, allowing to retrieve that
+    /// information subsequently.
+    SILENT
+  };
+
+
+
+  /// \brief a helper to translate a \ref CP::CorrectionCode into a
+  /// \ref ::StatusCode
+  ///
+  /// The prolem is OutOfValidityRange which does not have an
+  /// equivalent in StatusCode and which does not have a unique,
+  /// correct handling in all situations.  This helper allows to
+  /// configure a variety of behaviors via properties.
+
+  class OutOfValidityHelper final
+  {
+    /// \brief standard constructor
+  public:
+    template<typename T>
+    OutOfValidityHelper (T *owner, const std::string& propertyName = "outOfValidity",
+                         const std::string& propertyDescription = "how to handle out of validity results");
+
+
+    /// \brief standard initialize
+  public:
+    ::StatusCode initialize ();
+
+    /// \brief check the correction code and do the proper thing
+  public:
+    ::StatusCode check (xAOD::IParticle& particle,
+                        const CP::CorrectionCode& code,
+                        const char *context) const;
+
+
+    /// \brief the action to take
+  private:
+    unsigned m_action {unsigned (OutOfValidityAction::ABORT)};
+
+    /// \brief the message stream we use
+  private:
+    MsgStream *m_msg {nullptr};
+
+    /// \brief the accessor if we apply one
+  private:
+    std::unique_ptr<ISelectionAccessor> m_accessor;
+
+    /// \brief the decoration name we use (if we have one)
+  private:
+    std::string m_decorationName;
+
+    /// \brief whether we have been initialized
+    ///
+    /// This is only used in debug mode to indicate a programming
+    /// fault.  Otherwise it is too easy for users to forget to
+    /// initialize this object.
+  private:
+    bool m_isInitialized = false;
+
+    /// \brief helper for message macros
+  private:
+    MsgStream& msg( const MSG::Level lvl ) const;
+  };
+
+
+
+  template<typename T> OutOfValidityHelper ::
+  OutOfValidityHelper (T *owner, const std::string& propertyName,
+                       const std::string& propertyDescription)
+    : m_msg (&owner->msg())
+  {
+    owner->declareProperty (propertyName, m_action,
+                            propertyDescription);
+    owner->declareProperty (propertyName + "Deco", m_decorationName,
+                            "decoration to set alongside action described by " + propertyName);
+  }
+}
+
+/// \brief a helper check macro to work with \ref OutOfValidityHelper
+#define ANA_CHECK_CORRECTION(helper,object,expr)                \
+  { if ((helper).check ((object), (expr), #expr).isFailure())   \
+      return StatusCode::FAILURE; }
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/SelectionAccessorBits.h b/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/SelectionAccessorBits.h
new file mode 100644
index 0000000000000000000000000000000000000000..5c5d1df875225a8a4864c9fdf98e04d961d96d3d
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/SelectionAccessorBits.h
@@ -0,0 +1,65 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef SELECTION_HELPERS__SELECTION_ACCESSOR_BITS_H
+#define SELECTION_HELPERS__SELECTION_ACCESSOR_BITS_H
+
+#include <SelectionHelpers/ISelectionAccessor.h>
+
+namespace CP
+{
+  /// \brief the \ref SelectionAccesor for standard CP algorithm
+  /// selection decorations encoded as bits
+
+  class SelectionAccessorBits final : public ISelectionAccessor
+  {
+    //
+    // public interface
+    //
+
+  public:
+    SelectionAccessorBits (const std::string& name);
+
+  public:
+    virtual SelectionType
+    getBits (const SG::AuxElement& element) const override;
+
+  public:
+    virtual void setBits (SG::AuxElement& element,
+                          SelectionType selection) const override;
+
+  public:
+    virtual bool
+    getBool (const SG::AuxElement& element) const override;
+
+  public:
+    virtual void setBool (SG::AuxElement& element,
+                          bool selection) const override;
+
+  public:
+    virtual std::string label () const override;
+
+
+    //
+    // private interface
+    //
+
+    /// \brief the underlying accessor
+  private:
+    SG::AuxElement::Accessor<SelectionType> m_accessor;
+
+    /// \brief the underlying accessor
+  private:
+    SG::AuxElement::ConstAccessor<SelectionType> m_constAccessor;
+
+    /// \brief the label of the accessor
+  private:
+    std::string m_label;
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/SelectionAccessorChar.h b/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/SelectionAccessorChar.h
new file mode 100644
index 0000000000000000000000000000000000000000..8d9fe03c329b4c6224df8acdf93527213ea56945
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/SelectionAccessorChar.h
@@ -0,0 +1,65 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef SELECTION_HELPERS__SELECTION_ACCESSOR_OR_H
+#define SELECTION_HELPERS__SELECTION_ACCESSOR_OR_H
+
+#include <SelectionHelpers/ISelectionAccessor.h>
+
+namespace CP
+{
+  /// \brief the \ref SelectionAccesor for OR tool selection
+  /// decorations
+
+  class SelectionAccessorChar final : public ISelectionAccessor
+  {
+    //
+    // public interface
+    //
+
+  public:
+    SelectionAccessorChar (const std::string& name);
+
+  public:
+    virtual SelectionType
+    getBits (const SG::AuxElement& element) const override;
+
+  public:
+    virtual void setBits (SG::AuxElement& element,
+                          SelectionType selection) const override;
+
+  public:
+    virtual bool
+    getBool (const SG::AuxElement& element) const override;
+
+  public:
+    virtual void setBool (SG::AuxElement& element,
+                          bool selection) const override;
+
+  public:
+    virtual std::string label () const override;
+
+
+    //
+    // private interface
+    //
+
+    /// \brief th underlying accessor
+  private:
+    SG::AuxElement::Accessor<char> m_accessor;
+
+    /// \brief th underlying accessor
+  private:
+    SG::AuxElement::ConstAccessor<char> m_constAccessor;
+
+    /// \brief the label of the accessor
+  private:
+    std::string m_label;
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/SelectionAccessorExprBase.h b/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/SelectionAccessorExprBase.h
new file mode 100644
index 0000000000000000000000000000000000000000..46cfcc31f479fc077bda44c0cf1dcf6470be0b13
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/SelectionAccessorExprBase.h
@@ -0,0 +1,29 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef SELECTION_HELPERS__SELECTION_ACCESSOR_EXPR_BASE_H
+#define SELECTION_HELPERS__SELECTION_ACCESSOR_EXPR_BASE_H
+
+#include "SelectionHelpers/ISelectionAccessor.h"
+
+namespace CP {
+
+/// @brief Serves as the base for a few logical expression classes.
+class SelectionAccessorExprBase : public ISelectionAccessor {
+  // leave getBool pure virtual
+  // leave label pure virtual
+
+ public:
+  virtual SelectionType getBits(const SG::AuxElement &element) const override;
+
+  virtual void setBool(SG::AuxElement & /*element*/,
+                       bool /*value*/) const override;
+
+  virtual void setBits(SG::AuxElement & /*element*/,
+                       SelectionType /*selection*/) const override;
+};
+
+}  // namespace CP
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/SelectionAccessorExprNot.h b/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/SelectionAccessorExprNot.h
new file mode 100644
index 0000000000000000000000000000000000000000..b7464e16c6af53cddb6624daf119b101c0343884
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/SelectionAccessorExprNot.h
@@ -0,0 +1,34 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef SELECTION_HELPERS__SELECTION_ACCESSOR_EXPR_AND_H
+#define SELECTION_HELPERS__SELECTION_ACCESSOR_EXPR_AND_H
+
+#include "SelectionHelpers/SelectionAccessorExprBase.h"
+
+#include <memory>
+
+namespace CP {
+
+/// @brief SelectionAccessor which implements a logical NOT
+class SelectionAccessorExprNot : public SelectionAccessorExprBase {
+ public:
+  /// @param child The selection accessor that is to be negated
+  SelectionAccessorExprNot(std::unique_ptr<ISelectionAccessor> child);
+
+  /// @param element AuxElement to evaluate the selection on
+  /// @return Result of the negated result of @c child
+  virtual bool getBool(const SG::AuxElement &element) const override;
+
+  /// @return Returns a readable and parseable representation
+  virtual std::string label() const override;
+
+ private:
+  std::unique_ptr<ISelectionAccessor> m_child;
+};
+
+
+}  // namespace CP
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/SelectionAccessorExprOr.h b/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/SelectionAccessorExprOr.h
new file mode 100644
index 0000000000000000000000000000000000000000..072ff29819f0fa40330ced18a483065f0d6a16cb
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/SelectionAccessorExprOr.h
@@ -0,0 +1,36 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef SELECTION_HELPERS__SELECTION_ACCESSOR_EXPR_OR_H
+#define SELECTION_HELPERS__SELECTION_ACCESSOR_EXPR_OR_H
+
+#include "SelectionHelpers/SelectionAccessorExprBase.h"
+
+#include <memory>
+
+namespace CP {
+
+/// @brief Implements a SelectionAccessor that performs a binary logical OR
+class SelectionAccessorExprOr : public SelectionAccessorExprBase {
+ public:
+   /// @param left Left argument to the OR expression
+   /// @param Right Right argument to the OR expression
+  SelectionAccessorExprOr(std::unique_ptr<ISelectionAccessor> left,
+                          std::unique_ptr<ISelectionAccessor> right);
+
+  /// @param element AuxElement to evaluate the selection on
+  /// @return The result of the expression `left OR right`
+  virtual bool getBool(const SG::AuxElement &element) const override;
+
+  /// @return Returns a readable and parseable representation
+  virtual std::string label() const override;
+
+ private:
+  std::unique_ptr<ISelectionAccessor> m_left;
+  std::unique_ptr<ISelectionAccessor> m_right;
+};
+
+}  // namespace CP
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/SelectionAccessorInvert.h b/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/SelectionAccessorInvert.h
new file mode 100644
index 0000000000000000000000000000000000000000..abb9c2a09aaf5dce476f58379ce8ff82e34e3874
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/SelectionAccessorInvert.h
@@ -0,0 +1,59 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef SELECTION_HELPERS__SELECTION_ACCESSOR_INVERT_H
+#define SELECTION_HELPERS__SELECTION_ACCESSOR_INVERT_H
+
+#include <SelectionHelpers/ISelectionAccessor.h>
+
+#include <memory>
+
+namespace CP
+{
+  /// \brief the \ref SelectionAccesor for inverting a selection
+  /// decoration
+
+  class SelectionAccessorInvert final : public ISelectionAccessor
+  {
+    //
+    // public interface
+    //
+
+  public:
+    SelectionAccessorInvert (std::unique_ptr<ISelectionAccessor> val_base);
+
+  public:
+    virtual SelectionType
+    getBits (const SG::AuxElement& element) const override;
+
+  public:
+    virtual void setBits (SG::AuxElement& element,
+                          SelectionType selection) const override;
+
+  public:
+    virtual bool
+    getBool (const SG::AuxElement& element) const override;
+
+  public:
+    virtual void setBool (SG::AuxElement& element,
+                          bool selection) const override;
+
+  public:
+    virtual std::string label () const override;
+
+
+    //
+    // private interface
+    //
+
+    /// \brief the base selection accessors I invert
+  private:
+    std::unique_ptr<ISelectionAccessor> m_base;
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/SelectionAccessorList.h b/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/SelectionAccessorList.h
new file mode 100644
index 0000000000000000000000000000000000000000..900fa820136a9b1dd61ce32a6dfc2c39e0b11fda
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/SelectionAccessorList.h
@@ -0,0 +1,64 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef SELECTION_HELPERS__SELECTION_ACCESSOR_LIST_H
+#define SELECTION_HELPERS__SELECTION_ACCESSOR_LIST_H
+
+#include <SelectionHelpers/ISelectionAccessor.h>
+
+#include <memory>
+#include <vector>
+
+namespace CP
+{
+  /// \brief the \ref SelectionAccesor for list of selection
+  /// decorations
+
+  class SelectionAccessorList final : public ISelectionAccessor
+  {
+    //
+    // public interface
+    //
+
+  public:
+    SelectionAccessorList (std::vector<std::unique_ptr<ISelectionAccessor> > val_list);
+
+  public:
+    virtual SelectionType
+    getBits (const SG::AuxElement& element) const override;
+
+  public:
+    virtual void setBits (SG::AuxElement& element,
+                          SelectionType selection) const override;
+
+  public:
+    virtual bool
+    getBool (const SG::AuxElement& element) const override;
+
+  public:
+    virtual void setBool (SG::AuxElement& element,
+                          bool selection) const override;
+
+  public:
+    virtual std::string label () const override;
+
+
+    //
+    // private interface
+    //
+
+    /// \brief the list of selection accessors I rely on
+  private:
+    std::vector<std::unique_ptr<ISelectionAccessor> > m_list;
+
+    /// \brief the label of the accessor
+  private:
+    std::string m_label;
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/SelectionAccessorNull.h b/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/SelectionAccessorNull.h
new file mode 100644
index 0000000000000000000000000000000000000000..08fbe74e243aec05f535e5575ea4361155dfc2da
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/SelectionAccessorNull.h
@@ -0,0 +1,59 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef SELECTION_HELPERS__SELECTION_ACCESSOR_NULL_H
+#define SELECTION_HELPERS__SELECTION_ACCESSOR_NULL_H
+
+#include <SelectionHelpers/ISelectionAccessor.h>
+
+namespace CP
+{
+  /// \brief a \ref SelectionAccesor that can be used instead of a
+  /// nullptr
+  ///
+  /// This makes it easier to set up code that just uses an \ref
+  /// ISelectionAccessor to preselect its objects, and doesn't want a
+  /// special code path for the case of no preselections.
+
+  class SelectionAccessorNull final : public ISelectionAccessor
+  {
+    //
+    // public interface
+    //
+
+  public:
+    SelectionAccessorNull (bool value = true);
+
+  public:
+    virtual SelectionType
+    getBits (const SG::AuxElement& element) const override;
+
+  public:
+    virtual void setBits (SG::AuxElement& element,
+                          SelectionType selection) const override;
+
+  public:
+    virtual bool
+    getBool (const SG::AuxElement& element) const override;
+
+  public:
+    virtual void setBool (SG::AuxElement& element,
+                          bool selection) const override;
+
+  public:
+    virtual std::string label () const override;
+
+
+    //
+    // private interface
+    //
+  private:
+    bool m_value;
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/SelectionExprParser.h b/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/SelectionExprParser.h
new file mode 100644
index 0000000000000000000000000000000000000000..cd58408031a8dd7f8b00d1e686f607babeba7171
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/SelectionExprParser.h
@@ -0,0 +1,112 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef SELECTION_HELPERS__SELECTION_EXPR_PARSER_H
+#define SELECTION_HELPERS__SELECTION_EXPR_PARSER_H
+
+#include <boost/tokenizer.hpp>
+#include <functional>
+#include <memory>
+#include <string>
+
+#include "SelectionHelpers/ISelectionAccessor.h"
+#include "SelectionHelpers/SelectionHelpers.h"
+
+namespace CP {
+
+// Private detail namespace. This is not strictly private to enable testing
+namespace DetailSelectionExprParser {
+
+/// Separator to be used in a @c boost::tokenizer
+class Separator {
+ public:
+  /// @brief Extracts the subsequent token from the input string iterator
+  bool operator()(std::string::const_iterator &next,
+                  const std::string::const_iterator &end,
+                  std::string &tok) const;
+
+  /// @brief Optional state reset method, does nothing.
+  void reset() {}
+};
+
+// Typedef over boost's tokenizer using the above Separator
+typedef boost::tokenizer<Separator> Tokenizer;
+
+/// Lexer which turns a token stream into a stream of unambigous symbols to be
+/// used by a parser
+class Lexer {
+ public:
+  /// @brief Constructor from a strig
+  Lexer(const std::string &s);
+
+  /// Disable copying of this class
+  Lexer(const Lexer &) = delete;
+  /// Default move-constructor behaviour
+  Lexer(Lexer &&) = default;
+
+  /// Enum over the possible symbols that can be extracted from the token
+  /// stream.
+  enum Type {
+    AND = 0,
+    OR,
+    LEFT,
+    RIGHT,
+    NOT,
+    TRUE_LITERAL,
+    FALSE_LITERAL,
+    VAR,
+    END
+  };
+
+  /// Struct grouping together the type and original string representation
+  /// of a symbol.
+  struct Symbol {
+    Type type;
+    std::string value;
+  };
+
+  /// Generate a new symbol from the token sequence
+  Symbol nextSymbol();
+
+ private:
+  std::string m_string;
+  Tokenizer m_tokenizer;
+  Tokenizer::iterator m_iterator;
+};
+}  // namespace DetailSelectionExprParser
+
+/// Public interface for the expression parsing facility.
+class SelectionExprParser {
+ public:
+  /// Constructor for the parser which accepts a @c Lecer
+  /// @param lexer The lexer to use for parsing
+  /// @note @c Lexer can be auto-constructed from a string, so you can pass one directly.
+  /// @param defaultToChar Assume "as_char" as default encoding
+  SelectionExprParser(DetailSelectionExprParser::Lexer lexer,
+                      bool defaultToChar = false);
+
+  /// Triggers the actual parsing of the expression
+  /// @param [out] accessor Unique pointer the resulting accessor will be written to.
+  /// @return StatusCode noting whether the operation succeeded.
+  StatusCode build(std::unique_ptr<ISelectionAccessor> &accessor);
+
+ private:
+  // Construct a binary OR
+  StatusCode expression(std::unique_ptr<ISelectionAccessor> &root);
+  // Construct an AND, attempts to group all ANDs it can see into a list 
+  StatusCode term(std::unique_ptr<ISelectionAccessor> &root);
+  // Handle other constructs, potentially more ORs or ANDs.
+  StatusCode factor(std::unique_ptr<ISelectionAccessor> &root);
+
+  // The lexer to generate symbols from.
+  DetailSelectionExprParser::Lexer m_lexer;
+  // The last extracted symbol
+  DetailSelectionExprParser::Lexer::Symbol m_symbol;
+  // Stores constructor parameter to be used in calls to makeSelectionAccessorVar.
+  bool m_defaultToChar;
+};
+
+}  // namespace CP
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/SelectionHelpers.h b/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/SelectionHelpers.h
new file mode 100644
index 0000000000000000000000000000000000000000..c75f7ba99eed885ac220b2f78327ab0d5895f7d1
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/SelectionHelpers.h
@@ -0,0 +1,61 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef SELECTION_HELPERS__SELECTION_HELPERS_H
+#define SELECTION_HELPERS__SELECTION_HELPERS_H
+
+#include <AsgTools/MessageCheck.h>
+#include <cstdint>
+
+namespace Root
+{
+  class TAccept;
+}
+
+namespace CP
+{
+  /// \brief the type for selection decorations that are meant to hold
+  /// a \ref Root::TAccept
+  ///
+  /// This is matched to the number of bits in a TAccept, since that
+  /// holds 32 bits, so do we.
+  typedef uint32_t SelectionType;
+
+
+  /// \brief the selection decoration to apply for objects that are
+  /// selected
+  ///
+  /// This is to be used when making a selection decoration for a
+  /// single cut and no TAccept object is at hand, as well as to check
+  /// whether a particular selection passed.
+  inline constexpr SelectionType selectionAccept () {
+    return ~SelectionType (0);}
+
+
+  /// \brief the selection decoration to apply for objects that are
+  /// rejected
+  ///
+  /// This is to be used when making a selection decoration for a
+  /// single cut and no TAccept object is at hand.
+  inline constexpr SelectionType selectionReject () {
+    return ~SelectionType (1);}
+
+
+  /// \brief the selection decoration made from the given boolean
+  /// (true = accept)
+  SelectionType selectionFromBool (bool accept);
+
+
+  /// \brief the selection decoration made from the given TAccept
+  /// object
+  SelectionType selectionFromAccept (const Root::TAccept& accept);
+
+
+  ANA_MSG_HEADER (msgSelectionHelpers)
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/SelectionHelpersDict.h b/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/SelectionHelpersDict.h
new file mode 100644
index 0000000000000000000000000000000000000000..ea6e06901e907dae9f03d3d5579b864998959931
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/SelectionHelpersDict.h
@@ -0,0 +1,14 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef SELECTION_HELPERS__SELECTION_HELPERS_DICT_H
+#define SELECTION_HELPERS__SELECTION_HELPERS_DICT_H
+
+#include "SelectionHelpers/ISelectionAccessor.h"
+#include "SelectionHelpers/OutOfValidityHelper.h"
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/SelectionReadHandle.h b/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/SelectionReadHandle.h
new file mode 100644
index 0000000000000000000000000000000000000000..3ad43fc265e24799bf12a9c7a56857db42b51dc1
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/SelectionReadHandle.h
@@ -0,0 +1,92 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef SELECTION_HELPERS_SELECTION_READ_HANDLE_H
+#define SELECTION_HELPERS_SELECTION_READ_HANDLE_H
+
+#include <AthContainers/AuxElement.h>
+#include <AsgTools/MsgStream.h>
+#include <memory>
+
+class StatusCode;
+
+namespace CP
+{
+  class ISelectionAccessor;
+
+  /// \brief a data handle for reading selection properties from
+  /// objects
+  ///
+  /// Essentially this is just a wrapper around \ref
+  /// ISelectionAccessor to make it easier to use in an algorithm.
+  /// Since we are now using this a lot, it seems like a good idea to
+  /// streamline this as much as possible.
+
+  class SelectionReadHandle final
+  {
+    //
+    // public interface
+    //
+
+    /// \brief standard constructor
+  public:
+    template<typename T2>
+    SelectionReadHandle (T2 *owner, const std::string& propertyName,
+                         const std::string& propertyValue,
+                         const std::string& propertyDescription);
+
+
+    /// \brief whether we have a name configured
+  public:
+    bool empty () const noexcept;
+
+    /// \brief !empty()
+  public:
+    explicit operator bool () const noexcept;
+
+
+    /// \brief initialize the accessor
+  public:
+    StatusCode initialize ();
+
+
+    /// \brief get the selection as a bool
+  public:
+    bool getBool (const SG::AuxElement& element) const;
+
+
+
+    //
+    // private interface
+    //
+
+    /// \brief the selection we use
+  private:
+    std::string m_selection;
+
+    /// \brief the accessor we use
+  private:
+    std::unique_ptr<ISelectionAccessor> m_accessor;
+
+
+    /// \brief the message stream we use
+  private:
+    MsgStream *m_msg {nullptr};
+
+    /// \brief helper for message macros
+  private:
+    MsgStream& msg( ) const;
+
+    /// \brief helper for message macros
+  private:
+    MsgStream& msg( const MSG::Level lvl ) const;
+  };
+}
+
+#include "SelectionReadHandle.icc"
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/SelectionReadHandle.icc b/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/SelectionReadHandle.icc
new file mode 100644
index 0000000000000000000000000000000000000000..e6d084e78168c6898dc7ff61c15d27b1e1b7b11d
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/SelectionReadHandle.icc
@@ -0,0 +1,38 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <SelectionHelpers/ISelectionAccessor.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  template<typename T2> SelectionReadHandle ::
+  SelectionReadHandle (T2 *owner, const std::string& propertyName,
+                 const std::string& propertyValue,
+                 const std::string& propertyDescription)
+    : m_selection (propertyValue)
+    , m_msg (&owner->msg())
+  {
+    owner->declareProperty (propertyName, m_selection, propertyDescription);
+  }
+
+
+
+  inline bool SelectionReadHandle ::
+  getBool (const SG::AuxElement& element) const
+  {
+    assert (m_accessor);
+    return m_accessor->getBool (element);
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/selection.xml b/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/selection.xml
new file mode 100644
index 0000000000000000000000000000000000000000..9f977b4433e0f347dd67f96c5dd8e6a6706ab5b9
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SelectionHelpers/SelectionHelpers/selection.xml
@@ -0,0 +1,6 @@
+<lcgdict>
+
+   <class name="CP::ISelectionAccessor" />
+   <class name="CP::OutOfValidityAction" />
+
+</lcgdict>
diff --git a/PhysicsAnalysis/Algorithms/SelectionHelpers/test/gt_ISelectionAccessor.cxx b/PhysicsAnalysis/Algorithms/SelectionHelpers/test/gt_ISelectionAccessor.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..58b44391d3888f147cfae218c8ef6f5590f0e7b1
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SelectionHelpers/test/gt_ISelectionAccessor.cxx
@@ -0,0 +1,541 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+//
+// includes
+//
+
+#include <SelectionHelpers/ISelectionAccessor.h>
+#include <SelectionHelpers/SelectionAccessorChar.h>
+#include <SelectionHelpers/SelectionAccessorBits.h>
+#include <SelectionHelpers/SelectionAccessorList.h>
+#include <SelectionHelpers/SelectionAccessorNull.h>
+#include <AsgTools/MessageCheck.h>
+#include <AsgTesting/UnitTest.h>
+#include <xAODJet/Jet.h>
+#include <gtest/gtest.h>
+#include "SelectionHelpers/SelectionExprParser.h"
+
+//
+// unit test
+//
+
+namespace CP
+{
+  TEST (ISelectionAccessorTest, all_tests)
+  {
+    auto jet = std::make_unique<xAOD::Jet> ();
+    jet->makePrivateStore();
+
+    // check a basic char accessor
+    std::unique_ptr<ISelectionAccessor> accA;
+    ASSERT_SUCCESS (makeSelectionAccessor ("a,as_char", accA));
+    auto* selA = dynamic_cast<SelectionAccessorChar*>(accA.get());
+    // check that this is actually nothing but a simple char accessor
+    EXPECT_NE(selA, nullptr);
+    accA->setBool (*jet, false);
+    EXPECT_EQ (jet->auxdata<char> ("a"), 0);
+    accA->setBool (*jet, true);
+    EXPECT_EQ (jet->auxdata<char> ("a"), 1);
+    accA->setBits (*jet, selectionReject());
+    EXPECT_EQ (jet->auxdata<char> ("a"), 0);
+    accA->setBits (*jet, selectionAccept());
+    EXPECT_EQ (jet->auxdata<char> ("a"), 1);
+
+    // check an implicit char accessor
+    std::unique_ptr<ISelectionAccessor> accB;
+    ASSERT_SUCCESS (makeSelectionAccessor ("b", accB, true));
+    accB->setBool (*jet, false);
+    EXPECT_EQ (jet->auxdata<char> ("b"), 0);
+    accB->setBool (*jet, true);
+    EXPECT_EQ (jet->auxdata<char> ("b"), 1);
+
+    // check a basic bits accessor
+    std::unique_ptr<ISelectionAccessor> accC;
+    ASSERT_SUCCESS (makeSelectionAccessor ("c,as_bits", accC));
+    // check that this is actually nothing but a simple bits accessor
+    auto* selC = dynamic_cast<SelectionAccessorBits*>(accC.get());
+    EXPECT_NE(selC, nullptr);
+    accC->setBool (*jet, false);
+    EXPECT_EQ (jet->auxdata<SelectionType> ("c"), selectionReject());
+    accC->setBool (*jet, true);
+    EXPECT_EQ (jet->auxdata<SelectionType> ("c"), selectionAccept());
+    accC->setBits (*jet, selectionReject());
+    EXPECT_EQ (jet->auxdata<SelectionType> ("c"), selectionReject());
+    accC->setBits (*jet, selectionAccept());
+    EXPECT_EQ (jet->auxdata<SelectionType> ("c"), selectionAccept());
+
+    // check an implicit bits accessor
+    std::unique_ptr<ISelectionAccessor> accD;
+    ASSERT_SUCCESS (makeSelectionAccessor ("d", accD, false));
+    accD->setBool (*jet, false);
+    EXPECT_EQ (jet->auxdata<SelectionType> ("d"), selectionReject());
+    accD->setBool (*jet, true);
+    EXPECT_EQ (jet->auxdata<SelectionType> ("d"), selectionAccept());
+
+    // check an and of two accessors
+    std::unique_ptr<ISelectionAccessor> accAnd;
+    ASSERT_SUCCESS (makeSelectionAccessor ("a,as_char&&b,as_char", accAnd));
+    accA->setBool (*jet, true);
+    accB->setBool (*jet, true);
+    EXPECT_TRUE (accAnd->getBool (*jet));
+    EXPECT_EQ (accAnd->getBits (*jet), ~(SelectionType)0);
+    accA->setBool (*jet, false);
+    accB->setBool (*jet, true);
+    EXPECT_FALSE (accAnd->getBool (*jet));
+    EXPECT_EQ (accAnd->getBits (*jet), ~(SelectionType)1);
+    accA->setBool (*jet, true);
+    accB->setBool (*jet, false);
+    EXPECT_FALSE (accAnd->getBool (*jet));
+    EXPECT_EQ (accAnd->getBits (*jet), ~(SelectionType)2);
+    accA->setBool (*jet, false);
+    accB->setBool (*jet, false);
+    EXPECT_FALSE (accAnd->getBool (*jet));
+    EXPECT_EQ (accAnd->getBits (*jet), ~(SelectionType)3);
+
+    // check AND of three accessors
+    ASSERT_SUCCESS (makeSelectionAccessor ("a,as_char&&b,as_char&&c,as_bits", accAnd));
+    auto* selAnd = dynamic_cast<SelectionAccessorList*>(accAnd.get());
+    EXPECT_NE(selAnd, nullptr);
+
+    accA->setBool (*jet, true);
+    accB->setBool (*jet, true);
+    accC->setBool (*jet, true);
+    EXPECT_TRUE (accAnd->getBool (*jet));
+    EXPECT_EQ (accAnd->getBits (*jet), ~(SelectionType)0);
+
+    accA->setBool (*jet, false);
+    accB->setBool (*jet, true);
+    accC->setBool (*jet, true);
+    EXPECT_FALSE (accAnd->getBool (*jet));
+    EXPECT_EQ (accAnd->getBits (*jet), ~(SelectionType)1);
+
+    accA->setBool (*jet, true);
+    accB->setBool (*jet, false);
+    accC->setBool (*jet, true);
+    EXPECT_FALSE (accAnd->getBool (*jet));
+    EXPECT_EQ (accAnd->getBits (*jet), ~(SelectionType)2);
+
+    accA->setBool (*jet, false);
+    accB->setBool (*jet, false);
+    accC->setBool (*jet, true);
+    EXPECT_FALSE (accAnd->getBool (*jet));
+    EXPECT_EQ (accAnd->getBits (*jet), ~(SelectionType)3);
+
+    accA->setBool (*jet, true);
+    accB->setBool (*jet, true);
+    accC->setBool (*jet, false);
+    EXPECT_FALSE (accAnd->getBool (*jet));
+    EXPECT_EQ (accAnd->getBits (*jet), ~(SelectionType)4);
+
+    accA->setBool (*jet, false);
+    accB->setBool (*jet, true);
+    accC->setBool (*jet, false);
+    EXPECT_FALSE (accAnd->getBool (*jet));
+    EXPECT_EQ (accAnd->getBits (*jet), ~(SelectionType)5);
+
+    accA->setBool (*jet, true);
+    accB->setBool (*jet, false);
+    accC->setBool (*jet, false);
+    EXPECT_FALSE (accAnd->getBool (*jet));
+    EXPECT_EQ (accAnd->getBits (*jet), ~(SelectionType)6);
+
+    accA->setBool (*jet, false);
+    accB->setBool (*jet, false);
+    accC->setBool (*jet, false);
+    EXPECT_FALSE (accAnd->getBool (*jet));
+    EXPECT_EQ (accAnd->getBits (*jet), ~(SelectionType)7);
+
+    // check an OR of two accessors
+    std::unique_ptr<ISelectionAccessor> accOr;
+    ASSERT_SUCCESS (makeSelectionAccessor ("a,as_char||b,as_char", accOr));
+    accA->setBool (*jet, false);
+    accB->setBool (*jet, true);
+    EXPECT_TRUE (accOr->getBool (*jet));
+    EXPECT_EQ (accOr->getBits (*jet), selectionAccept());
+    accA->setBool (*jet, true);
+    accB->setBool (*jet, false);
+    EXPECT_TRUE (accOr->getBool (*jet));
+    EXPECT_EQ (accOr->getBits (*jet), selectionAccept());
+    accA->setBool (*jet, false);
+    accB->setBool (*jet, false);
+    EXPECT_FALSE (accOr->getBool (*jet));
+    EXPECT_EQ (accOr->getBits (*jet), selectionReject());
+
+    std::unique_ptr<ISelectionAccessor> accEx;
+    ASSERT_SUCCESS (makeSelectionAccessor ("a,as_char||(b,as_char && c,as_bits)", accEx));
+    accA->setBool (*jet, true);
+    accB->setBool (*jet, true);
+    accC->setBool (*jet, true);
+    EXPECT_TRUE (accEx->getBool (*jet));
+    EXPECT_EQ (accEx->getBits (*jet), selectionAccept());
+    accA->setBool (*jet, false);
+    accB->setBool (*jet, true);
+    accC->setBool (*jet, true);
+    EXPECT_TRUE (accEx->getBool (*jet));
+    EXPECT_EQ (accEx->getBits (*jet), selectionAccept());
+    accA->setBool (*jet, false);
+    accB->setBool (*jet, false);
+    accC->setBool (*jet, true);
+    EXPECT_FALSE (accEx->getBool (*jet));
+    EXPECT_EQ (accEx->getBits (*jet), selectionReject());
+    accA->setBool (*jet, false);
+    accB->setBool (*jet, true);
+    accC->setBool (*jet, false);
+    EXPECT_FALSE (accEx->getBool (*jet));
+    EXPECT_EQ (accEx->getBits (*jet), selectionReject());
+    accA->setBool (*jet, false);
+    accB->setBool (*jet, false);
+    accC->setBool (*jet, false);
+    EXPECT_FALSE (accEx->getBool (*jet));
+    EXPECT_EQ (accEx->getBits (*jet), selectionReject());
+    accA->setBool (*jet, true);
+    accB->setBool (*jet, false);
+    accC->setBool (*jet, false);
+    EXPECT_TRUE (accEx->getBool (*jet));
+    EXPECT_EQ (accEx->getBits (*jet), selectionAccept());
+
+    ASSERT_SUCCESS (makeSelectionAccessor ("a,as_char||(b,as_char && !c,as_bits)", accEx));
+    accA->setBool (*jet, true);
+    accB->setBool (*jet, true);
+    accC->setBool (*jet, false);
+    EXPECT_TRUE (accEx->getBool (*jet));
+    EXPECT_EQ (accEx->getBits (*jet), selectionAccept());
+    accA->setBool (*jet, false);
+    accB->setBool (*jet, true);
+    accC->setBool (*jet, false);
+    EXPECT_TRUE (accEx->getBool (*jet));
+    EXPECT_EQ (accEx->getBits (*jet), selectionAccept());
+    accA->setBool (*jet, false);
+    accB->setBool (*jet, false);
+    accC->setBool (*jet, false);
+    EXPECT_FALSE (accEx->getBool (*jet));
+    EXPECT_EQ (accEx->getBits (*jet), selectionReject());
+    accA->setBool (*jet, false);
+    accB->setBool (*jet, true);
+    accC->setBool (*jet, true);
+    EXPECT_FALSE (accEx->getBool (*jet));
+    EXPECT_EQ (accEx->getBits (*jet), selectionReject());
+    accA->setBool (*jet, false);
+    accB->setBool (*jet, false);
+    accC->setBool (*jet, true);
+    EXPECT_FALSE (accEx->getBool (*jet));
+    EXPECT_EQ (accEx->getBits (*jet), selectionReject());
+    accA->setBool (*jet, true);
+    accB->setBool (*jet, false);
+    accC->setBool (*jet, true);
+    EXPECT_TRUE (accEx->getBool (*jet));
+    EXPECT_EQ (accEx->getBits (*jet), selectionAccept());
+
+
+    // test that an empty string produces a SelectionAccessorNull(true)
+    std::unique_ptr<ISelectionAccessor> accEmpty;
+    ASSERT_SUCCESS (makeSelectionAccessor ("", accEmpty));
+    auto accNull = dynamic_cast<SelectionAccessorNull*>(accEmpty.get());
+    EXPECT_NE (accNull, nullptr); // is in fact a null accessor
+    // can either be true or false, let's test that it is true
+    EXPECT_TRUE (accEmpty->getBool (*jet));
+
+  }
+
+  TEST (SelectionExprParser, tokenizer) {
+    using tok = DetailSelectionExprParser::Tokenizer;
+
+    std::string s = "A && B";
+    tok tokens(s, {});
+    auto it = tokens.begin();
+    EXPECT_EQ(*(it++), "A");
+    EXPECT_EQ(*(it++), "&&");
+    EXPECT_EQ(*(it++), "B");
+
+    s = "A&&B";
+    tokens = tok{s, {}};
+    it = tokens.begin();
+    EXPECT_EQ(*(it++), "A");
+    EXPECT_EQ(*(it++), "&&");
+    EXPECT_EQ(*(it++), "B");
+
+    s = "( alpha || gamma) &beta ";
+    tokens = tok{s, {}};
+    it = tokens.begin();
+    EXPECT_EQ(*(it++), "(");
+    EXPECT_EQ(*(it++), "alpha");
+    EXPECT_EQ(*(it++), "||");
+    EXPECT_EQ(*(it++), "gamma");
+    EXPECT_EQ(*it, ")");
+    EXPECT_THROW(it++, std::runtime_error);
+
+    s = "a|b";
+    tokens = tok{s, {}};
+    it = tokens.begin();
+    EXPECT_EQ(*it, "a");
+    EXPECT_THROW(it++, std::runtime_error);
+
+    s = "( alpha || gamma) &&beta ";
+    tokens = tok{s, {}};
+    it = tokens.begin();
+    EXPECT_EQ(*(it++), "(");
+    EXPECT_EQ(*(it++), "alpha");
+    EXPECT_EQ(*(it++), "||");
+    EXPECT_EQ(*(it++), "gamma");
+    EXPECT_EQ(*(it++), ")");
+    EXPECT_EQ(*(it++), "&&"); // & not a token: considered part of variable
+    EXPECT_EQ(*(it++), "beta");
+
+  }
+
+  TEST (SelectionExprParser, lexer) {
+    using lexer = DetailSelectionExprParser::Lexer;
+    using type = lexer::Type;
+
+    lexer::Symbol s;
+    {
+      lexer lex("A&&B");
+      s = lex.nextSymbol();
+      EXPECT_EQ(s.type, type::VAR);
+      EXPECT_EQ(s.value, "A");
+
+      s = lex.nextSymbol();
+      EXPECT_EQ(s.type, type::AND);
+      EXPECT_EQ(s.value, "&&");
+
+      s = lex.nextSymbol();
+      EXPECT_EQ(s.type, type::VAR);
+      EXPECT_EQ(s.value, "B");
+    }
+
+    {
+      std::string str = "alpha,as_bits && beta,as_char";
+      lexer lex{str};
+      s = lex.nextSymbol();
+      EXPECT_EQ(s.type, type::VAR);
+      EXPECT_EQ(s.value, "alpha,as_bits");
+
+      s = lex.nextSymbol();
+      EXPECT_EQ(s.type, type::AND);
+      EXPECT_EQ(s.value, "&&");
+
+      s = lex.nextSymbol();
+      EXPECT_EQ(s.type, type::VAR);
+      EXPECT_EQ(s.value, "beta,as_char");
+    }
+
+
+    {
+      std::string str = "alpha,as_bits&&beta,as_char";
+      lexer lex{str};
+      s = lex.nextSymbol();
+      EXPECT_EQ(s.type, type::VAR);
+      EXPECT_EQ(s.value, "alpha,as_bits");
+
+      s = lex.nextSymbol();
+      EXPECT_EQ(s.type, type::AND);
+      EXPECT_EQ(s.value, "&&");
+
+      s = lex.nextSymbol();
+      EXPECT_EQ(s.type, type::VAR);
+      EXPECT_EQ(s.value, "beta,as_char");
+    }
+
+    {
+      std::string str = "alpha,as_bits || beta,as_char";
+      lexer lex{str};
+      s = lex.nextSymbol();
+      EXPECT_EQ(s.type, type::VAR);
+      EXPECT_EQ(s.value, "alpha,as_bits");
+
+      s = lex.nextSymbol();
+      EXPECT_EQ(s.type, type::OR);
+      EXPECT_EQ(s.value, "||");
+
+      s = lex.nextSymbol();
+      EXPECT_EQ(s.type, type::VAR);
+      EXPECT_EQ(s.value, "beta,as_char");
+    }
+
+
+    {
+      std::string str = "alpha,as_bits||beta,as_char";
+      lexer lex{str};
+      s = lex.nextSymbol();
+      EXPECT_EQ(s.type, type::VAR);
+      EXPECT_EQ(s.value, "alpha,as_bits");
+
+      s = lex.nextSymbol();
+      EXPECT_EQ(s.type, type::OR);
+      EXPECT_EQ(s.value, "||");
+
+      s = lex.nextSymbol();
+      EXPECT_EQ(s.type, type::VAR);
+      EXPECT_EQ(s.value, "beta,as_char");
+    }
+
+    {
+      std::string str = "alpha,as_bits||(beta,as_char &&!gamma,as_bits)";
+      lexer lex{str};
+      s = lex.nextSymbol();
+      EXPECT_EQ(s.type, type::VAR);
+      EXPECT_EQ(s.value, "alpha,as_bits");
+
+      s = lex.nextSymbol();
+      EXPECT_EQ(s.type, type::OR);
+
+      s = lex.nextSymbol();
+      EXPECT_EQ(s.type, type::LEFT);
+
+      s = lex.nextSymbol();
+      EXPECT_EQ(s.type, type::VAR);
+      EXPECT_EQ(s.value, "beta,as_char");
+
+      s = lex.nextSymbol();
+      EXPECT_EQ(s.type, type::AND);
+
+      s = lex.nextSymbol();
+      EXPECT_EQ(s.type, type::NOT);
+
+      s = lex.nextSymbol();
+      EXPECT_EQ(s.type, type::VAR);
+      EXPECT_EQ(s.value, "gamma,as_bits");
+
+      s = lex.nextSymbol();
+      EXPECT_EQ(s.type, type::RIGHT);
+    }
+  }
+
+  TEST (SelectionExprParser, parser) {
+    auto parse = [](std::string s) -> std::string {
+      SelectionExprParser p(s, true);
+      std::unique_ptr<ISelectionAccessor> acc;
+      if(!p.build(acc).isSuccess()) {
+        ADD_FAILURE() << "unable to parse expression";
+      } 
+      return acc->label();
+    };
+
+    std::string s;
+    // this asserts the tree structure
+    s = parse("A&&(C||!B)");
+    EXPECT_EQ(s, "( A && ( C || !B ) )");
+    // and this asserts the label output can be parse again,
+    // leading to the exact same expression
+    EXPECT_EQ(parse(s), s);
+
+    s = parse("A&&(C||!(B&&true))");
+    EXPECT_EQ(s, "( A && ( C || !( B && true ) ) )");
+    EXPECT_EQ(parse(s), s);
+
+    s = parse("alpha && ( beta || ! gamma )");
+    EXPECT_EQ(s, "( alpha && ( beta || !gamma ) )");
+    EXPECT_EQ(parse(s), s);
+
+    s = parse(" alpha&&beta || !gamma ");
+    EXPECT_EQ(s, "( ( alpha && beta ) || !gamma )");
+    EXPECT_EQ(parse(s), s);
+
+    s = parse(" (alpha&&beta) || !gamma ");
+    EXPECT_EQ(s, "( ( alpha && beta ) || !gamma )");
+    EXPECT_EQ(parse(s), s);
+
+    s = parse(" A && B && C && D");
+    EXPECT_EQ(s, "( A && B && C && D )");
+    EXPECT_EQ(parse(s), s);
+
+
+    EXPECT_THROW(parse("alpha &&"), std::runtime_error);
+    EXPECT_THROW(parse("&&"), std::runtime_error);
+    EXPECT_THROW(parse("&& alpha"), std::runtime_error);
+    EXPECT_THROW(parse("alpha ||"), std::runtime_error);
+    EXPECT_THROW(parse("||"), std::runtime_error);
+    EXPECT_THROW(parse("|| alpha"), std::runtime_error);
+    EXPECT_THROW(parse("(alpha && beta || gamma"), std::runtime_error);
+    EXPECT_THROW(parse("alpha && beta) || gamma"), std::runtime_error);
+    EXPECT_THROW(parse("!"), std::runtime_error);
+    EXPECT_THROW(parse("alpha !"), std::runtime_error);
+    EXPECT_THROW(parse("()"), std::runtime_error);
+    EXPECT_THROW(parse(")"), std::runtime_error);
+    EXPECT_THROW(parse("("), std::runtime_error);
+  }
+  
+  TEST (SelectionExprParser, evaluate) {
+    auto mkex = [](const std::string& s) {
+      SelectionExprParser p(s, true);
+      std::unique_ptr<ISelectionAccessor> acc;
+      if(!p.build(acc).isSuccess()) {
+        ADD_FAILURE() << "unable to parse expression";
+      }
+      return acc;
+    };
+
+    auto jet_ptr = std::make_unique<xAOD::Jet> ();
+    auto& jet = *jet_ptr;
+    jet.makePrivateStore();
+
+    xAOD::Jet::Decorator<char> alpha("alpha");
+    xAOD::Jet::Decorator<char> beta("beta");
+    xAOD::Jet::Decorator<char> gamma("gamma");
+
+    {
+
+      std::string s = "alpha && ( beta || ! gamma )";
+      auto ex = mkex(s);
+
+      alpha(jet) = true;
+      beta(jet) = true;
+      gamma(jet) = true;
+      EXPECT_EQ(ex->getBool(jet), true);
+
+      alpha(jet) = false;
+      EXPECT_EQ(ex->getBool(jet), 0);
+
+      alpha(jet) = true;
+      beta(jet) = false;
+      EXPECT_EQ(ex->getBool(jet), 0);
+
+      gamma(jet) = false;
+      EXPECT_EQ(ex->getBool(jet), 1);
+    }
+    {
+      std::string s = "true||(alpha && ( beta || ! gamma ))";
+      auto ex = mkex(s);
+
+      EXPECT_TRUE(ex->getBool(jet));
+
+      alpha(jet) = false;
+      EXPECT_TRUE(ex->getBool(jet));
+
+      alpha(jet) = true;
+      beta(jet) = false;
+      EXPECT_TRUE(ex->getBool(jet));
+
+      gamma(jet) = false;
+      EXPECT_TRUE(ex->getBool(jet));
+    }
+    {
+      auto ex = mkex("true");
+      EXPECT_TRUE(ex->getBool(jet));
+
+      ex = mkex("false");
+      EXPECT_FALSE(ex->getBool(jet));
+
+      ex = mkex("true && false");
+      EXPECT_FALSE(ex->getBool(jet));
+
+      ex = mkex("true || false");
+      EXPECT_TRUE(ex->getBool(jet));
+
+      ex = mkex("true && !false");
+      EXPECT_TRUE(ex->getBool(jet));
+
+      ex = mkex("!true && !false");
+      EXPECT_FALSE(ex->getBool(jet));
+
+    }
+  }
+}
+
+ATLAS_GOOGLE_TEST_MAIN
diff --git a/PhysicsAnalysis/Algorithms/StandaloneAnalysisAlgorithms/CMakeLists.txt b/PhysicsAnalysis/Algorithms/StandaloneAnalysisAlgorithms/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..ff41deca6757173eaef0d86abb266ac79bf563c7
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/StandaloneAnalysisAlgorithms/CMakeLists.txt
@@ -0,0 +1,36 @@
+
+# Set the name of the package:
+atlas_subdir( StandaloneAnalysisAlgorithms )
+
+# Check that this is a standalone release:
+if( NOT XAOD_STANDALONE )
+   message( WARNING "This package is only meant for standalone releases" )
+   return()
+endif()
+
+# Set the package's dependencies:
+atlas_depends_on_subdirs(
+   PUBLIC
+   Control/xAODRootAccess
+   PhysicsAnalysis/Algorithms/SystematicsHandles
+   PhysicsAnalysis/D3PDTools/AnaAlgorithm
+   PRIVATE
+   Control/AthContainers
+   PhysicsAnalysis/D3PDTools/EventLoop )
+
+# External(s):
+find_package( ROOT COMPONENTS Core RIO )
+
+# Set up the main library:
+atlas_add_library( StandaloneAnalysisAlgorithmsLib
+   StandaloneAnalysisAlgorithms/*.h Root/*.cxx
+   PUBLIC_HEADERS StandaloneAnalysisAlgorithms
+   LINK_LIBRARIES xAODRootAccess SystematicsHandlesLib AnaAlgorithmLib
+   PRIVATE_INCLUDE_DIRS ${ROOT_INCLUDE_DIRS}
+   PRIVATE_LINK_LIBRARIES ${ROOT_LIBRARIES} AthContainers EventLoop )
+
+# Set up a dictionary:
+atlas_add_dictionary( StandaloneAnalysisAlgorithmsDict
+   StandaloneAnalysisAlgorithms/StandaloneAnalysisAlgorithmsDict.h
+   StandaloneAnalysisAlgorithms/selection.xml
+   LINK_LIBRARIES StandaloneAnalysisAlgorithmsLib )
diff --git a/PhysicsAnalysis/Algorithms/StandaloneAnalysisAlgorithms/README.md b/PhysicsAnalysis/Algorithms/StandaloneAnalysisAlgorithms/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..866f31f8a482324a01bce80cd494f802d011f508
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/StandaloneAnalysisAlgorithms/README.md
@@ -0,0 +1,9 @@
+# Standalone Analysis Algorithms
+
+This package collects code/algorithms that are only relevant for standalone
+analysis releases. So they wouldn't need to be compiled in Athena based
+releases.
+
+Current algorithm(s) in the package:
+  - [CP::xAODWriterAlg](StandaloneAnalysisAlgorithms/xAODWriterAlg.h):
+    Algorithm for writing mini-xAOD files as the output of analysis jobs.
diff --git a/PhysicsAnalysis/Algorithms/StandaloneAnalysisAlgorithms/Root/xAODWriterAlg.cxx b/PhysicsAnalysis/Algorithms/StandaloneAnalysisAlgorithms/Root/xAODWriterAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..ae74459e41638f063e9c2e3ea68df98102e37c91
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/StandaloneAnalysisAlgorithms/Root/xAODWriterAlg.cxx
@@ -0,0 +1,202 @@
+//
+// Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+//
+
+// System include(s):
+#include <regex>
+
+// ROOT include(s):
+#include <TClass.h>
+#include <TFile.h>
+
+// Core include(s):
+#include "AthContainers/normalizedTypeinfoName.h"
+#include "EventLoop/Worker.h"
+#include "SystematicsHandles/Helpers.h"
+
+// Local include(s):
+#include "StandaloneAnalysisAlgorithms/xAODWriterAlg.h"
+
+namespace CP {
+
+   xAODWriterAlg::xAODWriterAlg( const std::string& name, ISvcLocator* svcLoc )
+      : EL::AnaAlgorithm( name, svcLoc ) {
+
+      // Declare the algorithm's properties.
+      declareProperty( "OutputStreamName", m_outputStreamName = "ANALYSIS",
+                       "Stream name of the output file to use" );
+      declareProperty( "ItemList", m_itemList,
+                       "Objects to write to the output file" );
+      declareProperty( "BasketSize", m_basketSize = 32000,
+                       "(Starter) Basket size for the created branches" );
+      declareProperty( "SplitLevel", m_splitLevel = 0,
+                       "Split level for the created branches" );
+   }
+
+   StatusCode xAODWriterAlg::initialize() {
+
+      // Make sure that the xAOD::TEvent object managed by EventLoop is the
+      // "active" one.
+      evtStore()->event()->setActive();
+
+      // Set up the systematics list.
+      ATH_CHECK( m_systematicsList.initialize() );
+
+      // Access the file of the output stream.
+      TFile* ofile = wk()->getOutputFile( m_outputStreamName );
+      if( ! ofile ) {
+         ATH_MSG_FATAL( "Couldn't access output file for stream \""
+                        << m_outputStreamName << "\"" );
+         return StatusCode::FAILURE;
+      }
+
+      // Write to this output file.
+      ANA_CHECK( m_event.writeTo( ofile ) );
+
+      // Reset the internal flag(s).
+      m_itemListInitialized = false;
+
+      // Return gracefully.
+      return StatusCode::SUCCESS;
+   }
+
+   StatusCode xAODWriterAlg::execute() {
+
+      // If this is the first event, figure out which objects can actually be
+      // written out.
+      if( ! m_itemListInitialized ) {
+         ANA_CHECK( setup() );
+         m_itemListInitialized = true;
+      }
+
+      // Write all objects to the output file.
+      xAOD::TEvent* event = evtStore()->event();
+      for( const Item& item : m_writtenItemList ) {
+
+         // Get the object. See the description in @c xAOD::TEvent::retrieve
+         // (the const version) for an explanation of this implementation.
+         static const bool SILENT = true;
+         static const bool METADATA = false;
+         const void* obj = event->getOutputObject( item.name, *( item.type ),
+                                                   METADATA );
+         if( ! obj ) {
+            obj = event->getInputObject( item.name, *( item.type ), SILENT,
+                                         METADATA );
+         } else {
+            event->getInputObject( item.name, *( item.type ), SILENT,
+                                   METADATA );
+         }
+
+         // Check that we succeeded.
+         if( ! obj ) {
+            ATH_MSG_FATAL( "Couldn't retrieve object \"" << item.name << "\"" );
+            return StatusCode::FAILURE;
+         }
+
+         // Record it to the output for the current event.
+         ANA_CHECK( m_event.record( const_cast< void* >( obj ), item.typeName,
+                                    item.name, m_basketSize, m_splitLevel ) );
+      }
+
+      // Write the event.
+      if( m_event.fill() <= 0 ) {
+         ATH_MSG_FATAL( "There was an error writing out the event" );
+         return StatusCode::FAILURE;
+      }
+
+      // Return gracefully.
+      return StatusCode::SUCCESS;
+   }
+
+   StatusCode xAODWriterAlg::finalize() {
+
+      // Access the file of the output stream.
+      TFile* ofile = wk()->getOutputFile( m_outputStreamName );
+      if( ! ofile ) {
+         ATH_MSG_FATAL( "Couldn't access output file for stream \""
+                        << m_outputStreamName << "\"" );
+         return StatusCode::FAILURE;
+      }
+
+      // Finish writing to this output file.
+      ANA_CHECK( m_event.finishWritingTo( ofile ) );
+
+      // Return gracefully.
+      return StatusCode::SUCCESS;
+   }
+
+   StatusCode xAODWriterAlg::setup() {
+
+      // Loop over all of the declared items.
+      for( const std::string& stringItem : m_itemList ) {
+
+         // Interpret the item string.
+         static const std::regex itemRegex( "([^#]+)#([^\\.]+\\.?)(.*)" );
+         std::smatch itemMatch;
+         if( ! std::regex_match( stringItem, itemMatch, itemRegex ) ) {
+            ATH_MSG_FATAL( "Item \"" << stringItem
+                           << "\" is not of the form: \"Type#Name\"" );
+            return StatusCode::FAILURE;
+         }
+         ATH_MSG_VERBOSE( "Found item: " << itemMatch[ 1 ] << "#"
+                          << itemMatch[ 2 ] << itemMatch[ 3 ] );
+
+         // Consider all systematics. Not usin CP::SysListHandle::foreach, to
+         // be able to exit the for-loop early if necessary.
+         auto sysVector = m_systematicsList.systematicsVector();
+         for( const auto& sys : sysVector ) {
+
+            // Event store key for the object under consideration.
+            const std::string key = makeSystematicsName( itemMatch[ 2 ], sys );
+
+            // Whether or not the object will be available, as long as
+            // variable selection rules were set up for it, let xAOD::TEvent
+            // know about them.
+            if( itemMatch[ 3 ] != "" ) {
+               ATH_MSG_DEBUG( "Calling setAuxItemList( \"" << key << "\""
+                              << ", \"" << itemMatch[ 3 ]
+                              << "\" )" );
+               m_event.setAuxItemList( key, itemMatch[ 3 ] );
+            }
+
+            // Construct an Item object.
+            Item item;
+            item.name = key;
+            TClass* cl = TClass::GetClass( itemMatch[ 1 ].str().c_str() );
+            if( ! cl ) {
+               ATH_MSG_FATAL( "Type \"" << itemMatch[ 1 ] << "\" not found" );
+               return StatusCode::FAILURE;
+            }
+            item.type = cl->GetTypeInfo();
+            if( ! item.type ) {
+               ATH_MSG_FATAL( "No compiled dictionary found for \""
+                              << itemMatch[ 1 ] << "\"" );
+               return StatusCode::FAILURE;
+            }
+            item.typeName = SG::normalizedTypeinfoName( *( item.type ) );
+
+            // Check if the item is available.
+            static const bool SILENT = true;
+            static const bool METADATA = false;
+            xAOD::TEvent* event = evtStore()->event();
+            if( event->getOutputObject( item.name, *( item.type ), METADATA ) ||
+                event->getInputObject( item.name, *( item.type ), SILENT,
+                                       METADATA ) ) {
+                  m_writtenItemList.push_back( item );
+                  ATH_MSG_DEBUG( "Scheduling \"" << itemMatch[ 1 ] << "#"
+                                 << key << "\" for writing" );
+            }
+
+            // If there was no %SYS% pattern in the object name, stop the loop
+            // over the systematics now.
+            if( key == itemMatch[ 2 ] ) {
+               break;
+            }
+         }
+      }
+
+      // Return gracefully.
+      return StatusCode::SUCCESS;
+   }
+
+} // namespace CP
diff --git a/PhysicsAnalysis/Algorithms/StandaloneAnalysisAlgorithms/StandaloneAnalysisAlgorithms/StandaloneAnalysisAlgorithmsDict.h b/PhysicsAnalysis/Algorithms/StandaloneAnalysisAlgorithms/StandaloneAnalysisAlgorithms/StandaloneAnalysisAlgorithmsDict.h
new file mode 100644
index 0000000000000000000000000000000000000000..0fad3aeb3153c93849a857e5b81b259b4834b551
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/StandaloneAnalysisAlgorithms/StandaloneAnalysisAlgorithms/StandaloneAnalysisAlgorithmsDict.h
@@ -0,0 +1,11 @@
+// Dear emacs, this is -*- c++ -*-
+//
+// Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+//
+#ifndef STANDALONEANALYSISALGORITHMS_STANDALONEANALYSISALGORITHMSDICT_H
+#define STANDALONEANALYSISALGORITHMS_STANDALONEANALYSISALGORITHMSDICT_H
+
+// Local include(s):
+#include "StandaloneAnalysisAlgorithms/xAODWriterAlg.h"
+
+#endif // STANDALONEANALYSISALGORITHMS_STANDALONEANALYSISALGORITHMSDICT_H
diff --git a/PhysicsAnalysis/Algorithms/StandaloneAnalysisAlgorithms/StandaloneAnalysisAlgorithms/selection.xml b/PhysicsAnalysis/Algorithms/StandaloneAnalysisAlgorithms/StandaloneAnalysisAlgorithms/selection.xml
new file mode 100644
index 0000000000000000000000000000000000000000..073de929384c57dfbffdf09ba40ca49693669739
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/StandaloneAnalysisAlgorithms/StandaloneAnalysisAlgorithms/selection.xml
@@ -0,0 +1,7 @@
+<!-- Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration -->
+<lcgdict>
+
+  <!-- Algorithm(s): -->
+  <class name="CP::xAODWriterAlg" />
+
+</lcgdict>
diff --git a/PhysicsAnalysis/Algorithms/StandaloneAnalysisAlgorithms/StandaloneAnalysisAlgorithms/xAODWriterAlg.h b/PhysicsAnalysis/Algorithms/StandaloneAnalysisAlgorithms/StandaloneAnalysisAlgorithms/xAODWriterAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..2be743298f94f86255598a2990a0a657027395c9
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/StandaloneAnalysisAlgorithms/StandaloneAnalysisAlgorithms/xAODWriterAlg.h
@@ -0,0 +1,92 @@
+// Dear emacs, this is -*- c++ -*-
+//
+// Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+//
+#ifndef STANDALONEANALYSISALGORITHMS_XAODWRITERALG_H
+#define STANDALONEANALYSISALGORITHMS_XAODWRITERALG_H
+
+// System include(s):
+#include <string>
+#include <vector>
+#include <typeinfo>
+
+// Core include(s):
+#include "AnaAlgorithm/AnaAlgorithm.h"
+#include "SystematicsHandles/SysListHandle.h"
+#include "xAODRootAccess/TEvent.h"
+
+namespace CP {
+
+   /// Algorithm writing an xAOD output file
+   ///
+   /// This algorithm is meant to be used to write mini-xAOD output files from
+   /// analysis jobs. Similar to how such a file could be written using an
+   /// Athena job.
+   ///
+   /// @author Attila Krasznahorkay <Attila.Krasznahorkay@cern.ch>
+   ///
+   class xAODWriterAlg final : public EL::AnaAlgorithm {
+
+   public:
+      /// Algorithm constructor
+      xAODWriterAlg( const std::string& name, ISvcLocator* svcLoc );
+
+      /// @name Function(s) inherited from @c EL::AnaAlgorithm
+      /// @{
+
+      /// Function initialising the algorithm
+      StatusCode initialize() override;
+
+      /// Function executing the algorithm
+      StatusCode execute() override;
+
+      /// Function finalising the algorithm
+      StatusCode finalize() override;
+
+      /// @}
+
+   private:
+      /// Function setting up the algorithm while processing the first event
+      StatusCode setup();
+
+      /// @name Algorithm properties
+      /// @{
+
+      /// Name of the output stream to write to
+      std::string m_outputStreamName;
+      /// Item list to write to the output file
+      std::vector< std::string > m_itemList;
+
+      /// (Starter) Basket size for the created branches
+      int m_basketSize;
+      /// Split level for the created branches
+      int m_splitLevel;
+
+      /// @}
+
+      /// Helper struct
+      struct Item {
+         /// Name of the written object
+         std::string name;
+         /// Type of the written object
+         const std::type_info* type;
+         /// Type name of the written object
+         std::string typeName;
+      }; // struct Item
+
+      /// Object to write the output file with
+      xAOD::TEvent m_event;
+
+      /// Internal flag
+      bool m_itemListInitialized = false;
+      /// Item list being written after the first event
+      std::vector< Item > m_writtenItemList;
+
+      /// The systematic list to consider during execution
+      SysListHandle m_systematicsList{ this };
+
+   }; // class xAODWriterAlg
+
+} // namespace CP
+
+#endif // STANDALONEANALYSISALGORITHMS_XAODWRITERALG_H
diff --git a/PhysicsAnalysis/Algorithms/SystematicsHandles/CMakeLists.txt b/PhysicsAnalysis/Algorithms/SystematicsHandles/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..6fa55968831c29fa603107f3f8d099f5268dcbb2
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SystematicsHandles/CMakeLists.txt
@@ -0,0 +1,58 @@
+# Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+#
+# @author Nils Krumnack
+
+
+atlas_subdir( SystematicsHandles )
+
+atlas_depends_on_subdirs(
+   PUBLIC
+   Control/AthContainers
+   Control/AthToolSupport/AsgTools
+   PhysicsAnalysis/AnalysisCommon/PATInterfaces
+   PhysicsAnalysis/D3PDTools/AnaAlgorithm
+   Event/xAOD/xAODBase
+   Event/xAOD/xAODCore
+   Event/xAOD/xAODEventInfo
+   PRIVATE
+   Control/xAODRootAccess
+   Event/xAOD/xAODJet
+   Event/xAOD/xAODMuon
+   Event/xAOD/xAODEgamma
+   Event/xAOD/xAODTau
+   PhysicsAnalysis/D3PDTools/RootCoreUtils )
+
+atlas_add_library( SystematicsHandlesLib
+   SystematicsHandles/*.h SystematicsHandles/*.icc Root/*.cxx
+   PUBLIC_HEADERS SystematicsHandles
+   LINK_LIBRARIES AsgTools PATInterfaces AnaAlgorithmLib xAODBase xAODCore AthContainers xAODEventInfo
+   PRIVATE_LINK_LIBRARIES RootCoreUtils
+   xAODJet xAODMuon xAODEgamma xAODTau )
+
+atlas_add_test( cc_SysReadHandle
+   SOURCES test/cc_SysReadHandle.cxx
+   LINK_LIBRARIES AsgTools AnaAlgorithmLib SystematicsHandlesLib )
+
+atlas_add_test( cc_SysCopyHandle
+   SOURCES test/cc_SysCopyHandle.cxx
+   LINK_LIBRARIES AsgTools AnaAlgorithmLib xAODEventInfo xAODJet
+   SystematicsHandlesLib )
+
+if( XAOD_STANDALONE )
+   atlas_add_test( ut_CopyHelpers
+      SOURCES test/ut_CopyHelpers.cxx
+      LINK_LIBRARIES xAODRootAccess AsgTools xAODBase xAODJet
+      SystematicsHandlesLib )
+endif()
+
+atlas_add_test( cc_SysWriteHandle
+   SOURCES test/cc_SysWriteHandle.cxx
+   LINK_LIBRARIES AsgTools AnaAlgorithmLib SystematicsHandlesLib )
+
+atlas_add_test( cc_SysListHandle
+   SOURCES test/cc_SysListHandle.cxx
+   LINK_LIBRARIES AsgTools AnaAlgorithmLib SystematicsHandlesLib )
+
+atlas_add_test( cc_SysDecorationHandle
+   SOURCES test/cc_SysDecorationHandle.cxx
+   LINK_LIBRARIES AsgTools AnaAlgorithmLib SystematicsHandlesLib )
diff --git a/PhysicsAnalysis/Algorithms/SystematicsHandles/Root/CopyHelpers.cxx b/PhysicsAnalysis/Algorithms/SystematicsHandles/Root/CopyHelpers.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..91520513e1911f1dd759e4395db96152d872314b
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SystematicsHandles/Root/CopyHelpers.cxx
@@ -0,0 +1,69 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <SystematicsHandles/CopyHelpers.h>
+
+#include <xAODEgamma/PhotonContainer.h>
+#include <xAODEgamma/ElectronContainer.h>
+#include <xAODJet/JetContainer.h>
+#include <xAODMuon/MuonContainer.h>
+#include <xAODTau/TauJetContainer.h>
+#include <xAODTracking/TrackParticleContainer.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  namespace detail
+  {
+    StatusCode ShallowCopy<xAOD::IParticleContainer> ::
+    getCopy (MsgStream& msgStream, StoreType& store,
+             xAOD::IParticleContainer*& object,
+             const xAOD::IParticleContainer *inputObject,
+             const std::string& outputName, const std::string& auxName)
+    {
+      // this is probably not the best way to do this, but doing this
+      // the proper way will require an xAOD expert to do it.
+
+      // Set up a lambda for providing a msg(...) function.
+      const auto msg = [&] (MSG::Level lvl) -> MsgStream& {msgStream << lvl; return msgStream;};
+
+      // using a macro is a bit awkward, but doing this as a template
+      // is not practical either
+#define COPY(TYPE)                                                             \
+       do {                                                                    \
+          const TYPE *in = dynamic_cast< const TYPE* >( inputObject );         \
+          if( in ) {                                                           \
+             TYPE *out = nullptr;                                              \
+             ANA_CHECK( ShallowCopy<TYPE>::getCopy( msgStream, store, out, in, \
+                                                    outputName, auxName ) );   \
+             object = out;                                                     \
+             return StatusCode::SUCCESS;                                       \
+          }                                                                    \
+       } while( false )
+
+      COPY( xAOD::JetContainer );
+      COPY( xAOD::MuonContainer );
+      COPY( xAOD::ElectronContainer );
+      COPY( xAOD::PhotonContainer );
+      COPY( xAOD::TauJetContainer );
+      COPY(xAOD::TrackParticleContainer);
+
+#undef COPY
+
+      ANA_MSG_ERROR ("could not determine type to create shallow copy " << outputName);
+      ANA_MSG_ERROR ("please extend CopyHelpers.cxx with the appropriate type");
+      return StatusCode::FAILURE;
+    }
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/SystematicsHandles/Root/Helpers.cxx b/PhysicsAnalysis/Algorithms/SystematicsHandles/Root/Helpers.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..64e25257586b14a32fc6bec187390f036848b8e4
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SystematicsHandles/Root/Helpers.cxx
@@ -0,0 +1,56 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <SystematicsHandles/Helpers.h>
+
+#include <RootCoreUtils/StringUtil.h>
+#include <PATInterfaces/SystematicSet.h>
+#include <regex>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  std::string nominalSystematicsName ()
+  {
+    return "NOSYS";
+  }
+
+
+
+  std::string makeSystematicsName (const std::string& name,
+                                   const CP::SystematicSet& sys)
+  {
+    std::string sysName = sys.name();
+    if (sysName.empty())
+      sysName = nominalSystematicsName();
+    return RCU::substitute (name, "%SYS%", sysName);
+  }
+
+
+
+  std::string makeSystematicsName (const std::string& name,
+                                   const std::string& affecting,
+                                   const CP::SystematicSet& sys)
+  {
+    CP::SystematicSet mysys;
+    std::regex expr (affecting);
+
+    for (auto& var : sys)
+    {
+      if (regex_match (var.basename(), expr))
+        mysys.insert (var);
+    }
+    return makeSystematicsName (name, mysys);
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/SystematicsHandles/Root/ISysHandleBase.cxx b/PhysicsAnalysis/Algorithms/SystematicsHandles/Root/ISysHandleBase.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..67a269319eb27130b7f62c7ae61536b6f9d23d41
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SystematicsHandles/Root/ISysHandleBase.cxx
@@ -0,0 +1,20 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <SystematicsHandles/ISysHandleBase.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+}
diff --git a/PhysicsAnalysis/Algorithms/SystematicsHandles/Root/SysFilterReporter.cxx b/PhysicsAnalysis/Algorithms/SystematicsHandles/Root/SysFilterReporter.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..602f2d290aec669302f8c550177e70792f5267ce
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SystematicsHandles/Root/SysFilterReporter.cxx
@@ -0,0 +1,67 @@
+/*
+  Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <SystematicsHandles/SysFilterReporter.h>
+
+#include <SystematicsHandles/SysFilterReporterCombiner.h>
+#include <SystematicsHandles/SysFilterReporterParams.h>
+#include <AsgMessaging/MessageCheck.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  SysFilterReporter ::
+  SysFilterReporter (SysFilterReporterCombiner& val_combiner,
+                     const SystematicSet& val_sys)
+    : AsgMessagingForward (&val_combiner)
+    , m_combiner (val_combiner)
+    , m_sys (val_sys)
+    , m_passed (val_combiner.m_passedDefault)
+  {
+    ANA_CHECK_THROW (val_combiner.m_params.m_eventInfoHandle.getCopy (m_eventInfo, m_sys));
+  }
+
+
+
+  SysFilterReporter ::
+  ~SysFilterReporter () noexcept
+  {
+    ANA_MSG_DEBUG ("setting systematics-filter-passed flag to " << m_passed << " for " << m_sys.name());
+    if (m_passed)
+      m_combiner.m_passedOne = true;
+    else
+      m_combiner.m_passedAll = false;
+    // decorate event info
+    m_combiner.m_params.m_eventDecisionOutputDecoration.set (*m_eventInfo, m_passed, m_sys);
+
+    // only recording nominal event selection for now
+    if (m_passed && m_sys.empty())
+      m_combiner.m_params.m_passedNominal += 1;
+
+#ifndef XAOD_STANDALONE
+    // only recording nominal event selection for now
+    if (m_passed && m_sys.empty() && m_combiner.m_params.m_cutID != 0)
+      m_combiner.m_params.m_cutFlowSvc->addEvent (m_combiner.m_params.m_cutID);
+#endif
+  }
+
+
+
+  void SysFilterReporter ::
+  setPassed (bool val_passed) noexcept
+  {
+    ANA_MSG_DEBUG ("change systematics-filter-passed flag to " << m_passed << " for " << m_sys.name());
+    m_passed = val_passed;
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/SystematicsHandles/Root/SysFilterReporterCombiner.cxx b/PhysicsAnalysis/Algorithms/SystematicsHandles/Root/SysFilterReporterCombiner.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..eff665d98c889e94531beb14a19757f318e36c58
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SystematicsHandles/Root/SysFilterReporterCombiner.cxx
@@ -0,0 +1,54 @@
+/*
+  Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <SystematicsHandles/SysFilterReporterCombiner.h>
+
+#include <SystematicsHandles/SysFilterReporterParams.h>
+#include <AsgMessaging/MessageCheck.h>
+#include <exception>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  SysFilterReporterCombiner ::
+  SysFilterReporterCombiner (SysFilterReporterParams& val_params,
+                             SysListHandle& systematicsList,
+                             bool val_passedDefault)
+    : AsgMessagingForward (&val_params)
+    , m_params (val_params)
+    , m_passedDefault (val_passedDefault)
+  {
+    if (!m_params.m_isInitialized)
+    {
+      ANA_MSG_FATAL ("using uninitialized SysFilterReporterParams, throwing exception");
+      throw std::logic_error ("using uninitialized SysFilterReporterParams");
+    }
+
+    ANA_CHECK_THROW (m_params.m_eventDecisionOutputDecoration.preExecute(systematicsList));
+  }
+
+
+
+  SysFilterReporterCombiner ::
+  ~SysFilterReporterCombiner () noexcept
+  {
+    ANA_MSG_DEBUG ("setting algorithm-filter-passed flag to " << m_passedOne);
+    m_params.m_setFilterPassed (m_passedOne);
+    m_params.m_total += 1;
+    if (m_passedOne)
+      m_params.m_passedOne += 1;
+    if (m_passedAll)
+      m_params.m_passedAll += 1;
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/SystematicsHandles/Root/SysFilterReporterParams.cxx b/PhysicsAnalysis/Algorithms/SystematicsHandles/Root/SysFilterReporterParams.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..3daac19f9e8efa948e7f58b0559a05e7aaf4534a
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SystematicsHandles/Root/SysFilterReporterParams.cxx
@@ -0,0 +1,71 @@
+/*
+  Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <SystematicsHandles/SysFilterReporterParams.h>
+
+#include <AsgMessaging/MessageCheck.h>
+#include <AsgMessaging/StatusCode.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  StatusCode SysFilterReporterParams ::
+  initialize ()
+  {
+    if (m_isInitialized)
+    {
+      ANA_MSG_FATAL ("calling initialize twice on SysFilterReporterParams, aborting");
+      return StatusCode::FAILURE;
+    }
+
+    if (m_eventDecisionOutputDecoration.empty())
+    {
+      ANA_MSG_ERROR ("no event filter decoration name set");
+      return StatusCode::FAILURE;
+    }
+
+#ifndef XAOD_STANDALONE
+    if (!m_cutFlowSvc.empty())
+    {
+      ANA_CHECK (m_cutFlowSvc.retrieve());
+      m_cutID = m_cutFlowSvc->registerFilter (m_cutFlowSvc.parentName(), m_filterDescription + " (nominal only)");
+      if (m_cutID == 0)
+      {
+        ANA_MSG_ERROR ("problem registering myself with cutflow-svc");
+        return StatusCode::FAILURE;
+      }
+    }
+#endif
+
+    m_isInitialized = true;
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode SysFilterReporterParams ::
+  finalize ()
+  {
+    if (!m_isInitialized)
+    {
+      ANA_MSG_FATAL ("using unitialized SysFilterReporterParams, aborting");
+      return StatusCode::FAILURE;
+    }
+
+    ATH_MSG_INFO ("Events passing selection for at least one systematic: " << m_passedOne << " / " << m_total << " for " << m_filterDescription);
+    ATH_MSG_INFO ("Events passing selection for at nominal: " << m_passedNominal << " / " << m_total << " for " << m_filterDescription);
+    ATH_MSG_INFO ("Events passing selection for all systematics: " << m_passedAll << " / " << m_total << " for " << m_filterDescription);
+    return StatusCode::SUCCESS;
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/SystematicsHandles/Root/SysListHandle.cxx b/PhysicsAnalysis/Algorithms/SystematicsHandles/Root/SysListHandle.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..ccd200164bc7c0d63bc160d19b8689f570932a72
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SystematicsHandles/Root/SysListHandle.cxx
@@ -0,0 +1,135 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <SystematicsHandles/SysListHandle.h>
+
+#include <AsgTools/MessageCheck.h>
+#include <AsgTools/StatusCode.h>
+#include <PATInterfaces/SystematicSet.h>
+#include <SystematicsHandles/ISysHandleBase.h>
+#include <regex>
+
+#include <stdexcept>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  void SysListHandle ::
+  addHandle (ISysHandleBase& handle)
+  {
+    assert (!isInitialized());
+    m_sysHandles.push_back (&handle);
+  }
+
+
+
+  StatusCode SysListHandle ::
+  addAffectingSystematics (const CP::SystematicSet& affectingSystematics)
+  {
+    assert (!isInitialized());
+    bool failures = false;
+    std::regex expr (m_affectingRegex);
+    for (const CP::SystematicVariation& sys : affectingSystematics)
+    {
+      if (!regex_match (sys.basename(), expr))
+      {
+        ANA_MSG_ERROR ("algorithm reports affecting systematic " << sys << " which doesn't match user supplied pattern " << m_affectingRegex);
+        failures = true;
+      }
+    }
+    if (failures)
+      return StatusCode::FAILURE;
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  ::StatusCode SysListHandle ::
+  initialize ()
+  {
+    m_evtStore = m_evtStoreGetter();
+    m_isInitialized = true;
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  std::unordered_set<CP::SystematicSet> SysListHandle ::
+  systematicsVector ()
+  {
+    assert (isInitialized());
+
+    if (m_fullAffecting.empty())
+    {
+      std::string affecting = m_affectingRegex;
+      for (ISysHandleBase *handle : m_sysHandles)
+      {
+        std::string subAffecting = handle->getInputAffecting ();
+        if (!subAffecting.empty())
+        {
+          if (!affecting.empty())
+            affecting += "|";
+          affecting += subAffecting;
+        }
+      }
+      if (affecting.empty())
+        affecting = "^$";
+      m_fullAffecting = std::move (affecting);
+    }
+
+    const SysListType *systematicsList = nullptr;
+    ANA_CHECK_THROW (m_evtStore->retrieve (systematicsList, m_systematicsListName));
+
+    std::unordered_set<CP::SystematicSet> mysysList;
+    for (const auto& sys : *systematicsList)
+    {
+      auto iter = m_affectingCache.find (sys);
+      if (iter != m_affectingCache.end())
+      {
+        mysysList.insert (iter->second);
+      } else
+      {
+        CP::SystematicSet mysys;
+        std::regex affecting (m_fullAffecting);
+
+        for (const CP::SystematicVariation& subsys : sys)
+        {
+          if (regex_match (subsys.basename(),affecting))
+            mysys.insert (subsys);
+        }
+        m_affectingCache.insert (std::make_pair (sys, mysys));
+        mysysList.insert (mysys);
+      }
+    }
+    return mysysList;
+  }
+
+
+
+  StatusCode SysListHandle ::
+  foreach (const std::function<StatusCode(const CP::SystematicSet&)>& func)
+  {
+    assert (isInitialized());
+
+    for (const CP::SystematicSet& sys : systematicsVector())
+    {
+      if (func (sys).isFailure())
+      {
+        ANA_MSG_ERROR ("failed to evaluate algorithm for systematic " << sys.name());
+        return StatusCode::FAILURE;
+      }
+    }
+    return StatusCode::SUCCESS;
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/SystematicsHandles/Root/SysListType.cxx b/PhysicsAnalysis/Algorithms/SystematicsHandles/Root/SysListType.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..c352f7aff01b453f98f6f12b628bf67d3b3ea633
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SystematicsHandles/Root/SysListType.cxx
@@ -0,0 +1,27 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <SystematicsHandles/SysListType.h>
+
+#include <string>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  const std::string& sysListDefaultName ()
+  {
+    static const std::string result = "systematics";
+    return result;
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/CopyHelpers.h b/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/CopyHelpers.h
new file mode 100644
index 0000000000000000000000000000000000000000..c2eb45959410e9c270828132d1dc2da2ae38582f
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/CopyHelpers.h
@@ -0,0 +1,315 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef SYSTEMATICS_HANDLES__COPY_HELPERS_H
+#define SYSTEMATICS_HANDLES__COPY_HELPERS_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <AsgTools/MessageCheck.h>
+#include <AsgTools/StatusCode.h>
+#include <xAODBase/IParticleContainer.h>
+#include <xAODBase/IParticleHelpers.h>
+#include <xAODCore/ShallowCopy.h>
+
+#include <memory>
+
+namespace CP
+{
+  namespace detail
+  {
+    /// \brief check what kind of object/container the argument is
+    template <typename T>  
+    struct ContainerType
+    {
+      template <class, class> class checker;
+
+      template <typename C>
+      static std::true_type test_iparticle(checker<C, decltype((*(const xAOD::IParticle**)nullptr) = ((C*)nullptr)->at(0))> *);
+
+      template <typename C>
+      static std::false_type test_iparticle(...);
+
+      template <typename C>
+      static std::true_type test_container(checker<C, decltype((*(const SG::AuxVectorBase**)nullptr) = ((C*)nullptr))>*);
+
+      template <typename C>
+      static std::false_type test_container(...);
+
+      /// Value evaluating to:
+      ///  - 1 for xAOD::IParticleContainer types;
+      ///  - 2 for other DataVector types;
+      ///  - 3 for non-vector types.
+      static const int value = ( std::is_same< std::true_type, decltype( test_iparticle< T >( nullptr ) ) >::value ?
+                                 1 : ( std::is_same< std::true_type, decltype( test_container< T >( nullptr ) ) >::value ?
+                                       2 : 3 ) );
+    };
+
+    /// \brief a helper class to create shallow copies and register
+    /// them in the event store
+    ///
+    /// The main purpose of this class is to make it (fairly)
+    /// straightforward to do partial specializations for base classes
+    /// which need special handling to register their objects with the
+    /// correct type.
+    template<typename T, int type = ContainerType<T>::value>
+    struct ShallowCopy
+    {
+      static_assert ((type==1)||(type==2)||(type==3),
+                     "Type can not be shallow copied");
+    };
+
+    template<typename T>
+    struct ShallowCopy<T,1>
+    {
+      /// \brief the type of the event store we use
+    public:
+      typedef std::decay<decltype(*((EL::AnaAlgorithm*)0)->evtStore())>::type
+        StoreType;
+
+      static StatusCode
+      getCopy (MsgStream& msgStream, StoreType& store,
+               T*& object, const T *inputObject,
+               const std::string& outputName, const std::string& auxName)
+      {
+        // Define the msg(...) function as a lambda.
+        const auto msg = [&] (MSG::Level lvl) -> MsgStream& {msgStream << lvl;
+                                                             return msgStream;};
+
+        // Make sure that we get a valid pointer.
+        assert (inputObject != nullptr);
+
+        // Handle the case when the input object is a view container.
+        if( ! inputObject->getConstStore() ) {
+
+           // Decide how to handle the container.
+           if( inputObject->size() ) {
+              // Get the pointer to the "owning container" from the first
+              // element.
+              const T* originContainer =
+                 dynamic_cast< const T* >( ( *inputObject )[ 0 ]->container() );
+              // Make sure that every element in the view container has the same
+              // parent.
+              for( size_t i = 1; i < inputObject->size(); ++i ) {
+                 if( ( *inputObject )[ i ]->container() != originContainer ) {
+                    ANA_MSG_ERROR( "Not all elements of the received view "
+                                   "container come from the same container!" );
+                    return StatusCode::FAILURE;
+                 }
+              }
+              // Postfix for the shallow-copy container of the origin container.
+              static const char* ORIGIN_POSTFIX = "_ShallowCopyOrigin";
+              // Make a shallow copy of the origin container.
+              auto originCopy = xAOD::shallowCopyContainer( *originContainer );
+              if( ( ! originCopy.first ) || ( ! originCopy.second ) ) {
+                 ANA_MSG_ERROR( "Failed to shallow copy the origin of a view "
+                                << "container, meant for: " << outputName );
+                 return StatusCode::FAILURE;
+              }
+              // ...and record it.
+              ANA_CHECK( store.record( originCopy.first,
+                                       outputName + ORIGIN_POSTFIX ) );
+              ANA_CHECK( store.record( originCopy.second,
+                                       outputName + ORIGIN_POSTFIX + "Aux." ) );
+              // Make a view copy on top of it.
+              auto viewCopy = std::make_unique< T >( SG::VIEW_ELEMENTS );
+              auto viewCopyPtr = viewCopy.get();
+              for( const auto* element : *inputObject ) {
+                 viewCopy->push_back( originCopy.first->at( element->index() ) );
+              }
+              // Set the origin links on it. Note that
+              // xAOD::setOriginalObjectLink's "container version" doesn't work
+              // with view containers, we have to call this function one-by-one
+              // on the elements.
+              for( size_t i = 0; i < inputObject->size(); ++i ) {
+                 if( ! xAOD::setOriginalObjectLink( *( ( *inputObject )[ i ] ),
+                                                    *( ( *viewCopy )[ i ] ) ) ) {
+                    return StatusCode::FAILURE;
+                 }
+              }
+              // Finally, record the view container with the requested name.
+              ANA_CHECK( store.record( viewCopy.release(), outputName ) );
+              // The copy is done.
+              object = viewCopyPtr;
+              return StatusCode::SUCCESS;
+           } else {
+              // If the container was empty, then let's just make a new empty
+              // container, and that's that...
+              auto viewCopy = std::make_unique< T >( SG::VIEW_ELEMENTS );
+              auto viewCopyPtr = viewCopy.get();
+              ANA_CHECK( store.record( viewCopy.release(), outputName ) );
+              // The copy is done.
+              object = viewCopyPtr;
+              return StatusCode::SUCCESS;
+           }
+
+        } else {
+
+           // We can just copy the container as is.
+           auto copy = xAOD::shallowCopyContainer( *inputObject );
+           if (!copy.first || !copy.second)
+           {
+              ANA_MSG_ERROR ("failed to shallow copy object: " << outputName);
+              ANA_MSG_ERROR ("likely shallow copying a view container");
+              return StatusCode::FAILURE;
+           }
+
+           if (!xAOD::setOriginalObjectLink (*inputObject, *copy.first)) {
+              return StatusCode::FAILURE;
+           }
+
+           ANA_CHECK (store.record (copy.second, auxName));
+           ANA_CHECK (store.record (copy.first, outputName));
+           object = copy.first;
+           return StatusCode::SUCCESS;
+        }
+      }
+    };
+
+    template<typename T>
+    struct ShallowCopy<T,2>
+    {
+      /// \brief the type of the event store we use
+    public:
+      typedef std::decay<decltype(*((EL::AnaAlgorithm*)0)->evtStore())>::type
+      StoreType;
+
+      static StatusCode
+      getCopy (MsgStream& msgStream, StoreType& store,
+               T*& object, const T *inputObject,
+               const std::string& outputName, const std::string& auxName)
+      {
+         // Define the msg(...) function as a lambda.
+         const auto msg = [&] (MSG::Level lvl) -> MsgStream& {msgStream << lvl;
+            return msgStream;};
+
+         // Make sure that we get a valid pointer.
+         assert (inputObject != nullptr);
+
+         // Handle the case when the input object is a view container.
+         if( ! inputObject->getConstStore() ) {
+
+            // Decide how to handle the container.
+            if( inputObject->size() ) {
+               // Get the pointer to the "owning container" from the first
+               // element.
+               const T* originContainer =
+                 dynamic_cast< const T* >( ( *inputObject )[ 0 ]->container() );
+               // Make sure that every element in the view container has the same
+               // parent.
+               for( size_t i = 1; i < inputObject->size(); ++i ) {
+                  if( ( *inputObject )[ i ]->container() != originContainer ) {
+                     ANA_MSG_ERROR( "Not all elements of the received view "
+                                    "container come from the same container!" );
+                     return StatusCode::FAILURE;
+                  }
+               }
+               // Postfix for the shallow-copy container of the origin container.
+               static const char* ORIGIN_POSTFIX = "_ShallowCopyOrigin";
+               // Make a shallow copy of the origin container.
+               auto originCopy = xAOD::shallowCopyContainer( *originContainer );
+               if( ( ! originCopy.first ) || ( ! originCopy.second ) ) {
+                  ANA_MSG_ERROR( "Failed to shallow copy the origin of a view "
+                                 << "container, meant for: " << outputName );
+                  return StatusCode::FAILURE;
+               }
+               // ...and record it.
+               ANA_CHECK( store.record( originCopy.first,
+                                        outputName + ORIGIN_POSTFIX ) );
+               ANA_CHECK( store.record( originCopy.second,
+                                        outputName + ORIGIN_POSTFIX +
+                                        "Aux." ) );
+               // Make a view copy on top of it.
+               auto viewCopy = std::make_unique< T >( SG::VIEW_ELEMENTS );
+               auto viewCopyPtr = viewCopy.get();
+               for( const auto* element : *inputObject ) {
+                  viewCopy->push_back( originCopy.first->at( element->index() ) );
+               }
+               // Finally, record the view container with the requested name.
+               ANA_CHECK( store.record( viewCopy.release(), outputName ) );
+               // The copy is done.
+               object = viewCopyPtr;
+               return StatusCode::SUCCESS;
+            } else {
+               // If the container was empty, then let's just make a new empty
+               // container, and that's that...
+               auto viewCopy = std::make_unique< T >( SG::VIEW_ELEMENTS );
+               auto viewCopyPtr = viewCopy.get();
+               ANA_CHECK( store.record( viewCopy.release(), outputName ) );
+               // The copy is done.
+               object = viewCopyPtr;
+               return StatusCode::SUCCESS;
+              }
+
+         } else {
+
+            // We can just copy the container as is.
+            auto copy = xAOD::shallowCopyContainer( *inputObject );
+            if (!copy.first || !copy.second)
+            {
+               ANA_MSG_ERROR ("failed to shallow copy object: " << outputName);
+               ANA_MSG_ERROR ("likely shallow copying a view container");
+               return StatusCode::FAILURE;
+            }
+
+            ANA_CHECK (store.record (copy.second, auxName));
+            ANA_CHECK (store.record (copy.first, outputName));
+            object = copy.first;
+            return StatusCode::SUCCESS;
+         }
+      }
+    };
+
+    template<typename T>
+    struct ShallowCopy<T,3>
+    {
+       /// \brief the type of the event store we use
+    public:
+       typedef std::decay<decltype(*((EL::AnaAlgorithm*)0)->evtStore())>::type
+          StoreType;
+
+       static StatusCode
+       getCopy (MsgStream& msgStream, StoreType& store,
+                T*& object, const T *inputObject,
+                const std::string& outputName, const std::string& auxName)
+       {
+          // Define the msg(...) function as a lambda.
+          const auto msg = [&] (MSG::Level lvl) -> MsgStream& {msgStream << lvl;
+             return msgStream;};
+
+          // We can just copy the object as is.
+          auto copy = xAOD::shallowCopyObject( *inputObject );
+          if (!copy.first || !copy.second)
+          {
+             ANA_MSG_ERROR ("failed to shallow copy object: " << outputName);
+             ANA_MSG_ERROR ("likely shallow copying a view container");
+             return StatusCode::FAILURE;
+          }
+
+          ANA_CHECK (store.record (copy.second, auxName));
+          ANA_CHECK (store.record (copy.first, outputName));
+          object = copy.first;
+          return StatusCode::SUCCESS;
+       }
+    };
+
+    template<>
+    struct ShallowCopy<xAOD::IParticleContainer>
+    {
+      /// \brief the type of the event store we use
+    public:
+      typedef std::decay<decltype(*((EL::AnaAlgorithm*)0)->evtStore())>::type StoreType;
+
+      static StatusCode
+      getCopy (MsgStream& msgStream, StoreType& store,
+               xAOD::IParticleContainer*& object,
+               const xAOD::IParticleContainer *inputObject,
+               const std::string& outputName, const std::string& auxName);
+    };
+  }
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/Helpers.h b/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/Helpers.h
new file mode 100644
index 0000000000000000000000000000000000000000..702564bf452c0c16bf56e43eb36aaed2f0998b5f
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/Helpers.h
@@ -0,0 +1,41 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef SYSTEMATICS_HANDLES__HELPERS_H
+#define SYSTEMATICS_HANDLES__HELPERS_H
+
+#include <string>
+
+namespace CP
+{
+  class SystematicSet;
+
+  /// \brief nominal systematics name
+  std::string nominalSystematicsName ();
+
+
+  /// \brief make the name for the given systematics
+  /// \par Guarantee
+  ///   strong
+  /// \par Failures
+  ///   out of memory II
+  std::string makeSystematicsName (const std::string& name,
+                                   const CP::SystematicSet& sys);
+
+
+  /// \brief make the name for the given systematics, filtered for
+  /// the given affecting pattern
+  /// \par Guarantee
+  ///   strong
+  /// \par Failures
+  ///   out of memory II
+  std::string makeSystematicsName (const std::string& name,
+                                   const std::string& affecting,
+                                   const CP::SystematicSet& sys);
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/ISysHandleBase.h b/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/ISysHandleBase.h
new file mode 100644
index 0000000000000000000000000000000000000000..8aeb9efaa51d833324d62f1a623ab97075acf90e
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/ISysHandleBase.h
@@ -0,0 +1,40 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef SYSTEMATICS_HANDLES__I_SYS_HANDLE_BASE_H
+#define SYSTEMATICS_HANDLES__I_SYS_HANDLE_BASE_H
+
+#include <string>
+
+namespace CP
+{
+  /// \brief a basic interface for all systematics handles
+  ///
+  /// This is used by \ref SysListHandle to communicate with the
+  /// individual data handles.
+
+  class ISysHandleBase
+  {
+    /// \brief standard (virtual) destructor
+  public:
+    virtual ~ISysHandleBase () noexcept = default;
+
+    /// \brief get the regular expression for the affecting
+    /// systematics if this is an input handle
+    ///
+    /// This returns the empty string if this is either not an input
+    /// handle, or if the input handle is not affected by systematics.
+    ///
+    /// This should only be called inside execute(), not initialize(),
+    /// to allow upgrades to reading the affecting systematics from
+    /// the event store.
+  public:
+    virtual std::string getInputAffecting () const = 0;
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysCopyHandle.h b/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysCopyHandle.h
new file mode 100644
index 0000000000000000000000000000000000000000..7f7e5f62705e922410d366cb6ead68fe31ecf60b
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysCopyHandle.h
@@ -0,0 +1,110 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef SYSTEMATICS_HANDLES__SYS_COPY_HANDLE_H
+#define SYSTEMATICS_HANDLES__SYS_COPY_HANDLE_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <AsgTools/AsgMessagingForward.h>
+#include <PATInterfaces/SystematicSet.h>
+#include <SystematicsHandles/ISysHandleBase.h>
+#include <string>
+#include <tuple>
+#include <unordered_map>
+
+class StatusCode;
+
+namespace CP
+{
+  class SystematicSet;
+
+  /// \brief a data handle for copying systematics varied input data
+
+  template<typename T> class SysCopyHandle final
+    : public ISysHandleBase, public asg::AsgMessagingForward
+  {
+    //
+    // public interface
+    //
+
+    /// \brief standard constructor
+  public:
+    template<typename T2>
+    SysCopyHandle (T2 *owner, const std::string& propertyName,
+                   const std::string& propertyValue,
+                   const std::string& propertyDescription);
+
+
+    /// \brief whether we have a name configured
+  public:
+    bool empty () const noexcept;
+
+    /// \brief !empty()
+  public:
+    explicit operator bool () const noexcept;
+
+
+    /// \brief retrieve the object for the given name
+  public:
+    ::StatusCode getCopy (T*& object,
+                          const CP::SystematicSet& sys) const;
+
+
+
+    //
+    // inherited interface
+    //
+
+  public:
+    virtual std::string getInputAffecting () const override;
+
+
+
+    //
+    // private interface
+    //
+
+    /// \brief the input name we use
+  private:
+    std::string m_inputName;
+
+    /// \brief the regular expression for affecting systematics
+  private:
+    std::string m_affectingRegex {".*"};
+
+    /// \brief the output name we use
+  private:
+    std::string m_outputName;
+
+    /// \brief the cache of names we use
+  private:
+    mutable std::unordered_map<CP::SystematicSet,std::tuple<std::string,std::string,std::string> > m_nameCache;
+
+
+    /// \brief the type of the event store we use
+  private:
+    typedef std::decay<decltype(*((EL::AnaAlgorithm*)0)->evtStore())>::type StoreType;
+
+    /// \brief the event store we use
+  private:
+    mutable StoreType *m_evtStore = nullptr;
+
+    /// \brief the function to retrieve the event store
+    ///
+    /// This is an std::function to allow the parent to be either a
+    /// tool or an algorithm.  Though we are not really supporting
+    /// tools as parents when using \ref SysListHandle, so in
+    /// principle this could be replaced with a pointer to the
+    /// algorithm instead.
+  private:
+    std::function<StoreType*()> m_evtStoreGetter;
+  };
+}
+
+#include "SysCopyHandle.icc"
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysCopyHandle.icc b/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysCopyHandle.icc
new file mode 100644
index 0000000000000000000000000000000000000000..e41dd1c55ca1af023168ac5525cfddab231d3101
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysCopyHandle.icc
@@ -0,0 +1,95 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <AsgTools/MessageCheck.h>
+#include <SystematicsHandles/CopyHelpers.h>
+#include <SystematicsHandles/Helpers.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  template<typename T> template<typename T2> SysCopyHandle<T> ::
+  SysCopyHandle (T2 *owner, const std::string& propertyName,
+                 const std::string& propertyValue,
+                 const std::string& propertyDescription)
+    : AsgMessagingForward (owner), m_inputName (propertyValue)
+    , m_evtStoreGetter ([owner] () {return &*owner->evtStore();})
+  {
+    owner->declareProperty (propertyName, m_inputName, propertyDescription);
+    owner->declareProperty (propertyName + "Regex", m_affectingRegex, "affecting systematics for " + propertyDescription);
+    owner->declareProperty (propertyName + "Out", m_outputName, "name to which to copy " + propertyName);
+  }
+
+
+
+  template<typename T> bool SysCopyHandle<T> ::
+  empty () const noexcept
+  {
+    return m_inputName.empty();
+  }
+
+
+
+  template<typename T> SysCopyHandle<T> ::
+  operator bool () const noexcept
+  {
+    return !m_inputName.empty();
+  }
+
+
+
+  template<typename T> ::StatusCode SysCopyHandle<T> ::
+  getCopy (T*& object, const CP::SystematicSet& sys) const
+  {
+    auto cache = m_nameCache.find (sys);
+    if (cache == m_nameCache.end())
+    {
+      std::string inputName = makeSystematicsName (m_inputName, m_affectingRegex, sys);
+      std::string outputName = makeSystematicsName (m_outputName, sys);
+      ANA_MSG_DEBUG ("SysCopyHandle: " << inputName << " -> " << outputName << " (" << sys.name() << ")");
+      m_nameCache.insert (std::make_pair (sys, std::make_tuple (inputName, outputName, outputName+"Aux.")));
+      cache = m_nameCache.find (sys);
+      assert (cache != m_nameCache.end());
+      if (!m_evtStore)
+        m_evtStore = m_evtStoreGetter();
+    }
+    assert (m_evtStore);
+    if (std::get<1>(cache->second).empty())
+    {
+      // if no output name is configured, act like an update handle
+      return m_evtStore->retrieve (object, std::get<0>(cache->second));
+    } else
+    {
+      // if an output name is configured, retrieve the input object as
+      // a const object, (shallow) copy it, record the copy and return
+      // it.
+
+      const T *inputObject = nullptr;
+      if (m_evtStore->retrieve (inputObject, std::get<0>(cache->second)).isFailure())
+        return StatusCode::FAILURE;
+
+      return detail::ShallowCopy<T>::getCopy
+        (msg(), *m_evtStore, object, inputObject,
+         std::get<1>(cache->second), std::get<2>(cache->second));
+    }
+  }
+
+
+
+  template<typename T> std::string SysCopyHandle<T> ::
+  getInputAffecting () const
+  {
+    return m_affectingRegex;
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysDecorationHandle.h b/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysDecorationHandle.h
new file mode 100644
index 0000000000000000000000000000000000000000..5c736c3aaf41a9b2b2f690b12a0d018aede58a77
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysDecorationHandle.h
@@ -0,0 +1,134 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef SYSTEMATICS_HANDLES__SYS_DECORATION_HANDLE_H
+#define SYSTEMATICS_HANDLES__SYS_DECORATION_HANDLE_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <AsgTools/AsgMessagingForward.h>
+#include <AthContainers/AuxElement.h>
+#include <PATInterfaces/SystematicSet.h>
+#include <SystematicsHandles/ISysHandleBase.h>
+#include <SystematicsHandles/SysListHandle.h>
+#include <string>
+#include <type_traits>
+#include <unordered_map>
+
+class StatusCode;
+
+namespace CP
+{
+  class SystematicSet;
+
+  /// \brief the decoration value to use if there is no valid scale
+  /// factor decoration
+  constexpr float invalidScaleFactor () {return -1;}
+
+  /// \brief the decoration value to use if there is no valid
+  /// efficiency decoration
+  constexpr float invalidEfficiency () {return -1;}
+
+
+  /// \brief a data handle for reading systematics varied input data
+
+  template<typename T> class SysDecorationHandle final
+    : public ISysHandleBase, public asg::AsgMessagingForward
+  {
+    //
+    // public interface
+    //
+
+    /// \brief standard constructor
+  public:
+    template<typename T2>
+    SysDecorationHandle (T2 *owner, const std::string& propertyName,
+                             const std::string& propertyValue,
+                             const std::string& propertyDescription);
+
+
+    /// \brief register a set of used systematics for in the current
+    /// execut call (usually obtained from \ref CP::SystListHandle)
+  public:
+    StatusCode preExecute (SysListHandle &systematics);
+
+
+    /// \brief whether \ref preExecute has been called successfully at least once
+  public:
+    bool isPrepared () const noexcept;
+
+
+    /// \brief whether we have a name configured
+  public:
+    bool empty () const noexcept;
+
+    /// \brief !empty()
+  public:
+    explicit operator bool () const noexcept;
+
+
+    /// \brief get the name we retrieve from the event store
+  public:
+    const std::string& getName (const CP::SystematicSet& sys) const;
+
+
+    /// \brief retrieve the object decoration for the given systematic
+  public:
+    const T& get (const SG::AuxElement& object,
+                  const CP::SystematicSet& sys) const;
+
+    /// \brief check if the object decoration is available
+  public:
+    bool isAvailable (const SG::AuxElement& object,
+                      const CP::SystematicSet& sys) const;
+
+
+    /// \brief set the object decoration for the given systematic
+  public:
+    void set (SG::AuxElement& object, const T& value,
+              const CP::SystematicSet& sys) const;
+
+
+
+    //
+    // inherited interface
+    //
+
+  public:
+    virtual std::string getInputAffecting () const override;
+
+
+
+    //
+    // private interface
+    //
+
+    /// \brief the value of \ref isPrepared
+  private:
+    bool m_isPrepared{false};
+
+    /// \brief the input name we use
+  private:
+    std::string m_inputName;
+
+    /// \brief the regular expression for affecting systematics
+  private:
+    std::string m_affectingRegex {".*"};
+
+    /// \brief the cache of names we use
+  private:
+    mutable std::unordered_map<CP::SystematicSet,std::tuple<std::string,SG::AuxElement::ConstAccessor<T>,SG::AuxElement::Accessor<T> > > m_dataCache;
+
+    /// \brief get the data for the given systematics
+  private:
+    const std::tuple<std::string,SG::AuxElement::ConstAccessor<T>,SG::AuxElement::Accessor<T> >&
+    getData (const CP::SystematicSet& sys) const;
+  };
+}
+
+#include "SysDecorationHandle.icc"
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysDecorationHandle.icc b/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysDecorationHandle.icc
new file mode 100644
index 0000000000000000000000000000000000000000..13a59deed6ecdfac643b907c3b40cab0634437d8
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysDecorationHandle.icc
@@ -0,0 +1,152 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <AsgTools/MessageCheck.h>
+#include <AsgTools/StatusCode.h>
+#include <SystematicsHandles/Helpers.h>
+
+#include <regex>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  template<typename T> template<typename T2> SysDecorationHandle<T> ::
+  SysDecorationHandle (T2 *owner, const std::string& propertyName,
+                 const std::string& propertyValue,
+                 const std::string& propertyDescription)
+    : AsgMessagingForward (owner), m_inputName (propertyValue)
+  {
+    owner->declareProperty (propertyName, m_inputName, propertyDescription);
+    owner->declareProperty (propertyName + "Regex", m_affectingRegex, "affecting systematics for " + propertyDescription);
+  }
+
+
+
+  template<typename T> ::StatusCode SysDecorationHandle<T> ::
+  preExecute (SysListHandle &systematics)
+  {
+    if (isPrepared())
+    {
+      return StatusCode::SUCCESS;
+    }
+
+    ANA_CHECK (systematics.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode
+    {
+      getData (sys);
+      return StatusCode::SUCCESS;
+    }));
+
+    m_isPrepared = true;
+
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  template<typename T> inline bool SysDecorationHandle<T> ::
+  isPrepared () const noexcept
+  {
+    return m_isPrepared;
+  }
+
+
+
+  template<typename T> bool SysDecorationHandle<T> ::
+  empty () const noexcept
+  {
+    return m_inputName.empty();
+  }
+
+
+
+  template<typename T> SysDecorationHandle<T> ::
+  operator bool () const noexcept
+  {
+    return !m_inputName.empty();
+  }
+
+
+
+  template<typename T> const std::string& SysDecorationHandle<T> ::
+  getName (const CP::SystematicSet& sys) const
+  {
+    assert (isPrepared());
+
+    const auto& data = getData (sys);
+    return std::get<0> (data);
+  }
+
+
+
+  template<typename T> const T& SysDecorationHandle<T> ::
+  get (const SG::AuxElement& object,
+       const CP::SystematicSet& sys) const
+  {
+    assert (isPrepared());
+
+    const auto& data = getData (sys);
+    return std::get<1> (data) (object);
+  }
+
+
+
+  template<typename T> bool SysDecorationHandle<T> ::
+  isAvailable (const SG::AuxElement& object,
+       const CP::SystematicSet& sys) const
+  {
+    assert (isPrepared());
+
+    const auto& data = getData (sys);
+    return std::get<1> (data).isAvailable(object);
+  }
+
+
+
+  template<typename T> void SysDecorationHandle<T> ::
+  set (SG::AuxElement& object, const T& value,
+       const CP::SystematicSet& sys) const
+  {
+    assert (isPrepared());
+
+    const auto& data = getData (sys);
+    std::get<2> (data) (object) = value;
+  }
+
+
+
+  template<typename T> std::string SysDecorationHandle<T> ::
+  getInputAffecting () const
+  {
+    return m_affectingRegex;
+  }
+
+
+
+  template<typename T> const std::tuple<std::string,SG::AuxElement::ConstAccessor<T>,SG::AuxElement::Accessor<T> >&
+  SysDecorationHandle<T> ::
+  getData (const CP::SystematicSet& sys) const
+  {
+    auto cache = m_dataCache.find (sys);
+    if (cache == m_dataCache.end())
+    {
+      std::string newName = makeSystematicsName
+        (m_inputName, m_affectingRegex, sys);
+      ANA_MSG_DEBUG ("SysDecorationHandle: " << newName << " (" << sys.name() << ")");
+      m_dataCache.insert (std::make_pair (sys, std::make_tuple (newName, newName, newName)));
+      cache = m_dataCache.find (sys);
+      assert (cache != m_dataCache.end());
+    }
+    return cache->second;
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysFilterReporter.h b/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysFilterReporter.h
new file mode 100644
index 0000000000000000000000000000000000000000..57042a5feccd307a77b4dfdd250610cc2e34a9b1
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysFilterReporter.h
@@ -0,0 +1,96 @@
+/*
+  Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+#ifndef SYSTEMATICS_HANDLES__FILTER_REPORTER_H
+#define SYSTEMATICS_HANDLES__FILTER_REPORTER_H
+
+#include <AsgMessaging/AsgMessagingForward.h>
+#include <xAODEventInfo/EventInfo.h>
+
+namespace CP
+{
+  class SysFilterReporterCombiner;
+  class SystematicSet;
+
+  /// \brief a systematics aware filter reporter
+  ///
+  /// In order to ensure that the filter decision will always be set
+  /// as part of `execute()`, this reporter class should be used like
+  /// this:
+  ///
+  /// ```
+  /// StatusCode Foo::execute()
+  /// {
+  ///   ...
+  ///   static constexpr bool DEFAULT_DECISION = false;
+  ///   SysFilterReporterCombiner filterCombiner
+  ///       (m_filterParams, m_systematicsList, DEFAULT_DECISION);
+  ///
+  ///   return m_systematicsList.foreach ([&](const CP::SystematicSet &sys) -> StatusCode {
+  ///     SysFilterReporter filter (filterCombiner, sys);
+  ///     ...
+  ///     filter.setPassed ();
+  ///     return StatusCode::SUCCESS;
+  ///     });
+  /// }
+  /// ```
+
+  class SysFilterReporter final : public asg::AsgMessagingForward
+  {
+    //
+    // public interface
+    //
+
+    /// \brief standard constructor
+    /// \par Guarantee
+    ///   strong
+    /// \par Failures
+    ///   out of memory I
+  public:
+    SysFilterReporter (SysFilterReporterCombiner& val_combiner,
+                       const SystematicSet& val_sys);
+
+
+    /// \brief standard destructor
+    /// \par Guarantee
+    ///   no-fail
+  public:
+    ~SysFilterReporter () noexcept;
+
+
+    /// \brief report the filter decision
+    /// \par Guarantee
+    ///   no-fail
+  public:
+    void setPassed (bool val_passed = true) noexcept;
+
+
+
+    //
+    // private interface
+    //
+
+    /// \brief the \ref SysFilterReporterParams object
+  private:
+    SysFilterReporterCombiner& m_combiner;
+
+    /// \brief the \ref SystematicSet object we use
+  private:
+    const SystematicSet& m_sys;
+
+    /// \brief the event info object we are decorating
+  private:
+    xAOD::EventInfo *m_eventInfo {nullptr};
+
+    /// \brief the value of passed we will set
+  private:
+    bool m_passed {false};
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysFilterReporterCombiner.h b/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysFilterReporterCombiner.h
new file mode 100644
index 0000000000000000000000000000000000000000..481ee7be7ca85eedf90f3269e059946abf35d400
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysFilterReporterCombiner.h
@@ -0,0 +1,81 @@
+/*
+  Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+#ifndef SYSTEMATICS_HANDLES__FILTER_REPORTER_COMBINER_H
+#define SYSTEMATICS_HANDLES__FILTER_REPORTER_COMBINER_H
+
+#include <AsgMessaging/AsgMessagingForward.h>
+
+class EventContext;
+
+namespace CP
+{
+  class SysFilterReporterParams;
+  class SysListHandle;
+
+
+  /// \brief a reporter class that combines the filter decisions for
+  /// all systematics
+  ///
+  /// Unlike the non-systematics aware version, when dealing with
+  /// systematics we need to combine all the individual filter
+  /// decisions into an overall filter decision for the algorithm,
+  /// i.e. if and only if the filter decision fails for all
+  /// systematics we should set filter to failed for the algorithm
+  /// itself, so that we avoid/skip processing of subsequent
+  /// algorithms.  For details on usage see \ref SysFilterReporter.
+
+  class SysFilterReporterCombiner final : public asg::AsgMessagingForward
+  {
+    //
+    // public interface
+    //
+
+    /// \brief standard constructor
+    /// \par Guarantee
+    ///   strong
+    /// \par Failures
+    ///   out of memory I
+  public:
+    SysFilterReporterCombiner (SysFilterReporterParams& val_params,
+                               SysListHandle& systematicsList,
+                               bool val_passedDefault);
+
+
+    /// \brief standard destructor
+    /// \par Guarantee
+    ///   no-fail
+  public:
+    ~SysFilterReporterCombiner () noexcept;
+
+
+
+    //
+    // private interface
+    //
+
+    // this class does most of the actual work and acts as accessor to
+    // our private data members, and needs our internals for that.
+    friend class SysFilterReporter;
+
+    /// \brief the \ref SysFilterReporterParams object
+  private:
+    SysFilterReporterParams& m_params;
+
+    /// \brief the default value of passed we use for each individual reporter
+  private:
+    bool m_passedDefault {false};
+
+    /// \brief whether we passed for at least one/all systematics
+  private:
+    bool m_passedOne {false};
+    bool m_passedAll {true};
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysFilterReporterParams.h b/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysFilterReporterParams.h
new file mode 100644
index 0000000000000000000000000000000000000000..f73c74500627915b1ed40e0114053503a95dfda5
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysFilterReporterParams.h
@@ -0,0 +1,156 @@
+/*
+  Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+#ifndef SYSTEMATICS_HANDLES__FILTER_REPORTER_PARAMS_H
+#define SYSTEMATICS_HANDLES__FILTER_REPORTER_PARAMS_H
+
+#include <AsgMessaging/AsgMessagingForward.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <SystematicsHandles/SysDecorationHandle.h>
+#include <xAODEventInfo/EventInfo.h>
+#include <functional>
+
+#ifndef XAOD_STANDALONE
+#include <AthenaKernel/ICutFlowSvc.h>
+#include <GaudiKernel/ServiceHandle.h>
+#endif
+
+class StatusCode;
+
+namespace CP
+{
+  /// \brief the properties and persistent data for systematics aware
+  /// filter reporters
+  ///
+  /// This is a systematics-aware version of the \ref
+  /// EL::FilterReporter mechanism.  This is somewhat more
+  /// complicated, since there is per-systematics filter decisions and
+  /// an overall event-level filter decision, for details see \ref
+  /// SysFilterReporter.
+
+  class SysFilterReporterParams final : public asg::AsgMessagingForward
+  {
+    //
+    // public interface
+    //
+
+    /// \brief standard constructor
+    /// \par Guarantee
+    ///   strong
+    /// \par Failures
+    ///   out of memory I
+  public:
+    template<typename T>
+    explicit SysFilterReporterParams (T *owner, std::string val_filterDescription);
+
+
+    /// \brief do anything we need to do in initialize
+    /// \par Guarantee
+    ///   strong
+    /// \par Failures
+    ///   configuration/initialization errors
+  public:
+    StatusCode initialize ();
+
+
+    /// \brief do anything we need to do in finalize
+    /// \par Guarantee
+    ///   strong
+    /// \par Failures
+    ///   finalization errors
+  public:
+    StatusCode finalize ();
+
+
+
+    //
+    // private interface
+    //
+
+    // this class does most of the actual work and acts as accessor to
+    // our private data members, and needs our internals for that.
+    friend class SysFilterReporterCombiner;
+    friend class SysFilterReporter;
+
+    /// \brief the function to call setFilterPassed() on the algorithm
+    ///
+    /// This is using a `std::function` object, so as to avoid tying
+    /// this to a particular algorithm class.
+  private:
+    std::function<void(bool)> m_setFilterPassed;
+
+    /// \brief the event info we run on
+  private:
+    SysCopyHandle<xAOD::EventInfo> m_eventInfoHandle;
+
+    /// \brief the decoration for writing the event decision
+  private:
+    SysDecorationHandle<char> m_eventDecisionOutputDecoration;
+
+    /// \brief counter for passed events
+  private:
+    unsigned m_passedOne {0};
+    unsigned m_passedNominal {0};
+    unsigned m_passedAll {0};
+
+    /// \brief counter for total events
+  private:
+    unsigned m_total {0};
+
+    /// \brief whether the handle was initialized
+  private:
+    bool m_isInitialized {false};
+
+    /// \brief description what this filter does
+  private:
+    std::string m_filterDescription;
+
+
+#ifndef XAOD_STANDALONE
+
+    /// \brief the \ref CutIdentifier for this filter algorithm
+  private:
+    CutIdentifier m_cutID;
+
+    /// \brief the handle to the service holding tables of cut-flows
+    /// for filtering algs.
+  private:
+    ServiceHandle<ICutFlowSvc> m_cutFlowSvc;
+
+#endif
+  };
+
+
+
+  //
+  // inline/template methods
+  //
+
+  template<typename T> SysFilterReporterParams ::
+  SysFilterReporterParams (T *owner, std::string val_filterDescription)
+    : AsgMessagingForward (owner)
+    , m_setFilterPassed ([owner] (bool val_setFilterPassed) {owner->setFilterPassed (val_setFilterPassed);})
+    , m_eventInfoHandle (owner, "eventInfo", "EventInfo", "the event info object to run on")
+    , m_eventDecisionOutputDecoration (owner, "eventDecisionOutputDecoration", "", "the decoration for the event decision")
+    , m_filterDescription (std::move (val_filterDescription))
+#ifndef XAOD_STANDALONE
+    , m_cutFlowSvc ("CutFlowSvc/CutFlowSvc", owner->name())
+#endif
+  {
+    owner->declareProperty("FilterDescription", m_filterDescription,
+                           "describe to the cutflowsvc what this filter does.");
+
+#ifndef XAOD_STANDALONE
+    owner->declareProperty("CutFlowSvc", m_cutFlowSvc,
+                           "handle to the ICutFlowSvc instance this filtering algorithm"
+                           " will use for building the flow of cuts.");
+#endif
+  }
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysListHandle.h b/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysListHandle.h
new file mode 100644
index 0000000000000000000000000000000000000000..53ec9d7a325a7c329637ad0b715b528836274d7a
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysListHandle.h
@@ -0,0 +1,161 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef SYSTEMATICS_HANDLES__SYS_LIST_HANDLE_H
+#define SYSTEMATICS_HANDLES__SYS_LIST_HANDLE_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <AsgTools/AsgMessagingForward.h>
+#include <PATInterfaces/SystematicSet.h>
+#include <SystematicsHandles/SysListType.h>
+#include <functional>
+#include <string>
+#include <vector>
+#include <unordered_set>
+
+class StatusCode;
+
+namespace CP
+{
+  class ISysHandleBase;
+  class SystematicSet;
+
+  /// \brief a class managing the property to configure the list of
+  /// systematics to process
+
+  class SysListHandle : public asg::AsgMessagingForward
+  {
+    //
+    // public interface
+    //
+
+    /// \brief standard constructor
+  public:
+    template<typename T>
+    SysListHandle (T *owner, const std::string& propertyName = "systematics",
+                   const std::string& propertyDescription = "list of systematics to evaluate");
+
+
+    /// \brief register an input handle we are using
+    ///
+    /// This is currently a no-op, but it could be useful for a
+    /// variety of future directions we could take the systematics
+    /// handling.
+    ///
+    /// \pre !isInitialized()
+  public:
+    void addHandle (ISysHandleBase& handle);
+
+
+    /// \brief register a set of affecting variables for the current
+    /// algorithm (usually obtained from an \ref CP::ISystematicsTool)
+    ///
+    /// This is currently a no-op, but it could be useful for a
+    /// variety of future directions we could take the systematics
+    /// handling (or at least as a cross check of those).
+    ///
+    /// \pre !isInitialized()
+  public:
+    StatusCode addAffectingSystematics
+      (const CP::SystematicSet& affectingSystematics);
+
+
+    /// \brief intialize this property
+    ///
+    /// This should be called exactly once during initialize of the
+    /// owning algorithm.
+  public:
+    ::StatusCode initialize ();
+
+
+    /// \brief whether \ref initialize has been called successfully
+  public:
+    bool isInitialized () const noexcept;
+
+
+    /// \brief the list of systematics to loop over
+  public:
+    std::unordered_set<CP::SystematicSet> systematicsVector ();
+
+
+    /// \brief run the function for each systematic
+    ///
+    /// This allows to perform some amount of behind-the-scenes
+    /// optimizations in the future, which hopefully not creating too
+    /// many issues in the present.
+    ///
+    /// Technically this would be slightly more performant as a
+    /// template, but this is likely not to be an issue, and can still
+    /// be changed if it ever becomes an issue.
+    ///
+    /// Ideally this would be const, but the current version is not
+    /// thread-safe, so I'd rather not add a const qualifier to it.
+    ///
+    /// \par Guarantee
+    ///   basic
+    /// \par Failures
+    ///   function failures
+    /// \pre isInitialized()
+  public:
+    StatusCode foreach
+      (const std::function<StatusCode(const CP::SystematicSet&)>& func);
+
+
+
+    //
+    // private interface
+    //
+
+    /// \brief the name under which the systematics list is stored in
+    /// the event store
+  private:
+    std::string m_systematicsListName {"systematics"};
+
+    /// \brief the regular expression for affecting systematics
+  private:
+    std::string m_affectingRegex {"^$"};
+
+    /// \brief the full affecting systematics including the inputs
+  private:
+    std::string m_fullAffecting;
+
+    /// \brief the cache of affecting filtered systematics
+  private:
+    std::unordered_map<CP::SystematicSet,CP::SystematicSet> m_affectingCache;
+
+    /// \brief the list of systematics handles we have
+  private:
+    std::vector<ISysHandleBase*> m_sysHandles;
+
+    /// \brief the value of \ref isInitialized
+  private:
+    bool m_isInitialized = false;
+
+
+    /// \brief the type of the event store we use
+  private:
+    typedef std::decay<decltype(*((EL::AnaAlgorithm*)0)->evtStore())>::type StoreType;
+
+    /// \brief the event store we use
+  private:
+    mutable StoreType *m_evtStore = nullptr;
+
+    /// \brief the function to retrieve the event store
+    ///
+    /// This is an std::function to allow the parent to be either a
+    /// tool or an algorithm.  Though we are not really supporting
+    /// tools as parents when using \ref SysListHandle, so in
+    /// principle this could be replaced with a pointer to the
+    /// algorithm instead.
+  private:
+    std::function<StoreType*()> m_evtStoreGetter;
+  };
+}
+
+#include "SysListHandle.icc"
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysListHandle.icc b/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysListHandle.icc
new file mode 100644
index 0000000000000000000000000000000000000000..753fc7c9905ec37c860f4d76a4d78c0ba14a5fdf
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysListHandle.icc
@@ -0,0 +1,38 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <cassert>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  template<typename T> SysListHandle ::
+  SysListHandle (T *owner, const std::string& propertyName,
+                     const std::string& propertyDescription)
+    : AsgMessagingForward (owner)
+    , m_evtStoreGetter ([owner] () {return &*owner->evtStore();})
+  {
+    owner->declareProperty (propertyName, m_systematicsListName,
+                            propertyDescription);
+    owner->declareProperty (propertyName + "Regex", m_affectingRegex, "affecting systematics for " + propertyDescription);
+  }
+
+
+
+  inline bool SysListHandle ::
+  isInitialized () const noexcept
+  {
+    return m_isInitialized;
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysListType.h b/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysListType.h
new file mode 100644
index 0000000000000000000000000000000000000000..57f7c8705b4d418d15af1509610130fd27aef766
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysListType.h
@@ -0,0 +1,37 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef SYSTEMATICS_HANDLES__SYS_LIST_TYPE_H
+#define SYSTEMATICS_HANDLES__SYS_LIST_TYPE_H
+
+#include <string>
+#include <vector>
+#include <AsgTools/CLASS_DEF.h>
+
+namespace CP
+{
+  class SystematicSet;
+}
+
+CLASS_DEF( std::vector<CP::SystematicSet> , 79952367 , 1 )
+
+namespace CP
+{
+  /// \brief the type for the systematics list in the event store
+  ///
+  /// For now this is just a vector of SystematicSet, but I made a
+  /// typedef for this as I am not sure this will necessarily be
+  /// performant in all situations.  This way at least I will get
+  /// compilation errors if I change it.
+  typedef std::vector<CP::SystematicSet> SysListType;
+
+  /// \brief the default name for putting the systematics list into
+  /// the event store
+  const std::string& sysListDefaultName ();
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysReadHandle.h b/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysReadHandle.h
new file mode 100644
index 0000000000000000000000000000000000000000..ae7c304c77fea2f4a96e6b83e282b833a198a1e1
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysReadHandle.h
@@ -0,0 +1,111 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef SYSTEMATICS_HANDLES__SYS_READ_HANDLE_H
+#define SYSTEMATICS_HANDLES__SYS_READ_HANDLE_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <AsgTools/AsgMessagingForward.h>
+#include <PATInterfaces/SystematicSet.h>
+#include <SystematicsHandles/ISysHandleBase.h>
+#include <string>
+#include <type_traits>
+#include <unordered_map>
+
+class StatusCode;
+
+namespace CP
+{
+  class SystematicSet;
+
+  /// \brief a data handle for reading systematics varied input data
+
+  template<typename T> class SysReadHandle final
+    : public ISysHandleBase, public asg::AsgMessagingForward
+  {
+    //
+    // public interface
+    //
+
+    /// \brief standard constructor
+  public:
+    template<typename T2>
+    SysReadHandle (T2 *owner, const std::string& propertyName,
+                   const std::string& propertyValue,
+                   const std::string& propertyDescription);
+
+
+    /// \brief whether we have a name configured
+  public:
+    bool empty () const noexcept;
+
+    /// \brief !empty()
+  public:
+    explicit operator bool () const noexcept;
+
+
+    /// \brief get the name we retrieve from the event store
+  public:
+    const std::string& getName (const CP::SystematicSet& sys) const;
+
+
+    /// \brief retrieve the object for the given name
+  public:
+    ::StatusCode retrieve (const T*& object,
+                           const CP::SystematicSet& sys) const;
+
+
+
+    //
+    // inherited interface
+    //
+
+  public:
+    virtual std::string getInputAffecting () const override;
+
+
+
+    //
+    // private interface
+    //
+
+    /// \brief the input name we use
+  private:
+    std::string m_inputName;
+
+    /// \brief the regular expression for affecting systematics
+  private:
+    std::string m_affectingRegex {".*"};
+
+    /// \brief the cache of names we use
+  private:
+    mutable std::unordered_map<CP::SystematicSet,std::string> m_inputNameCache;
+
+
+    /// \brief the type of the event store we use
+  private:
+    typedef std::decay<decltype(*((EL::AnaAlgorithm*)0)->evtStore())>::type StoreType;
+
+    /// \brief the event store we use
+  private:
+    mutable StoreType *m_evtStore = nullptr;
+
+    /// \brief the function to retrieve the event store
+    ///
+    /// This is an std::function to allow the parent to be either a
+    /// tool or an algorithm.  Though we are not really supporting
+    /// tools as parents when using \ref SysListHandle, so in
+    /// principle this could be replaced with a pointer to the
+    /// algorithm instead.
+  private:
+    std::function<StoreType*()> m_evtStoreGetter;
+  };
+}
+
+#include "SysReadHandle.icc"
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysReadHandle.icc b/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysReadHandle.icc
new file mode 100644
index 0000000000000000000000000000000000000000..690580cc971379baad805984ccf345cc1a4970e9
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysReadHandle.icc
@@ -0,0 +1,88 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <AsgTools/MessageCheck.h>
+#include <AsgTools/StatusCode.h>
+#include <SystematicsHandles/Helpers.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  template<typename T> template<typename T2> SysReadHandle<T> ::
+  SysReadHandle (T2 *owner, const std::string& propertyName,
+                 const std::string& propertyValue,
+                 const std::string& propertyDescription)
+    : AsgMessagingForward (owner), m_inputName (propertyValue)
+    , m_evtStoreGetter ([owner] () {return &*owner->evtStore();})
+  {
+    owner->declareProperty (propertyName, m_inputName, propertyDescription);
+    owner->declareProperty (propertyName + "Regex", m_affectingRegex, "affecting systematics for " + propertyDescription);
+  }
+
+
+
+  template<typename T> bool SysReadHandle<T> ::
+  empty () const noexcept
+  {
+    return m_inputName.empty();
+  }
+
+
+
+  template<typename T> SysReadHandle<T> ::
+  operator bool () const noexcept
+  {
+    return !m_inputName.empty();
+  }
+
+
+
+  template<typename T> const std::string& SysReadHandle<T> ::
+  getName (const CP::SystematicSet& sys) const
+  {
+    auto cache = m_inputNameCache.find (sys);
+    if (cache == m_inputNameCache.end())
+    {
+      std::string newName = makeSystematicsName
+        (m_inputName, m_affectingRegex, sys);
+      ANA_MSG_DEBUG ("SysReadHandle: " << newName << " (" << sys.name() << ")");
+      m_inputNameCache.insert (std::make_pair (sys, newName));
+      cache = m_inputNameCache.find (sys);
+      assert (cache != m_inputNameCache.end());
+
+      // retrieving this here, just so it exists
+      if (!m_evtStore)
+        m_evtStore = m_evtStoreGetter();
+    }
+    return cache->second;
+  }
+
+
+
+  template<typename T> ::StatusCode SysReadHandle<T> ::
+  retrieve (const T*& object, const CP::SystematicSet& sys) const
+  {
+    const std::string& name = getName (sys);
+    assert (m_evtStore);
+    return m_evtStore->retrieve (object, name);
+  }
+
+
+
+  template<typename T> std::string SysReadHandle<T> ::
+  getInputAffecting () const
+  {
+    return m_affectingRegex;
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysReadHandleArray.h b/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysReadHandleArray.h
new file mode 100644
index 0000000000000000000000000000000000000000..614541e0cf96dd3da47b3a790266be34487c7985
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysReadHandleArray.h
@@ -0,0 +1,123 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef SYSTEMATICS_HANDLES__SYS_READ_HANDLE_ARRAY_H
+#define SYSTEMATICS_HANDLES__SYS_READ_HANDLE_ARRAY_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <AsgTools/AsgMessagingForward.h>
+#include <PATInterfaces/SystematicSet.h>
+#include <SystematicsHandles/ISysHandleBase.h>
+#include <boost/functional/hash.hpp>
+#include <string>
+#include <type_traits>
+#include <unordered_map>
+
+class StatusCode;
+
+namespace CP
+{
+  class SystematicSet;
+
+  /// \brief a data handle for reading systematics varied input data
+
+  template<typename T> class SysReadHandleArray final
+    : public ISysHandleBase, public asg::AsgMessagingForward
+  {
+    //
+    // public interface
+    //
+
+    /// \brief standard constructor
+  public:
+    template<typename T2>
+    SysReadHandleArray (T2 *owner, const std::string& propertyName,
+                        const std::string& propertyDescription);
+
+
+    /// \brief initialize this data handle
+  public:
+    StatusCode initialize ();
+
+
+    /// \brief the number of read objects configured
+  public:
+    std::size_t size () const noexcept;
+
+
+    /// \brief get the name we retrieve from the event store
+  public:
+    const std::string& getName (const CP::SystematicSet& sys,
+                                std::size_t index) const;
+
+
+    /// \brief retrieve the object for the given name
+  public:
+    ::StatusCode retrieve (const T*& object,
+                           const CP::SystematicSet& sys,
+                           std::size_t index) const;
+
+
+
+    //
+    // inherited interface
+    //
+
+  public:
+    virtual std::string getInputAffecting () const override;
+
+
+
+    //
+    // private interface
+    //
+
+    /// \brief the name of the property we configured
+  private:
+    std::string m_propertyName;
+
+    /// \brief whether we have been initialized
+  private:
+    bool m_isInitialized {false};
+
+
+    /// \brief the input name we use
+  private:
+    std::vector<std::string> m_inputName;
+
+    /// \brief the regular expression for affecting systematics
+  private:
+    std::vector<std::string> m_affectingRegex;
+
+    /// \brief the cache of names we use
+  private:
+    mutable std::unordered_map<std::pair<CP::SystematicSet,std::size_t>,std::string,boost::hash<std::pair<CP::SystematicSet,std::size_t>>> m_inputNameCache;
+
+
+    /// \brief the type of the event store we use
+  private:
+    typedef std::decay<decltype(*((EL::AnaAlgorithm*)0)->evtStore())>::type StoreType;
+
+    /// \brief the event store we use
+  private:
+    mutable StoreType *m_evtStore = nullptr;
+
+    /// \brief the function to retrieve the event store
+    ///
+    /// This is an std::function to allow the parent to be either a
+    /// tool or an algorithm.  Though we are not really supporting
+    /// tools as parents when using \ref SysListHandle, so in
+    /// principle this could be replaced with a pointer to the
+    /// algorithm instead.
+  private:
+    std::function<StoreType*()> m_evtStoreGetter;
+  };
+}
+
+#include "SysReadHandleArray.icc"
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysReadHandleArray.icc b/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysReadHandleArray.icc
new file mode 100644
index 0000000000000000000000000000000000000000..89cbde7909719e76e745ac27a27947b89d8d36fd
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysReadHandleArray.icc
@@ -0,0 +1,113 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <AsgTools/MessageCheck.h>
+#include <AsgTools/StatusCode.h>
+#include <SystematicsHandles/Helpers.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  template<typename T> template<typename T2> SysReadHandleArray<T> ::
+  SysReadHandleArray (T2 *owner, const std::string& propertyName,
+                 const std::string& propertyDescription)
+    : AsgMessagingForward (owner)
+    , m_propertyName (propertyName)
+    , m_evtStoreGetter ([owner] () {return &*owner->evtStore();})
+  {
+    owner->declareProperty (propertyName, m_inputName, propertyDescription);
+    owner->declareProperty (propertyName + "Regex", m_affectingRegex, "affecting systematics for " + propertyDescription);
+  }
+
+
+
+  template<typename T> StatusCode SysReadHandleArray<T> ::
+  initialize ()
+  {
+    assert (m_isInitialized == false);
+    if (m_inputName.size() != m_affectingRegex.size())
+    {
+      ANA_MSG_ERROR ("array sizes for property " << m_propertyName << " and " << m_propertyName << "Regex do not match");
+      return StatusCode::FAILURE;
+    }
+    m_isInitialized = true;
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  template<typename T> std::size_t SysReadHandleArray<T> ::
+  size () const noexcept
+  {
+    assert (m_isInitialized);
+    return m_inputName.size();
+  }
+
+
+
+  template<typename T> const std::string& SysReadHandleArray<T> ::
+  getName (const CP::SystematicSet& sys,
+           std::size_t index) const
+  {
+    assert (m_isInitialized);
+    assert (index < size());
+    auto cache = m_inputNameCache.find (std::make_pair (sys, index));
+    if (cache == m_inputNameCache.end())
+    {
+      std::string newName = makeSystematicsName
+        (m_inputName[index], m_affectingRegex[index], sys);
+      ANA_MSG_DEBUG ("SysReadHandleArray: " << newName << " (" << sys.name() << ")");
+      m_inputNameCache.insert (std::make_pair (std::make_pair (sys, index), newName));
+      cache = m_inputNameCache.find (std::make_pair (sys, index));
+      assert (cache != m_inputNameCache.end());
+
+      // retrieving this here, just so it exists
+      if (!m_evtStore)
+        m_evtStore = m_evtStoreGetter();
+    }
+    return cache->second;
+  }
+
+
+
+  template<typename T> ::StatusCode SysReadHandleArray<T> ::
+  retrieve (const T*& object, const CP::SystematicSet& sys,
+            std::size_t index) const
+  {
+    assert (m_isInitialized);
+    assert (index < size());
+    const std::string& name = getName (sys, index);
+    assert (m_evtStore);
+    return m_evtStore->retrieve (object, name);
+  }
+
+
+
+  template<typename T> std::string SysReadHandleArray<T> ::
+  getInputAffecting () const
+  {
+    assert (m_isInitialized);
+    std::string result;
+    for (const std::string& regex : m_affectingRegex)
+    {
+      if (!regex.empty())
+      {
+        if (!result.empty())
+          result += "|";
+        result += regex;
+      }
+    }
+    return result;
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysWriteHandle.h b/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysWriteHandle.h
new file mode 100644
index 0000000000000000000000000000000000000000..81d7a20202b29a3f51388592f83c0efd301e94df
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysWriteHandle.h
@@ -0,0 +1,164 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef SYSTEMATICS_HANDLES__SYS_WRITE_HANDLE_H
+#define SYSTEMATICS_HANDLES__SYS_WRITE_HANDLE_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <AsgTools/AsgMessagingForward.h>
+#include <PATInterfaces/SystematicSet.h>
+#include <SystematicsHandles/ISysHandleBase.h>
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+class StatusCode;
+
+namespace CP
+{
+  class SystematicSet;
+
+  /// \brief a data handle for writing systematics varied input data
+
+  template<typename T,typename Aux = void> class SysWriteHandle final
+    : public ISysHandleBase, public asg::AsgMessagingForward
+  {
+    //
+    // public interface
+    //
+
+    /// \brief standard constructor
+  public:
+    template<typename T2>
+    SysWriteHandle (T2 *owner, const std::string& propertyName,
+                    const std::string& propertyValue,
+                    const std::string& propertyDescription);
+
+
+    /// \brief retrieve the object for the given name
+  public:
+    ::StatusCode record (std::unique_ptr<T> object,
+                         std::unique_ptr<Aux> aux,
+                         const CP::SystematicSet& sys) const;
+
+
+
+    //
+    // inherited interface
+    //
+
+  public:
+    virtual std::string getInputAffecting () const override;
+
+
+
+    //
+    // private interface
+    //
+
+    /// \brief the output name we use
+  private:
+    std::string m_outputName;
+
+    /// \brief the cache of names we use
+  private:
+    mutable std::unordered_map<CP::SystematicSet,std::string> m_outputNameCache;
+
+
+    /// \brief the type of the event store we use
+  private:
+    typedef std::decay<decltype(*((EL::AnaAlgorithm*)0)->evtStore())>::type StoreType;
+
+    /// \brief the event store we use
+  private:
+    mutable StoreType *m_evtStore = nullptr;
+
+    /// \brief the function to retrieve the event store
+    ///
+    /// This is an std::function to allow the parent to be either a
+    /// tool or an algorithm.  Though we are not really supporting
+    /// tools as parents when using \ref SysListHandle, so in
+    /// principle this could be replaced with a pointer to the
+    /// algorithm instead.
+  private:
+    std::function<StoreType*()> m_evtStoreGetter;
+  };
+
+
+
+  template<typename T> class SysWriteHandle<T,void> final
+    : public ISysHandleBase, public asg::AsgMessagingForward
+  {
+    //
+    // public interface
+    //
+
+    /// \brief standard constructor
+  public:
+    template<typename T2>
+    SysWriteHandle (T2 *owner, const std::string& propertyName,
+                    const std::string& propertyValue,
+                    const std::string& propertyDescription);
+
+
+    /// \brief get the name we record to the event store
+  public:
+    const std::string& getName (const CP::SystematicSet& sys) const;
+
+
+    /// \brief record the object for the given systematic
+  public:
+    ::StatusCode record (std::unique_ptr<T> object,
+                         const CP::SystematicSet& sys) const;
+
+
+
+    //
+    // inherited interface
+    //
+
+  public:
+    virtual std::string getInputAffecting () const override;
+
+
+
+    //
+    // private interface
+    //
+
+    /// \brief the output name we use
+  private:
+    std::string m_outputName;
+
+    /// \brief the cache of names we use
+  private:
+    mutable std::unordered_map<CP::SystematicSet,std::string> m_outputNameCache;
+
+
+    /// \brief the type of the event store we use
+  private:
+    typedef std::decay<decltype(*((EL::AnaAlgorithm*)0)->evtStore())>::type StoreType;
+
+    /// \brief the event store we use
+  private:
+    mutable StoreType *m_evtStore = nullptr;
+
+    /// \brief the function to retrieve the event store
+    ///
+    /// This is an std::function to allow the parent to be either a
+    /// tool or an algorithm.  Though we are not really supporting
+    /// tools as parents when using \ref SysListHandle, so in
+    /// principle this could be replaced with a pointer to the
+    /// algorithm instead.
+  private:
+    std::function<StoreType*()> m_evtStoreGetter;
+  };
+}
+
+#include "SysWriteHandle.icc"
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysWriteHandle.icc b/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysWriteHandle.icc
new file mode 100644
index 0000000000000000000000000000000000000000..74ba4964619dbf002cbfda78d76cc5ce04f47a74
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SystematicsHandles/SystematicsHandles/SysWriteHandle.icc
@@ -0,0 +1,114 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <AsgTools/MessageCheck.h>
+#include <SystematicsHandles/Helpers.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  template<typename T,typename Aux> template<typename T2> SysWriteHandle<T,Aux> ::
+  SysWriteHandle (T2 *owner, const std::string& propertyName,
+                 const std::string& propertyValue,
+                 const std::string& propertyDescription)
+    : AsgMessagingForward (owner), m_outputName (propertyValue)
+    , m_evtStoreGetter ([owner] () {return &*owner->evtStore();})
+  {
+    owner->declareProperty (propertyName, m_outputName, propertyDescription);
+  }
+
+
+
+  template<typename T,typename Aux> ::StatusCode SysWriteHandle<T,Aux> ::
+  record (std::unique_ptr<T> object, std::unique_ptr<Aux> aux,
+          const CP::SystematicSet& sys) const
+  {
+    auto cache = m_outputNameCache.find (sys);
+    if (cache == m_outputNameCache.end())
+    {
+      std::string newName = makeSystematicsName (m_outputName, sys);
+      ANA_MSG_DEBUG ("SysWriteHandle: " << newName << " (" << sys.name() << ")");
+      m_outputNameCache.insert (std::make_pair (sys, newName));
+      cache = m_outputNameCache.find (sys);
+      assert (cache != m_outputNameCache.end());
+      if (!m_evtStore)
+        m_evtStore = m_evtStoreGetter();
+    }
+    assert (m_evtStore);
+    if (m_evtStore->record (aux.release(), cache->second + "Aux.").isFailure())
+      return StatusCode::FAILURE;
+    if (m_evtStore->record (object.release(), cache->second).isFailure())
+      return StatusCode::FAILURE;
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  template<typename T,typename Aux> std::string SysWriteHandle<T,Aux> ::
+  getInputAffecting () const
+  {
+    return "";
+  }
+
+
+
+  template<typename T> template<typename T2> SysWriteHandle<T,void> ::
+  SysWriteHandle (T2 *owner, const std::string& propertyName,
+                 const std::string& propertyValue,
+                 const std::string& propertyDescription)
+    : AsgMessagingForward (owner), m_outputName (propertyValue)
+    , m_evtStoreGetter ([owner] () {return &*owner->evtStore();})
+  {
+    owner->declareProperty (propertyName, m_outputName, propertyDescription);
+  }
+
+
+
+  template<typename T> const std::string& SysWriteHandle<T,void> ::
+  getName (const CP::SystematicSet& sys) const
+  {
+    auto cache = m_outputNameCache.find (sys);
+    if (cache == m_outputNameCache.end())
+    {
+      std::string newName = makeSystematicsName (m_outputName, sys);
+      ANA_MSG_DEBUG ("SysWriteHandle: " << newName << " (" << sys.name() << ")");
+      m_outputNameCache.insert (std::make_pair (sys, newName));
+      cache = m_outputNameCache.find (sys);
+      assert (cache != m_outputNameCache.end());
+
+      // retrieving this here, just so that it exists
+      if (!m_evtStore)
+        m_evtStore = m_evtStoreGetter();
+    }
+    return cache->second;
+  }
+
+
+
+  template<typename T> ::StatusCode SysWriteHandle<T,void> ::
+  record (std::unique_ptr<T> object, const CP::SystematicSet& sys) const
+  {
+    const std::string& name = getName (sys);
+    assert (m_evtStore);
+    return m_evtStore->record (object.release(), name);
+  }
+
+
+
+  template<typename T> std::string SysWriteHandle<T,void> ::
+  getInputAffecting () const
+  {
+    return "";
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/SystematicsHandles/test/cc_SysCopyHandle.cxx b/PhysicsAnalysis/Algorithms/SystematicsHandles/test/cc_SysCopyHandle.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..31ed464c6c0bd058fd4010d8f2eb1ec9a587a7ed
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SystematicsHandles/test/cc_SysCopyHandle.cxx
@@ -0,0 +1,41 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <AsgTools/AsgTool.h>
+#include <xAODEventInfo/EventInfo.h>
+#include <xAODJet/JetContainer.h>
+
+//
+// test code
+//
+
+// this tries to instantiate the various handles
+void test ()
+{
+  CP::SystematicSet *sys = nullptr;
+  xAOD::EventInfo *object1 = nullptr;
+  xAOD::JetContainer *object2 = nullptr;
+
+  EL::AnaAlgorithm *alg = nullptr;
+  CP::SysCopyHandle<xAOD::EventInfo> algHandle (alg, "test", "test", "test");
+  algHandle.getCopy (object1, *sys);
+
+  asg::AsgTool *tool = nullptr;
+  CP::SysCopyHandle<xAOD::JetContainer> toolHandle (tool, "test", "test", "test");
+  toolHandle.getCopy (object2, *sys);
+}
+
+int main ()
+{
+  return 0;
+}
diff --git a/PhysicsAnalysis/Algorithms/SystematicsHandles/test/cc_SysDecorationHandle.cxx b/PhysicsAnalysis/Algorithms/SystematicsHandles/test/cc_SysDecorationHandle.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..08a1b12617bc55ff1a49502ab9064eb2b0bbea93
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SystematicsHandles/test/cc_SysDecorationHandle.cxx
@@ -0,0 +1,39 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <SystematicsHandles/SysDecorationHandle.h>
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <AsgTools/AsgTool.h>
+
+//
+// test code
+//
+
+// this tries to instantiate the various handles
+void test ()
+{
+  CP::SystematicSet *sys = nullptr;
+  const SG::AuxElement *constObject = nullptr;
+  SG::AuxElement *object = nullptr;
+
+  EL::AnaAlgorithm *alg = nullptr;
+  CP::SysDecorationHandle<float> algHandle (alg, "test", "test", "test");
+  float value = algHandle.get (*constObject, *sys);
+  algHandle.set (*object, value, *sys);
+
+  asg::AsgTool *tool = nullptr;
+  CP::SysDecorationHandle<float> toolHandle (tool, "test", "test", "test");
+}
+
+int main ()
+{
+  return 0;
+}
diff --git a/PhysicsAnalysis/Algorithms/SystematicsHandles/test/cc_SysListHandle.cxx b/PhysicsAnalysis/Algorithms/SystematicsHandles/test/cc_SysListHandle.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..dbcb0acc7d003ee158b12a61e6565b83155036f8
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SystematicsHandles/test/cc_SysListHandle.cxx
@@ -0,0 +1,41 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <AsgTools/AsgTool.h>
+#include <SystematicsHandles/SysListHandle.h>
+#include <SystematicsHandles/SysReadHandle.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+
+//
+// test code
+//
+
+// this tries to instantiate the various templates
+void test ()
+{
+  EL::AnaAlgorithm *alg = nullptr;
+  CP::SysListHandle algProperty (alg);
+  CP::SysReadHandle<float> algHandle (alg, "test", "test", "test");
+  algProperty.addHandle (algHandle);
+
+  // we probably don't want to use SysListHandle with tools, but
+  // we may as well allow it for now.
+  asg::AsgTool *tool = nullptr;
+  CP::SysListHandle toolProperty (tool);
+  CP::SysReadHandle<float> toolHandle (tool, "test", "test", "test");
+  toolProperty.addHandle (toolHandle);
+}
+
+int main ()
+{
+  return 0;
+}
diff --git a/PhysicsAnalysis/Algorithms/SystematicsHandles/test/cc_SysReadHandle.cxx b/PhysicsAnalysis/Algorithms/SystematicsHandles/test/cc_SysReadHandle.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..8edea9045817d150d1283becb52968136119017c
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SystematicsHandles/test/cc_SysReadHandle.cxx
@@ -0,0 +1,38 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <SystematicsHandles/SysReadHandle.h>
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <AsgTools/AsgTool.h>
+
+//
+// test code
+//
+
+// this tries to instantiate the various handles
+void test ()
+{
+  CP::SystematicSet *sys = nullptr;
+  const float *object = nullptr;
+
+  EL::AnaAlgorithm *alg = nullptr;
+  CP::SysReadHandle<float> algHandle (alg, "test", "test", "test");
+  algHandle.retrieve (object, *sys);
+
+  asg::AsgTool *tool = nullptr;
+  CP::SysReadHandle<float> toolHandle (tool, "test", "test", "test");
+  toolHandle.retrieve (object, *sys);
+}
+
+int main ()
+{
+  return 0;
+}
diff --git a/PhysicsAnalysis/Algorithms/SystematicsHandles/test/cc_SysWriteHandle.cxx b/PhysicsAnalysis/Algorithms/SystematicsHandles/test/cc_SysWriteHandle.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..d3454c857f7f57c975103cab07fb20be1e6f0856
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SystematicsHandles/test/cc_SysWriteHandle.cxx
@@ -0,0 +1,37 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+//
+// includes
+//
+
+#include <SystematicsHandles/SysWriteHandle.h>
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <AsgTools/AsgTool.h>
+
+//
+// test code
+//
+
+// this tries to instantiate the various handles
+void test ()
+{
+  CP::SystematicSet *sys = nullptr;
+
+  EL::AnaAlgorithm *alg = nullptr;
+  CP::SysWriteHandle<float> algHandle (alg, "test", "test", "test");
+  algHandle.record (std::unique_ptr<float> (), *sys);
+
+  asg::AsgTool *tool = nullptr;
+  CP::SysWriteHandle<float,double> toolHandle (tool, "test", "test", "test");
+  toolHandle.record (std::unique_ptr<float> (), std::unique_ptr<double> (), *sys);
+}
+
+int main ()
+{
+  return 0;
+}
diff --git a/PhysicsAnalysis/Algorithms/SystematicsHandles/test/ut_CopyHelpers.cxx b/PhysicsAnalysis/Algorithms/SystematicsHandles/test/ut_CopyHelpers.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..284efb0108095bb2731cbf369f43e537db8b942f
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/SystematicsHandles/test/ut_CopyHelpers.cxx
@@ -0,0 +1,117 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+// System include(s):
+#include <cstdlib>
+#include <cmath>
+#include <memory>
+
+// Infrastructure include(s):
+#include "xAODRootAccess/TEvent.h"
+#include "xAODRootAccess/TStore.h"
+#include "xAODRootAccess/Init.h"
+#include "AsgTools/MsgStream.h"
+#include "AsgTools/MessageCheck.h"
+#include "AsgTools/SgTEvent.h"
+
+// EDM include(s):
+#include "xAODBase/IParticleHelpers.h"
+#include "xAODJet/JetContainer.h"
+#include "xAODJet/JetAuxContainer.h"
+
+// Local include(s):
+#include "SystematicsHandles/CopyHelpers.h"
+
+/// Function generating a random number in a specific range
+double randomInRange( double min, double max ) {
+   return static_cast< double >( rand() ) / RAND_MAX * ( max - min ) + min;
+}
+
+int main() {
+
+   // Set up the usage of the ANA_CHECK(...) macro.
+   using namespace asg::msgUserCode;
+   ANA_CHECK_SET_TYPE( int );
+
+   // Set up the environment.
+   ANA_CHECK( xAOD::Init() );
+
+   // Create the transient store objects.
+   xAOD::TEvent event;
+   xAOD::TStore store;
+   asg::SgTEvent sgEvent( &event, &store );
+
+   // Create a message stream, to be used later on in the code.
+   MsgStream msgStream( "ut_CopyHelpers" );
+
+   // Create a primary container, fill it with some random content, and
+   // record it into the transient store.
+   auto originalJets = std::make_unique< xAOD::JetContainer >();
+   auto originalAux  = std::make_unique< xAOD::JetAuxContainer >();
+   originalJets->setStore( originalAux.get() );
+
+   for( int i = 0; i < 10; ++i ) {
+      xAOD::Jet* jet = new xAOD::Jet();
+      originalJets->push_back( jet );
+      jet->setJetP4( xAOD::JetFourMom_t( randomInRange( 10000.0, 50000.0 ), // Pt
+                                         randomInRange( -2.5, 2.5 ),        // Eta
+                                         randomInRange( -M_PI, M_PI ),      // Phi
+                                         randomInRange( 0.0, 500.0 ) ) );   // M
+   }
+
+   const xAOD::JetContainer* originalJetsPtr = originalJets.get();
+   ANA_CHECK( store.record( std::move( originalJets ), "OriginalJets" ) );
+   ANA_CHECK( store.record( std::move( originalAux ),  "OriginalJetsAux." ) );
+
+   // Make it easier to use CP::detail::ShallowCopy.
+   using CP::detail::ShallowCopy;
+
+   // Make a copy of this simple container.
+   xAOD::JetContainer* copiedJets = nullptr;
+   ANA_CHECK( ShallowCopy< xAOD::JetContainer >::getCopy( msgStream,
+                                                          sgEvent,
+                                                          copiedJets,
+                                                          originalJetsPtr,
+                                                          "CopiedJets",
+                                                          "CopiedJetsAux." ) );
+
+   // Make sure that the copied objects point to the originals.
+   ANA_CHECK( copiedJets->size() == originalJetsPtr->size() );
+   for( size_t i = 0; i < copiedJets->size(); ++i ) {
+      ANA_CHECK( xAOD::getOriginalObject( *( copiedJets->at( i ) ) ) ==
+                 originalJetsPtr->at( i ) );
+   }
+   msgStream << MSG::INFO << "Simple copy succeeded" << endmsg;
+
+   // Make a view copy of the copied container, which selects only every second
+   // object from it.
+   auto viewCopy = std::make_unique< xAOD::JetContainer >( SG::VIEW_ELEMENTS );
+   for( size_t i = 0; i < copiedJets->size(); i = i + 2 ) {
+      viewCopy->push_back( copiedJets->at( i ) );
+   }
+
+   const xAOD::JetContainer* viewCopyPtr = viewCopy.get();
+   ANA_CHECK( store.record( std::move( viewCopy ), "ViewOfCopiedJets" ) );
+
+   // Now make a shallow copy of this view copy.
+   xAOD::JetContainer* copiedViewJets = nullptr;
+   ANA_CHECK( ShallowCopy< xAOD::JetContainer >::getCopy( msgStream,
+                                                          sgEvent,
+                                                          copiedViewJets,
+                                                          viewCopyPtr,
+                                                          "CopyOfViewJets",
+                                                          "CopyOfViewJetsAux." ) );
+
+   // Check the copy. The logic here is a bit complicated, since the original
+   // objects of the copies of the view container are still the objects in the
+   // original container. And not the objects in the view container.
+   ANA_CHECK( copiedViewJets->size() == viewCopyPtr->size() );
+   for( size_t i = 0; i < viewCopyPtr->size(); ++i ) {
+      ANA_CHECK( xAOD::getOriginalObject( *( copiedViewJets->at( i ) ) ) ==
+                 originalJetsPtr->at( copiedViewJets->at( i )->index() ) );
+   }
+   msgStream << MSG::INFO << "Copy of view vector succeeded" << endmsg;
+
+   return 0;
+}
diff --git a/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/CMakeLists.txt b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..0b5989cb8c8d8078ba2ce48b44b4ae45217babf4
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/CMakeLists.txt
@@ -0,0 +1,64 @@
+# Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+#
+# @author Nils Krumnack
+
+
+# The name of the package:
+atlas_subdir( TauAnalysisAlgorithms )
+
+# The package's dependencies:
+atlas_depends_on_subdirs(
+   PUBLIC
+   Event/xAOD/xAODTau
+   PhysicsAnalysis/Algorithms/SelectionHelpers
+   PhysicsAnalysis/Algorithms/SystematicsHandles
+   PhysicsAnalysis/D3PDTools/AnaAlgorithm
+   PhysicsAnalysis/TauID/TauAnalysisTools )
+
+atlas_add_library( TauAnalysisAlgorithmsLib
+   TauAnalysisAlgorithms/*.h TauAnalysisAlgorithms/*.icc Root/*.cxx
+   PUBLIC_HEADERS TauAnalysisAlgorithms
+   LINK_LIBRARIES xAODTau SelectionHelpersLib
+   SystematicsHandlesLib
+   AnaAlgorithmLib TauAnalysisToolsLib )
+
+atlas_add_dictionary( TauAnalysisAlgorithmsDict
+   TauAnalysisAlgorithms/TauAnalysisAlgorithmsDict.h
+   TauAnalysisAlgorithms/selection.xml
+   LINK_LIBRARIES TauAnalysisAlgorithmsLib )
+
+if( NOT XAOD_STANDALONE )
+   atlas_add_component( TauAnalysisAlgorithms
+      src/*.h src/*.cxx src/components/*.cxx
+      LINK_LIBRARIES GaudiKernel TauAnalysisAlgorithmsLib )
+endif()
+
+atlas_install_python_modules( python/*.py )
+atlas_install_joboptions( share/*_jobOptions.py )
+atlas_install_scripts( share/*_eljob.py )
+atlas_install_data( data/*.conf )
+
+if( XAOD_STANDALONE )
+   atlas_add_test( testJobData
+      SCRIPT TauAnalysisAlgorithmsTest_eljob.py --data-type data --unit-test
+      PROPERTIES TIMEOUT 600 )
+   atlas_add_test( testJobFullSim
+      SCRIPT TauAnalysisAlgorithmsTest_eljob.py --data-type mc --unit-test
+      PROPERTIES TIMEOUT 600 )
+   atlas_add_test( testJobFastSim
+      SCRIPT TauAnalysisAlgorithmsTest_eljob.py --data-type afii --unit-test
+      PROPERTIES TIMEOUT 600 )
+else()
+   atlas_add_test( testJobData
+      SCRIPT athena.py
+      TauAnalysisAlgorithms/TauAnalysisAlgorithmsTest_jobOptions.py - --data-type data
+      PROPERTIES TIMEOUT 600 )
+   atlas_add_test( testJobFullSim
+      SCRIPT athena.py
+      TauAnalysisAlgorithms/TauAnalysisAlgorithmsTest_jobOptions.py - --data-type mc
+      PROPERTIES TIMEOUT 600 )
+   atlas_add_test( testJobFastSim
+      SCRIPT athena.py
+      TauAnalysisAlgorithms/TauAnalysisAlgorithmsTest_jobOptions.py - --data-type afii
+      PROPERTIES TIMEOUT 600 )
+endif()
diff --git a/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/Root/DiTauEfficiencyCorrectionsAlg.cxx b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/Root/DiTauEfficiencyCorrectionsAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..140ad67c91e0ee4f2b84ba165b858d1a2bd97392
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/Root/DiTauEfficiencyCorrectionsAlg.cxx
@@ -0,0 +1,73 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+//
+// includes
+//
+
+#include <TauAnalysisAlgorithms/DiTauEfficiencyCorrectionsAlg.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  DiTauEfficiencyCorrectionsAlg ::
+  DiTauEfficiencyCorrectionsAlg (const std::string& name, 
+                     ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+    , m_efficiencyCorrectionsTool ("TauAnalysisTools::DiTauEfficiencyCorrectionsTool", this)
+  {
+    declareProperty ("efficiencyCorrectionsTool", m_efficiencyCorrectionsTool, "the calibration and smearing tool we apply");
+    declareProperty ("scaleFactorDecoration", m_scaleFactorDecoration, "the decoration for the tau scale factor");
+  }
+
+
+
+  StatusCode DiTauEfficiencyCorrectionsAlg ::
+  initialize ()
+  {
+    if (m_scaleFactorDecoration.empty())
+    {
+      ANA_MSG_ERROR ("no scale factor decoration name set");
+      return StatusCode::FAILURE;
+    }
+    m_scaleFactorAccessor = std::make_unique<SG::AuxElement::Accessor<float> > (m_scaleFactorDecoration);
+
+    ANA_CHECK (m_efficiencyCorrectionsTool.retrieve());
+    m_systematicsList.addHandle (m_tauHandle);
+    ANA_CHECK (m_systematicsList.addAffectingSystematics (m_efficiencyCorrectionsTool->affectingSystematics()));
+    ANA_CHECK (m_systematicsList.initialize());
+    ANA_CHECK (m_preselection.initialize());
+    ANA_CHECK (m_outOfValidity.initialize());
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode DiTauEfficiencyCorrectionsAlg ::
+  execute ()
+  {
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode {
+        ANA_CHECK (m_efficiencyCorrectionsTool->applySystematicVariation (sys));
+        xAOD::DiTauJetContainer *taus = nullptr;
+        ANA_CHECK (m_tauHandle.getCopy (taus, sys));
+        for (xAOD::DiTauJet *tau : *taus)
+        {
+          if (m_preselection.getBool (*tau))
+          {
+            double sf = 0;
+            ANA_CHECK_CORRECTION (m_outOfValidity, *tau, m_efficiencyCorrectionsTool->getEfficiencyScaleFactor (*tau, sf));
+            (*m_scaleFactorAccessor) (*tau) = sf;
+          }
+        }
+        return StatusCode::SUCCESS;
+      });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/Root/DiTauSmearingAlg.cxx b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/Root/DiTauSmearingAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..e3a367ead8796488160470d8a15cbe64756c43c8
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/Root/DiTauSmearingAlg.cxx
@@ -0,0 +1,63 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+//
+// includes
+//
+
+#include <TauAnalysisAlgorithms/DiTauSmearingAlg.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  DiTauSmearingAlg ::
+  DiTauSmearingAlg (const std::string& name, 
+                     ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+    , m_smearingTool ("TauAnalysisTools::DiTauSmearingTool", this)
+  {
+    declareProperty ("smearingTool", m_smearingTool, "the calibration and smearing tool we apply");
+  }
+
+
+
+  StatusCode DiTauSmearingAlg ::
+  initialize ()
+  {
+    ANA_CHECK (m_smearingTool.retrieve());
+    m_systematicsList.addHandle (m_tauHandle);
+    ANA_CHECK (m_systematicsList.addAffectingSystematics (m_smearingTool->affectingSystematics()));
+    ANA_CHECK (m_systematicsList.initialize());
+    ANA_CHECK (m_preselection.initialize());
+    ANA_CHECK (m_outOfValidity.initialize());
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode DiTauSmearingAlg ::
+  execute ()
+  {
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode {
+        ANA_CHECK (m_smearingTool->applySystematicVariation (sys));
+        xAOD::DiTauJetContainer *taus = nullptr;
+        ANA_CHECK (m_tauHandle.getCopy (taus, sys));
+        for (xAOD::DiTauJet *tau : *taus)
+        {
+          if (m_preselection.getBool (*tau))
+          {
+            ANA_CHECK_CORRECTION (m_outOfValidity, *tau, m_smearingTool->applyCorrection (*tau));
+          }
+        }
+        return StatusCode::SUCCESS;
+      });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/Root/DiTauTruthMatchingAlg.cxx b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/Root/DiTauTruthMatchingAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..685c0e87581f89ef3203d2b924d3ca8ee13152cc
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/Root/DiTauTruthMatchingAlg.cxx
@@ -0,0 +1,60 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+//
+// includes
+//
+
+#include <TauAnalysisAlgorithms/DiTauTruthMatchingAlg.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  DiTauTruthMatchingAlg ::
+  DiTauTruthMatchingAlg (const std::string& name, 
+                     ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+    , m_matchingTool ("TauAnalysisTools::DiTauTruthMatchingTool", this)
+  {
+    declareProperty ("matchingTool", m_matchingTool, "the matching tool we apply");
+  }
+
+
+
+  StatusCode DiTauTruthMatchingAlg ::
+  initialize ()
+  {
+    ANA_CHECK (m_matchingTool.retrieve());
+    m_systematicsList.addHandle (m_tauHandle);
+    ANA_CHECK (m_systematicsList.initialize());
+    ANA_CHECK (m_preselection.initialize());
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode DiTauTruthMatchingAlg ::
+  execute ()
+  {
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode {
+        xAOD::DiTauJetContainer *taus = nullptr;
+        ANA_CHECK (m_tauHandle.getCopy (taus, sys));
+        for (xAOD::DiTauJet *tau : *taus)
+        {
+          if (m_preselection.getBool (*tau))
+          {
+            m_matchingTool->applyTruthMatch (*tau);
+          }
+        }
+        return StatusCode::SUCCESS;
+      });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/Root/TauEfficiencyCorrectionsAlg.cxx b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/Root/TauEfficiencyCorrectionsAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..bc7ed9a74c667f84a9c147032af1f11bd77a407e
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/Root/TauEfficiencyCorrectionsAlg.cxx
@@ -0,0 +1,75 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+//
+// includes
+//
+
+#include <TauAnalysisAlgorithms/TauEfficiencyCorrectionsAlg.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  TauEfficiencyCorrectionsAlg ::
+  TauEfficiencyCorrectionsAlg (const std::string& name, 
+                     ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+    , m_efficiencyCorrectionsTool ("TauAnalysisTools::TauEfficiencyCorrectionsTool", this)
+  {
+    declareProperty ("efficiencyCorrectionsTool", m_efficiencyCorrectionsTool, "the calibration and smearing tool we apply");
+  }
+
+
+
+  StatusCode TauEfficiencyCorrectionsAlg ::
+  initialize ()
+  {
+    if (m_scaleFactorDecoration.empty())
+    {
+      ANA_MSG_ERROR ("no scale factor decoration name set");
+      return StatusCode::FAILURE;
+    }
+
+    ANA_CHECK (m_efficiencyCorrectionsTool.retrieve());
+    m_systematicsList.addHandle (m_tauHandle);
+    ANA_CHECK (m_systematicsList.addAffectingSystematics (m_efficiencyCorrectionsTool->affectingSystematics()));
+    ANA_CHECK (m_systematicsList.initialize());
+    ANA_CHECK (m_preselection.initialize());
+    ANA_CHECK (m_outOfValidity.initialize());
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode TauEfficiencyCorrectionsAlg ::
+  execute ()
+  {
+    ANA_CHECK (m_scaleFactorDecoration.preExecute (m_systematicsList));
+
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode {
+        ANA_CHECK (m_efficiencyCorrectionsTool->applySystematicVariation (sys));
+        xAOD::TauJetContainer *taus = nullptr;
+        ANA_CHECK (m_tauHandle.getCopy (taus, sys));
+        for (xAOD::TauJet *tau : *taus)
+        {
+          if (m_preselection.getBool (*tau))
+          {
+            double sf = 0;
+            ANA_CHECK_CORRECTION (m_outOfValidity, *tau, m_efficiencyCorrectionsTool->getEfficiencyScaleFactor (*tau, sf));
+            m_scaleFactorDecoration.set (*tau, sf, sys);
+          } else {
+            m_scaleFactorDecoration.set (*tau, invalidScaleFactor(), sys);
+          }
+        }
+        return StatusCode::SUCCESS;
+      });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/Root/TauSmearingAlg.cxx b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/Root/TauSmearingAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..f7b65a29ee8735390f5a133539c0fabcfb650e00
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/Root/TauSmearingAlg.cxx
@@ -0,0 +1,63 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+//
+// includes
+//
+
+#include <TauAnalysisAlgorithms/TauSmearingAlg.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  TauSmearingAlg ::
+  TauSmearingAlg (const std::string& name, 
+                     ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+    , m_smearingTool ("TauAnalysisTools::TauSmearingTool", this)
+  {
+    declareProperty ("smearingTool", m_smearingTool, "the calibration and smearing tool we apply");
+  }
+
+
+
+  StatusCode TauSmearingAlg ::
+  initialize ()
+  {
+    ANA_CHECK (m_smearingTool.retrieve());
+    m_systematicsList.addHandle (m_tauHandle);
+    ANA_CHECK (m_systematicsList.addAffectingSystematics (m_smearingTool->affectingSystematics()));
+    ANA_CHECK (m_systematicsList.initialize());
+    ANA_CHECK (m_preselection.initialize());
+    ANA_CHECK (m_outOfValidity.initialize());
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode TauSmearingAlg ::
+  execute ()
+  {
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode {
+        ANA_CHECK (m_smearingTool->applySystematicVariation (sys));
+        xAOD::TauJetContainer *taus = nullptr;
+        ANA_CHECK (m_tauHandle.getCopy (taus, sys));
+        for (xAOD::TauJet *tau : *taus)
+        {
+          if (m_preselection.getBool (*tau))
+          {
+            ANA_CHECK_CORRECTION (m_outOfValidity, *tau, m_smearingTool->applyCorrection (*tau));
+          }
+        }
+        return StatusCode::SUCCESS;
+      });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/Root/TauTruthMatchingAlg.cxx b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/Root/TauTruthMatchingAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..d679581d9d9049a8c62075e0d82296727c8e0abc
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/Root/TauTruthMatchingAlg.cxx
@@ -0,0 +1,60 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+//
+// includes
+//
+
+#include <TauAnalysisAlgorithms/TauTruthMatchingAlg.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  TauTruthMatchingAlg ::
+  TauTruthMatchingAlg (const std::string& name, 
+                     ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+    , m_matchingTool ("TauAnalysisTools::TauTruthMatchingTool", this)
+  {
+    declareProperty ("matchingTool", m_matchingTool, "the matching tool we apply");
+  }
+
+
+
+  StatusCode TauTruthMatchingAlg ::
+  initialize ()
+  {
+    ANA_CHECK (m_matchingTool.retrieve());
+    m_systematicsList.addHandle (m_tauHandle);
+    ANA_CHECK (m_systematicsList.initialize());
+    ANA_CHECK (m_preselection.initialize());
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode TauTruthMatchingAlg ::
+  execute ()
+  {
+    return m_systematicsList.foreach ([&] (const CP::SystematicSet& sys) -> StatusCode {
+        xAOD::TauJetContainer *taus = nullptr;
+        ANA_CHECK (m_tauHandle.getCopy (taus, sys));
+        for (xAOD::TauJet *tau : *taus)
+        {
+          if (m_preselection.getBool (*tau))
+          {
+            m_matchingTool->applyTruthMatch (*tau);
+          }
+        }
+        return StatusCode::SUCCESS;
+      });
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/TauAnalysisAlgorithms/DiTauEfficiencyCorrectionsAlg.h b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/TauAnalysisAlgorithms/DiTauEfficiencyCorrectionsAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..194af5403133fc1bef8ec19bea7923f213b1945f
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/TauAnalysisAlgorithms/DiTauEfficiencyCorrectionsAlg.h
@@ -0,0 +1,72 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef TAU_ANALYSIS_ALGORITHMS__DI_TAU_EFFICIENCY_CORRECTIONS_ALG_H
+#define TAU_ANALYSIS_ALGORITHMS__DI_TAU_EFFICIENCY_CORRECTIONS_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <TauAnalysisTools/IDiTauEfficiencyCorrectionsTool.h>
+#include <SelectionHelpers/OutOfValidityHelper.h>
+#include <SelectionHelpers/SelectionReadHandle.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+#include <SystematicsHandles/SysReadHandle.h>
+#include <xAODTau/DiTauJetContainer.h>
+
+namespace CP
+{
+  /// \brief an algorithm for calling \ref IDiTauEfficiencyCorrectionsTool
+
+  class DiTauEfficiencyCorrectionsAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    DiTauEfficiencyCorrectionsAlg (const std::string& name, 
+                                   ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+    
+
+
+    /// \brief the smearing tool
+  private:
+    ToolHandle<TauAnalysisTools::IDiTauEfficiencyCorrectionsTool> m_efficiencyCorrectionsTool;
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the tau collection we run on
+  private:
+    SysCopyHandle<xAOD::DiTauJetContainer> m_tauHandle {
+      this, "taus", "DiTauJets", "the tau collection to run on"};
+
+    /// \brief the preselection we apply to our input
+  private:
+    SelectionReadHandle m_preselection {
+      this, "preselection", "", "the preselection to apply"};
+
+    /// \brief the helper for OutOfValidity results
+  private:
+    OutOfValidityHelper m_outOfValidity {this};
+
+    /// \brief the decoration for the tau scale factor
+  private:
+    std::string m_scaleFactorDecoration;
+
+    /// \brief the accessor for \ref m_scaleFactorDecoration
+  private:
+    std::unique_ptr<const SG::AuxElement::Accessor<float> > m_scaleFactorAccessor;
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/TauAnalysisAlgorithms/DiTauSmearingAlg.h b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/TauAnalysisAlgorithms/DiTauSmearingAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..9610128e082e3e0e461294b5821fad2426efe043
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/TauAnalysisAlgorithms/DiTauSmearingAlg.h
@@ -0,0 +1,64 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+#ifndef TAU_ANALYSIS_ALGORITHMS__DI_TAU_CALIBRATION_AND_SMEARING_ALG_H
+#define TAU_ANALYSIS_ALGORITHMS__DI_TAU_CALIBRATION_AND_SMEARING_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <TauAnalysisTools/IDiTauSmearingTool.h>
+#include <SelectionHelpers/OutOfValidityHelper.h>
+#include <SelectionHelpers/SelectionReadHandle.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+#include <xAODTau/DiTauJetContainer.h>
+
+namespace CP
+{
+  /// \brief an algorithm for calling \ref IDiTauSmearingTool
+
+  class DiTauSmearingAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    DiTauSmearingAlg (const std::string& name, 
+                                   ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+    
+
+
+    /// \brief the smearing tool
+  private:
+    ToolHandle<TauAnalysisTools::IDiTauSmearingTool> m_smearingTool;
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the tau collection we run on
+  private:
+    SysCopyHandle<xAOD::DiTauJetContainer> m_tauHandle {
+      this, "taus", "DiTauJets", "the tau collection to run on"};
+
+    /// \brief the preselection we apply to our input
+  private:
+    SelectionReadHandle m_preselection {
+      this, "preselection", "", "the preselection to apply"};
+
+    /// \brief the helper for OutOfValidity results
+  private:
+    OutOfValidityHelper m_outOfValidity {this};
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/TauAnalysisAlgorithms/DiTauTruthMatchingAlg.h b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/TauAnalysisAlgorithms/DiTauTruthMatchingAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..a93a3aaf61726eaf6a52e2d82079aaa9059107ae
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/TauAnalysisAlgorithms/DiTauTruthMatchingAlg.h
@@ -0,0 +1,59 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+#ifndef TAU_ANALYSIS_ALGORITHMS__DI_TAU_TRUTH_MATCHING_ALG_H
+#define TAU_ANALYSIS_ALGORITHMS__DI_TAU_TRUTH_MATCHING_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <TauAnalysisTools/IDiTauTruthMatchingTool.h>
+#include <SelectionHelpers/SelectionReadHandle.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+#include <xAODTau/DiTauJetContainer.h>
+
+namespace CP
+{
+  /// \brief an algorithm for calling \ref IDiTauTruthMatchingTool
+
+  class DiTauTruthMatchingAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    DiTauTruthMatchingAlg (const std::string& name, 
+                                   ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+    
+
+
+    /// \brief the matching tool
+  private:
+    ToolHandle<TauAnalysisTools::IDiTauTruthMatchingTool> m_matchingTool;
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the tau collection we run on
+  private:
+    SysCopyHandle<xAOD::DiTauJetContainer> m_tauHandle {
+      this, "taus", "DiTauJets", "the tau collection to run on"};
+
+    /// \brief the preselection we apply to our input
+  private:
+    SelectionReadHandle m_preselection {
+      this, "preselection", "", "the preselection to apply"};
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/TauAnalysisAlgorithms/TauAnalysisAlgorithmsDict.h b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/TauAnalysisAlgorithms/TauAnalysisAlgorithmsDict.h
new file mode 100644
index 0000000000000000000000000000000000000000..146cf87c4538b12e98bc258f8d6ab18d6f30be02
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/TauAnalysisAlgorithms/TauAnalysisAlgorithmsDict.h
@@ -0,0 +1,18 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef TAU_ANALYSIS_ALGORITHMS__TAU_ANALYSIS_ALGORITHMS_DICT_H
+#define TAU_ANALYSIS_ALGORITHMS__TAU_ANALYSIS_ALGORITHMS_DICT_H
+
+#include <TauAnalysisAlgorithms/DiTauEfficiencyCorrectionsAlg.h>
+#include <TauAnalysisAlgorithms/DiTauSmearingAlg.h>
+#include <TauAnalysisAlgorithms/DiTauTruthMatchingAlg.h>
+#include <TauAnalysisAlgorithms/TauEfficiencyCorrectionsAlg.h>
+#include <TauAnalysisAlgorithms/TauSmearingAlg.h>
+#include <TauAnalysisAlgorithms/TauTruthMatchingAlg.h>
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/TauAnalysisAlgorithms/TauEfficiencyCorrectionsAlg.h b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/TauAnalysisAlgorithms/TauEfficiencyCorrectionsAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..321199c88c5b0cbfe57ac8fbe2f83ceaea174f83
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/TauAnalysisAlgorithms/TauEfficiencyCorrectionsAlg.h
@@ -0,0 +1,70 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef TAU_ANALYSIS_ALGORITHMS__TAU_EFFICIENCY_CORRECTIONS_ALG_H
+#define TAU_ANALYSIS_ALGORITHMS__TAU_EFFICIENCY_CORRECTIONS_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <TauAnalysisTools/ITauEfficiencyCorrectionsTool.h>
+#include <SelectionHelpers/OutOfValidityHelper.h>
+#include <SelectionHelpers/SelectionReadHandle.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <SystematicsHandles/SysDecorationHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+#include <SystematicsHandles/SysReadHandle.h>
+#include <xAODTau/TauJetContainer.h>
+
+namespace CP
+{
+  /// \brief an algorithm for calling \ref ITauEfficiencyCorrectionsTool
+
+  class TauEfficiencyCorrectionsAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    TauEfficiencyCorrectionsAlg (const std::string& name, 
+                                   ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+    
+
+
+    /// \brief the smearing tool
+  private:
+    ToolHandle<TauAnalysisTools::ITauEfficiencyCorrectionsTool> m_efficiencyCorrectionsTool;
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the tau collection we run on
+  private:
+    SysCopyHandle<xAOD::TauJetContainer> m_tauHandle {
+      this, "taus", "TauJets", "the tau collection to run on"};
+
+    /// \brief the preselection we apply to our input
+  private:
+    SelectionReadHandle m_preselection {
+      this, "preselection", "", "the preselection to apply"};
+
+    /// \brief the helper for OutOfValidity results
+  private:
+    OutOfValidityHelper m_outOfValidity {this};
+
+    /// \brief the decoration for the tau scale factor
+  private:
+    SysDecorationHandle<float> m_scaleFactorDecoration {
+      this, "scaleFactorDecoration", "", "the decoration for the tau efficiency scale factor"};
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/TauAnalysisAlgorithms/TauSmearingAlg.h b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/TauAnalysisAlgorithms/TauSmearingAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..fa5f903e504949f9a2a60d7f92d0161ec8423649
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/TauAnalysisAlgorithms/TauSmearingAlg.h
@@ -0,0 +1,64 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+#ifndef TAU_ANALYSIS_ALGORITHMS__TAU_CALIBRATION_AND_SMEARING_ALG_H
+#define TAU_ANALYSIS_ALGORITHMS__TAU_CALIBRATION_AND_SMEARING_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <TauAnalysisTools/ITauSmearingTool.h>
+#include <SelectionHelpers/OutOfValidityHelper.h>
+#include <SelectionHelpers/SelectionReadHandle.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+#include <xAODTau/TauJetContainer.h>
+
+namespace CP
+{
+  /// \brief an algorithm for calling \ref ITauSmearingTool
+
+  class TauSmearingAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    TauSmearingAlg (const std::string& name, 
+                                   ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+    
+
+
+    /// \brief the smearing tool
+  private:
+    ToolHandle<TauAnalysisTools::ITauSmearingTool> m_smearingTool;
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the tau collection we run on
+  private:
+    SysCopyHandle<xAOD::TauJetContainer> m_tauHandle {
+      this, "taus", "TauJets", "the tau collection to run on"};
+
+    /// \brief the preselection we apply to our input
+  private:
+    SelectionReadHandle m_preselection {
+      this, "preselection", "", "the preselection to apply"};
+
+    /// \brief the helper for OutOfValidity results
+  private:
+    OutOfValidityHelper m_outOfValidity {this};
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/TauAnalysisAlgorithms/TauTruthMatchingAlg.h b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/TauAnalysisAlgorithms/TauTruthMatchingAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..0bd2409052c34bd10dd8fcd40157123108aca6e2
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/TauAnalysisAlgorithms/TauTruthMatchingAlg.h
@@ -0,0 +1,59 @@
+/*
+  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+#ifndef TAU_ANALYSIS_ALGORITHMS__TAU_TRUTH_MATCHING_ALG_H
+#define TAU_ANALYSIS_ALGORITHMS__TAU_TRUTH_MATCHING_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <TauAnalysisTools/ITauTruthMatchingTool.h>
+#include <SelectionHelpers/SelectionReadHandle.h>
+#include <SystematicsHandles/SysCopyHandle.h>
+#include <SystematicsHandles/SysListHandle.h>
+#include <xAODTau/TauJetContainer.h>
+
+namespace CP
+{
+  /// \brief an algorithm for calling \ref ITauTruthMatchingTool
+
+  class TauTruthMatchingAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    TauTruthMatchingAlg (const std::string& name, 
+                                   ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+    
+
+
+    /// \brief the matching tool
+  private:
+    ToolHandle<TauAnalysisTools::ITauTruthMatchingTool> m_matchingTool;
+
+    /// \brief the systematics list we run
+  private:
+    SysListHandle m_systematicsList {this};
+
+    /// \brief the tau collection we run on
+  private:
+    SysCopyHandle<xAOD::TauJetContainer> m_tauHandle {
+      this, "taus", "TauJets", "the tau collection to run on"};
+
+    /// \brief the preselection we apply to our input
+  private:
+    SelectionReadHandle m_preselection {
+      this, "preselection", "", "the preselection to apply"};
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/TauAnalysisAlgorithms/selection.xml b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/TauAnalysisAlgorithms/selection.xml
new file mode 100644
index 0000000000000000000000000000000000000000..a815f8e0a47edce1db6a49c6270e37aa573dfa5c
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/TauAnalysisAlgorithms/selection.xml
@@ -0,0 +1,10 @@
+<lcgdict>
+
+   <class name="CP::DiTauEfficiencyCorrectionsAlg" />
+   <class name="CP::DiTauSmearingAlg" />
+   <class name="CP::DiTauTruthMatchingAlg" />
+   <class name="CP::TauEfficiencyCorrectionsAlg" />
+   <class name="CP::TauSmearingAlg" />
+   <class name="CP::TauTruthMatchingAlg" />
+   
+</lcgdict>
diff --git a/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/data/tau_selection_baseline.conf b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/data/tau_selection_baseline.conf
new file mode 100644
index 0000000000000000000000000000000000000000..6fa54ab13cdc849e78564ae3e7621214296fa239
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/data/tau_selection_baseline.conf
@@ -0,0 +1,9 @@
+# Cuts to perform, need to be specified below
+SelectionCuts: PtMin AbsEtaRegion AbsCharge NTracks JetIDWP
+
+PtMin: 20
+AbsEtaRegion: 0; 1.37; 1.52; 2.5
+AbsCharge: 1
+NTracks: 1; 3
+
+# EOF
diff --git a/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/data/tau_selection_baseline_legacy.conf b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/data/tau_selection_baseline_legacy.conf
new file mode 100644
index 0000000000000000000000000000000000000000..bf5b84d0e25f8aeb5c4d1a5678d8f2d493d4b72c
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/data/tau_selection_baseline_legacy.conf
@@ -0,0 +1,10 @@
+# Cuts to perform, need to be specified below
+SelectionCuts: PtMin AbsEtaRegion AbsCharge NTracks JetIDWP EleOLR EleBDTWP
+
+PtMin: 20
+AbsEtaRegion: 0; 1.37; 1.52; 2.5
+AbsCharge: 1
+NTracks: 1; 3
+EleOLR: True
+
+# EOF
diff --git a/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/data/tau_selection_loose.conf b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/data/tau_selection_loose.conf
new file mode 100644
index 0000000000000000000000000000000000000000..5c3c8e8d125deeeea2ead9fe63cf5914d3c9e52e
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/data/tau_selection_loose.conf
@@ -0,0 +1,11 @@
+# Cuts to perform, need to be specified below
+SelectionCuts: PtMin AbsEtaRegion AbsCharge NTracks JetIDWP EleOLR EleBDTWP
+
+PtMin: 20
+AbsEtaRegion: 0; 1.37; 1.52; 2.5
+AbsCharge: 1
+NTracks: 1; 3
+JetIDWP: JETIDRNNLOOSE
+EleBDTWP: ELEIDBDTLOOSE
+
+# EOF
diff --git a/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/data/tau_selection_loose_legacy.conf b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/data/tau_selection_loose_legacy.conf
new file mode 100644
index 0000000000000000000000000000000000000000..5cd30ef26f94075d54dd63ad4e238ff860efb9d0
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/data/tau_selection_loose_legacy.conf
@@ -0,0 +1,12 @@
+# Cuts to perform, need to be specified below
+SelectionCuts: PtMin AbsEtaRegion AbsCharge NTracks JetIDWP EleOLR EleBDTWP
+
+PtMin: 20
+AbsEtaRegion: 0; 1.37; 1.52; 2.5
+AbsCharge: 1
+NTracks: 1; 3
+JetIDWP: JETIDBDTLOOSE
+EleOLR: True
+EleBDTWP: ELEIDBDTOLDLOOSE
+
+# EOF
\ No newline at end of file
diff --git a/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/data/tau_selection_medium.conf b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/data/tau_selection_medium.conf
new file mode 100644
index 0000000000000000000000000000000000000000..db9c7e1eff77ed7a3a21c4bad8a40ffba4bc4f6b
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/data/tau_selection_medium.conf
@@ -0,0 +1,11 @@
+# Cuts to perform, need to be specified below
+SelectionCuts: PtMin AbsEtaRegion AbsCharge NTracks JetIDWP EleOLR EleBDTWP
+
+PtMin: 20
+AbsEtaRegion: 0; 1.37; 1.52; 2.5
+AbsCharge: 1
+NTracks: 1; 3
+JetIDWP: JETIDRNNMEDIUM
+EleBDTWP: ELEIDBDTLOOSE
+
+# EOF
diff --git a/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/data/tau_selection_medium_legacy.conf b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/data/tau_selection_medium_legacy.conf
new file mode 100644
index 0000000000000000000000000000000000000000..73d26a97e56a9a1c92084d0b84dcf191f4f558c6
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/data/tau_selection_medium_legacy.conf
@@ -0,0 +1,12 @@
+# Cuts to perform, need to be specified below
+SelectionCuts: PtMin AbsEtaRegion AbsCharge NTracks JetIDWP EleOLR EleBDTWP
+
+PtMin: 20
+AbsEtaRegion: 0; 1.37; 1.52; 2.5
+AbsCharge: 1
+NTracks: 1; 3
+JetIDWP: JETIDBDTMEDIUM
+EleOLR: True
+EleBDTWP: ELEIDBDTOLDLOOSE
+
+# EOF
diff --git a/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/data/tau_selection_noid.conf b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/data/tau_selection_noid.conf
new file mode 100644
index 0000000000000000000000000000000000000000..8d12d46d42ada61fc596208eb60bedc1608ad48d
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/data/tau_selection_noid.conf
@@ -0,0 +1,11 @@
+# Cuts to perform, need to be specified below
+SelectionCuts: PtMin AbsEtaRegion AbsCharge NTracks JetIDWP EleOLR EleBDTWP
+
+PtMin: 20
+AbsEtaRegion: 0; 1.37; 1.52; 2.5
+AbsCharge: 1
+NTracks: 1; 3
+JetIDWP: JETIDNONE
+EleBDTWP: ELEIDBDTLOOSE
+
+# EOF
diff --git a/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/data/tau_selection_noid_legacy.conf b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/data/tau_selection_noid_legacy.conf
new file mode 100644
index 0000000000000000000000000000000000000000..c58146ecc3074d2b16bb14808d35575db6b2ac19
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/data/tau_selection_noid_legacy.conf
@@ -0,0 +1,12 @@
+# Cuts to perform, need to be specified below
+SelectionCuts: PtMin AbsEtaRegion AbsCharge NTracks JetIDWP EleOLR EleBDTWP
+
+PtMin: 20
+AbsEtaRegion: 0; 1.37; 1.52; 2.5
+AbsCharge: 1
+NTracks: 1; 3
+JetIDWP: JETIDNONE
+EleOLR: True
+EleBDTWP: ELEIDBDTOLDLOOSE
+
+# EOF
diff --git a/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/data/tau_selection_tight.conf b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/data/tau_selection_tight.conf
new file mode 100644
index 0000000000000000000000000000000000000000..862691f0f17e2ca2a47ae0d1371796f6394dc77e
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/data/tau_selection_tight.conf
@@ -0,0 +1,11 @@
+# Cuts to perform, need to be specified below
+SelectionCuts: PtMin AbsEtaRegion AbsCharge NTracks JetIDWP EleOLR EleBDTWP
+
+PtMin: 20
+AbsEtaRegion: 0; 1.37; 1.52; 2.5
+AbsCharge: 1
+NTracks: 1; 3
+JetIDWP: JETIDRNNTIGHT
+EleBDTWP: ELEIDBDTLOOSE
+
+# EOF
diff --git a/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/data/tau_selection_tight_legacy.conf b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/data/tau_selection_tight_legacy.conf
new file mode 100644
index 0000000000000000000000000000000000000000..c855481512ac2658ee2c07c76ff9d5c906f2ce45
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/data/tau_selection_tight_legacy.conf
@@ -0,0 +1,12 @@
+# Cuts to perform, need to be specified below
+SelectionCuts: PtMin AbsEtaRegion AbsCharge NTracks JetIDWP EleOLR EleBDTWP
+
+PtMin: 20
+AbsEtaRegion: 0; 1.37; 1.52; 2.5
+AbsCharge: 1
+NTracks: 1; 3
+JetIDWP: JETIDBDTTIGHT
+EleOLR: True
+EleBDTWP: ELEIDBDTOLDLOOSE
+
+# EOF
\ No newline at end of file
diff --git a/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/data/tau_selection_veryloose.conf b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/data/tau_selection_veryloose.conf
new file mode 100644
index 0000000000000000000000000000000000000000..1f3c0fb379e71c6debc24fbb2c3fdcf06bb85abc
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/data/tau_selection_veryloose.conf
@@ -0,0 +1,11 @@
+# Cuts to perform, need to be specified below
+SelectionCuts: PtMin AbsEtaRegion AbsCharge NTracks JetIDWP EleOLR EleBDTWP
+
+PtMin: 20
+AbsEtaRegion: 0; 1.37; 1.52; 2.5
+AbsCharge: 1
+NTracks: 1; 3
+JetIDWP: JETIDRNNVERYLOOSE
+EleBDTWP: ELEIDBDTLOOSE
+
+# EOF
diff --git a/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/data/tau_selection_veryloose_legacy.conf b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/data/tau_selection_veryloose_legacy.conf
new file mode 100644
index 0000000000000000000000000000000000000000..5266e9f6875bbe765ff2b5621ac7cb83d6153191
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/data/tau_selection_veryloose_legacy.conf
@@ -0,0 +1,12 @@
+# Cuts to perform, need to be specified below
+SelectionCuts: PtMin AbsEtaRegion AbsCharge NTracks JetIDWP EleOLR EleBDTWP
+
+PtMin: 20
+AbsEtaRegion: 0; 1.37; 1.52; 2.5
+AbsCharge: 1
+NTracks: 1; 3
+JetIDWP: JETIDBDTVERYLOOSE
+EleOLR: True
+EleBDTWP: ELEIDBDTOLDLOOSE
+
+# EOF
\ No newline at end of file
diff --git a/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/python/DiTauAnalysisSequence.py b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/python/DiTauAnalysisSequence.py
new file mode 100644
index 0000000000000000000000000000000000000000..b95773d5252648f511046e7a1d674c6b1d24075b
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/python/DiTauAnalysisSequence.py
@@ -0,0 +1,100 @@
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+
+# AnaAlgorithm import(s):
+from AnaAlgorithm.AnaAlgSequence import AnaAlgSequence
+from AnaAlgorithm.DualUseConfig import createAlgorithm, addPrivateTool
+
+def makeDiTauAnalysisSequence( dataType, workingPoint,
+                             deepCopyOutput = False, postfix = '' ):
+    """Create a tau analysis algorithm sequence
+
+    Keyword arguments:
+      dataType -- The data type to run on ("data", "mc" or "afii")
+      deepCopyOutput -- If set to 'True', the output containers will be
+                        standalone, deep copies (slower, but needed for xAOD
+                        output writing)
+      postfix -- a postfix to apply to decorations and algorithm
+                 names.  this is mostly used/needed when using this
+                 sequence with multiple working points to ensure all
+                 names are unique.
+    """
+
+    if dataType not in ["data", "mc", "afii"] :
+        raise ValueError ("invalid data type: " + dataType)
+
+    if postfix != '' :
+        postfix = '_' + postfix
+        pass
+
+    splitWP = workingPoint.split ('.')
+    if len (splitWP) != 1 :
+        raise ValueError ('working point should be of format "quality", not ' + workingPoint)
+
+    # using enum value from: https://gitlab.cern.ch/atlas/athena/blob/21.2/PhysicsAnalysis/TauID/TauAnalysisTools/TauAnalysisTools/Enums.h
+    # the dictionary is missing in Athena, so hard-coding values here
+    if splitWP[0] == 'Tight' :
+        IDLevel = 4 # ROOT.TauAnalysisTools.JETIDBDTTIGHT
+        pass
+    elif splitWP[0] == 'Medium' :
+        IDLevel = 3 # ROOT.TauAnalysisTools.JETIDBDTMEDIUM
+        pass
+    elif splitWP[0] == 'Loose' :
+        IDLevel = 2 # ROOT.TauAnalysisTools.JETIDBDTLOOSE
+        pass
+    else :
+        raise ValueError ("invalid tau quality: \"" + splitWP[0] +
+                          "\", allowed values are Tight, Medium, Loose, " +
+                          "VeryLoose")
+
+    # Create the analysis algorithm sequence object:
+    seq = AnaAlgSequence( "DiTauAnalysisSequence" + postfix )
+
+    # Set up the tau 4-momentum smearing algorithm:
+    alg = createAlgorithm( 'CP::DiTauSmearingAlg', 'DiTauSmearingAlg' + postfix )
+    addPrivateTool( alg, 'smearingTool', 'TauAnalysisTools::DiTauSmearingTool' )
+    seq.append( alg, inputPropName = 'taus', outputPropName = 'tausOut',
+                affectingSystematics = '(^TAUS_TRUEHADDITAU_SME_TES_.*)',
+                stageName = 'calibration' )
+
+    # Set up an algorithm dumping the properties of the taus, for debugging:
+    alg = createAlgorithm( 'CP::KinematicHistAlg', 'DiTauKinematicDumperAlg' + postfix )
+    alg.histPattern = "tau_%VAR%_%SYS%"
+    seq.append( alg, inputPropName = 'input',
+                stageName = 'selection' )
+
+    # Set up the algorithm calculating the efficiency scale factors for the
+    # taus:
+    alg = createAlgorithm( 'CP::DiTauEfficiencyCorrectionsAlg',
+                           'DiTauEfficiencyCorrectionsAlg' + postfix )
+    addPrivateTool( alg, 'efficiencyCorrectionsTool',
+                    'TauAnalysisTools::DiTauEfficiencyCorrectionsTool' )
+    alg.efficiencyCorrectionsTool.IDLevel = IDLevel
+    alg.scaleFactorDecoration = 'tau_effSF' + postfix
+    # alg.outOfValidity = 2 #silent
+    # alg.outOfValidityDeco = "bad_eff"
+    seq.append( alg, inputPropName = 'taus', outputPropName = 'tausOut',
+                affectingSystematics = '(^TAUS_TRUEHADDITAU_EFF_JETID_.*)',
+                stageName = 'efficiency' )
+
+    # Set up the tau truth matching algorithm:
+    if dataType != 'data':
+        alg = createAlgorithm( 'CP::DiTauTruthMatchingAlg',
+                               'DiTauTruthMatchingAlg' + postfix )
+        addPrivateTool( alg, 'matchingTool',
+                        'TauAnalysisTools::DiTauTruthMatchingTool' )
+        alg.matchingTool.WriteTruthTaus = 1
+        seq.append( alg, inputPropName = 'taus', outputPropName = 'tausOut',
+                    stageName = 'selection' )
+        pass
+
+    # Set up a final deep copy making algorithm if requested:
+    if deepCopyOutput:
+        alg = createAlgorithm( 'CP::AsgViewFromSelectionAlg',
+                               'DiTauDeepCopyMaker' + postfix )
+        alg.deepCopy = True
+        seq.append( alg, inputPropName = 'input', outputPropName = 'output',
+                    stageName = 'selection' )
+        pass
+
+    # Return the sequence:
+    return seq
diff --git a/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/python/TauAnalysisAlgorithmsTest.py b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/python/TauAnalysisAlgorithmsTest.py
new file mode 100644
index 0000000000000000000000000000000000000000..7c496b06b12be9f4bdcd56a54d5985fa44af3c3f
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/python/TauAnalysisAlgorithmsTest.py
@@ -0,0 +1,35 @@
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+#
+# @author Nils Krumnack
+
+from AnaAlgorithm.AlgSequence import AlgSequence
+from AnaAlgorithm.DualUseConfig import createAlgorithm
+
+def makeSequence (dataType) :
+
+    algSeq = AlgSequence()
+
+    # Set up the systematics loader/handler algorithm:
+    sysLoader = createAlgorithm( 'CP::SysListLoaderAlg', 'SysLoaderAlg' )
+    sysLoader.sigmaRecommended = 1
+    algSeq += sysLoader
+
+    # Include, and then set up the tau analysis algorithm sequence:
+    from TauAnalysisAlgorithms.TauAnalysisSequence import makeTauAnalysisSequence
+    tauSequence = makeTauAnalysisSequence( dataType, 'Tight', postfix = 'tight',
+                                           enableCutflow=True, enableKinematicHistograms=True )
+    tauSequence.configure( inputName = 'TauJets', outputName = 'AnalysisTauJets_%SYS%' )
+
+    # Add the sequence to the job:
+    algSeq += tauSequence
+
+    # Include, and then set up the tau analysis algorithm sequence:
+    from TauAnalysisAlgorithms.DiTauAnalysisSequence import makeDiTauAnalysisSequence
+    diTauSequence = makeDiTauAnalysisSequence( dataType, 'Tight', postfix = 'tight' )
+    diTauSequence.configure( inputName = 'DiTauJets', outputName = 'AnalysisDiTauJets_%SYS%' )
+
+    # Add the sequence to the job:
+    # disabling this, the standard test files don't have DiTauJets
+    # algSeq += diTauSequence
+
+    return algSeq
diff --git a/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/python/TauAnalysisSequence.py b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/python/TauAnalysisSequence.py
new file mode 100644
index 0000000000000000000000000000000000000000..c48a2fc3b96f012405440ffc50436e6a8a188216
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/python/TauAnalysisSequence.py
@@ -0,0 +1,170 @@
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+
+# AnaAlgorithm import(s):
+from AnaAlgorithm.AnaAlgSequence import AnaAlgSequence
+from AnaAlgorithm.DualUseConfig import createAlgorithm, addPrivateTool, \
+                                       createPublicTool
+
+def makeTauAnalysisSequence( dataType, workingPoint, postfix = '',
+                             legacyRecommendations = False,
+                             deepCopyOutput = False,
+                             shallowViewOutput = True,
+                             rerunTruthMatching = True,
+                             enableCutflow = False,
+                             enableKinematicHistograms = False ):
+    """Create a tau analysis algorithm sequence
+
+    Keyword arguments:
+      dataType -- The data type to run on ("data", "mc" or "afii")
+      legacyRecommendations -- use legacy tau BDT and electron veto recommendations
+      deepCopyOutput -- If set to 'True', the output containers will be
+                        standalone, deep copies (slower, but needed for xAOD
+                        output writing)
+      shallowViewOutput -- Create a view container if required
+      postfix -- a postfix to apply to decorations and algorithm
+                 names.  this is mostly used/needed when using this
+                 sequence with multiple working points to ensure all
+                 names are unique.
+      rerunTruthMatching -- Whether or not to rerun truth matching
+      enableCutflow -- Whether or not to dump the cutflow
+      enableKinematicHistograms -- Whether or not to dump the kinematic histograms
+    """
+
+    if dataType not in ["data", "mc", "afii"] :
+        raise ValueError ("invalid data type: " + dataType)
+
+    if postfix != '' :
+        postfix = '_' + postfix
+        pass
+
+    # Make sure selection options make sense
+    if deepCopyOutput and shallowViewOutput:
+        raise ValueError ("deepCopyOutput and shallowViewOutput can't both be true!")
+
+    splitWP = workingPoint.split ('.')
+    if len (splitWP) != 1 :
+        raise ValueError ('working point should be of format "quality", not ' + workingPoint)
+
+    nameFormat = 'TauAnalysisAlgorithms/tau_selection_{}.conf'
+    if legacyRecommendations:
+        nameFormat = 'TauAnalysisAlgorithms/tau_selection_{}_legacy.conf'
+
+    if splitWP[0] not in ['Tight', 'Medium', 'Loose', 'VeryLoose', 'NoID', 'Baseline'] :
+        raise ValueError ("invalid tau quality: \"" + splitWP[0] +
+                          "\", allowed values are Tight, Medium, Loose, " +
+                          "VeryLoose, NoID, Baseline")
+    inputfile = nameFormat.format(splitWP[0].lower())
+
+    # Create the analysis algorithm sequence object:
+    seq = AnaAlgSequence( "TauAnalysisSequence" + postfix )
+
+    # Variables keeping track of the selections being applied.
+    selectionDecorNames = []
+    selectionDecorCount = []
+
+    # Setup the tau selection tool
+    selectionTool = createPublicTool( 'TauAnalysisTools::TauSelectionTool',
+                                      'TauSelectionTool' + postfix)
+    selectionTool.ConfigPath = inputfile
+    seq.addPublicTool( selectionTool, stageName = 'selection' )
+
+    # Set up the tau truth matching algorithm:
+    if rerunTruthMatching and dataType != 'data':
+        alg = createAlgorithm( 'CP::TauTruthMatchingAlg',
+                               'TauTruthMatchingAlg' + postfix )
+        addPrivateTool( alg, 'matchingTool',
+                        'TauAnalysisTools::TauTruthMatchingTool' )
+        alg.matchingTool.WriteTruthTaus = 1
+        seq.append( alg, inputPropName = 'taus', outputPropName = 'tausOut',
+                    stageName = 'selection' )
+        pass
+
+    # Set up the tau 4-momentum smearing algorithm:
+    alg = createAlgorithm( 'CP::TauSmearingAlg', 'TauSmearingAlg' + postfix )
+    addPrivateTool( alg, 'smearingTool', 'TauAnalysisTools::TauSmearingTool' )
+    seq.append( alg, inputPropName = 'taus', outputPropName = 'tausOut',
+                affectingSystematics = '(^TAUS_TRUEHADTAU_SME_TES_.*)',
+                stageName = 'calibration' )
+
+    # Set up the algorithm selecting taus:
+    alg = createAlgorithm( 'CP::AsgSelectionAlg', 'TauSelectionAlg' + postfix )
+    alg.selectionTool = '%s/%s' % \
+        ( selectionTool.getType(), selectionTool.getName() )
+    alg.selectionDecoration = 'selected_tau' + postfix + ',as_bits'
+    seq.append( alg, inputPropName = 'particles',
+                stageName = 'selection' )
+    selectionDecorNames.append( alg.selectionDecoration )
+    selectionDecorCount.append( 6 )
+
+    # Set up the algorithm calculating the efficiency scale factors for the
+    # taus:
+    if dataType != 'data':
+        alg = createAlgorithm( 'CP::TauEfficiencyCorrectionsAlg',
+                               'TauEfficiencyCorrectionsAlg' + postfix )
+        addPrivateTool( alg, 'efficiencyCorrectionsTool',
+                        'TauAnalysisTools::TauEfficiencyCorrectionsTool' )
+        alg.efficiencyCorrectionsTool.TauSelectionTool = '%s/%s' % \
+            ( selectionTool.getType(), selectionTool.getName() )
+        alg.scaleFactorDecoration = 'tau_effSF' + postfix + '_%SYS%'
+        alg.scaleFactorDecorationRegex = '(^TAUS_TRUEELECTRON_EFF_.*)' \
+                                         + '|(^TAUS_TRUEHADTAU_EFF_RECO.*)' \
+                                         + '|(^TAUS_TRUEHADTAU_EFF_RNNID.*)' \
+                                         + '|(^TAUS_TRUEHADTAU_EFF_JETID.*)' \
+                                         + '|(^TAUS_TRUEHADTAU_EFF_RECO.*)' \
+                                         + '|(^TAUS_TRUEHADTAU_EFF_ELEOLR.*)'
+        alg.outOfValidity = 2 #silent
+        alg.outOfValidityDeco = 'bad_eff' + postfix
+        seq.append( alg, inputPropName = 'taus',
+                    affectingSystematics = '(^TAUS_TRUEELECTRON_EFF_.*)' \
+                                           + '|(^TAUS_TRUEHADTAU_EFF_RECO.*)' \
+                                           + '|(^TAUS_TRUEHADTAU_EFF_RNNID.*)' \
+                                           + '|(^TAUS_TRUEHADTAU_EFF_JETID.*)' \
+                                           + '|(^TAUS_TRUEHADTAU_EFF_RECO.*)' \
+                                           + '|(^TAUS_TRUEHADTAU_EFF_ELEOLR.*)',
+                    stageName = 'efficiency' )
+
+    # Set up an algorithm used to create tau selection cutflow:
+    if enableCutflow:
+        alg = createAlgorithm( 'CP::ObjectCutFlowHistAlg', 'TauCutFlowDumperAlg' + postfix )
+        alg.histPattern = 'tau_cflow_%SYS%'
+        alg.selection = selectionDecorNames[ : ]
+        alg.selectionNCuts = selectionDecorCount[ : ]
+        seq.append( alg, inputPropName = 'input', stageName = 'selection' )
+
+    # Set up an algorithm used for decorating baseline tau selection:
+    alg = createAlgorithm( 'CP::AsgSelectionAlg',
+                           'TauSelectionSummary' + postfix )
+    addPrivateTool( alg, 'selectionTool', 'CP::AsgFlagSelectionTool' )
+    alg.selectionTool.selectionFlags = selectionDecorNames[ : ]
+    alg.selectionDecoration = 'baselineSelection' + postfix + ',as_char'
+    seq.append( alg, inputPropName = 'particles',
+                stageName = 'selection' )
+
+    # Set up an algorithm that makes a view container using the selections
+    # performed previously:
+    if shallowViewOutput:
+        alg = createAlgorithm( 'CP::AsgViewFromSelectionAlg',
+                            'TauViewFromSelectionAlg' + postfix )
+        alg.selection = selectionDecorNames[ : ]
+        seq.append( alg, inputPropName = 'input', outputPropName = 'output',
+                    stageName = 'selection' )
+
+    # Set up an algorithm dumping the kinematic properties of the taus:
+    if enableKinematicHistograms:
+        alg = createAlgorithm( 'CP::KinematicHistAlg', 'TauKinematicDumperAlg' + postfix )
+        alg.preselection = '&&'.join (selectionDecorNames)
+        alg.histPattern = 'tau_%VAR%_%SYS%'
+        seq.append( alg, inputPropName = 'input', stageName = 'selection' )
+
+    # Set up a final deep copy making algorithm if requested:
+    if deepCopyOutput:
+        alg = createAlgorithm( 'CP::AsgViewFromSelectionAlg',
+                               'TauDeepCopyMaker' + postfix )
+        alg.deepCopy = True
+        alg.selection = selectionDecorNames[ : ]
+        seq.append( alg, inputPropName = 'input', outputPropName = 'output',
+                    stageName = 'selection' )
+        pass
+
+    # Return the sequence:
+    return seq
diff --git a/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/python/__init__.py b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/python/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..5ccb446532599c01cc3844e5b1b18db06f721002
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/python/__init__.py
@@ -0,0 +1,3 @@
+# Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+
+__version__ = '1.0.0'
diff --git a/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/share/TauAnalysisAlgorithmsTest_eljob.py b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/share/TauAnalysisAlgorithmsTest_eljob.py
new file mode 100755
index 0000000000000000000000000000000000000000..0b1755dd1058de72bf62c41d341da48ec2301d72
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/share/TauAnalysisAlgorithmsTest_eljob.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+#
+# @author Nils Krumnack
+
+
+# Read the submission directory as a command line argument. You can
+# extend the list of arguments with your private ones later on.
+import optparse
+parser = optparse.OptionParser()
+parser.add_option( '-d', '--data-type', dest = 'data_type',
+                   action = 'store', type = 'string', default = 'data',
+                   help = 'Type of data to run over. Valid options are data, mc, afii' )
+parser.add_option( '-s', '--submission-dir', dest = 'submission_dir',
+                   action = 'store', type = 'string', default = 'submitDir',
+                   help = 'Submission directory for EventLoop' )
+parser.add_option( '-u', '--unit-test', dest='unit_test',
+                   action = 'store_true', default = False,
+                   help = 'Run the job in "unit test mode"' )
+( options, args ) = parser.parse_args()
+
+# Set up (Py)ROOT.
+import ROOT
+ROOT.xAOD.Init().ignore()
+
+# this forces the tau algorithms dictionary to be loaded before
+# anything else, which works around some strange dictionary issues I
+# don't understand.
+ROOT.CP.TauSmearingAlg ("dummy", None)
+
+# ideally we'd run over all of them, but we don't have a mechanism to
+# configure per-sample right now
+
+dataType = options.data_type
+
+if dataType not in ["data", "mc", "afii"] :
+    raise ValueError ("invalid data type: " + dataType)
+
+# Set up the sample handler object. See comments from the C++ macro
+# for the details about these lines.
+import os
+sh = ROOT.SH.SampleHandler()
+sh.setMetaString( 'nc_tree', 'CollectionTree' )
+sample = ROOT.SH.SampleLocal (dataType)
+if dataType == "data" :
+    sample.add (os.getenv ('ASG_TEST_FILE_DATA'))
+    pass
+if dataType == "mc" :
+    sample.add (os.getenv ('ASG_TEST_FILE_MC'))
+    pass
+if dataType == "afii" :
+    sample.add (os.getenv ('ASG_TEST_FILE_MC_AFII'))
+    pass
+sh.add (sample)
+sh.printContent()
+
+# Create an EventLoop job.
+job = ROOT.EL.Job()
+job.sampleHandler( sh )
+job.options().setDouble( ROOT.EL.Job.optMaxEvents, 500 )
+
+from TauAnalysisAlgorithms.TauAnalysisAlgorithmsTest import makeSequence
+algSeq = makeSequence (dataType)
+print algSeq # For debugging
+for alg in algSeq:
+    job.algsAdd( alg )
+    pass
+
+# Find the right output directory:
+submitDir = options.submission_dir
+if options.unit_test:
+    import os
+    import tempfile
+    submitDir = tempfile.mkdtemp( prefix = 'tauTest_'+dataType+'_', dir = os.getcwd() )
+    os.rmdir( submitDir )
+    pass
+
+# Run the job using the direct driver.
+driver = ROOT.EL.DirectDriver()
+driver.submit( job, submitDir )
diff --git a/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/share/TauAnalysisAlgorithmsTest_jobOptions.py b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/share/TauAnalysisAlgorithmsTest_jobOptions.py
new file mode 100644
index 0000000000000000000000000000000000000000..fb9d15adaf94ab997419cd5864a1fd628ce01f6f
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/share/TauAnalysisAlgorithmsTest_jobOptions.py
@@ -0,0 +1,44 @@
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+#
+# @author Nils Krumnack
+
+# User options, which can be set from command line after a "-" character
+# athena EgammaAlgorithmsTest_jobOptions.py - --myOption ...
+from AthenaCommon.AthArgumentParser import AthArgumentParser
+athArgsParser = AthArgumentParser()
+athArgsParser.add_argument("--data-type", action = "store", dest = "data_type",
+                           default = "data",
+                           help = "Type of input to run over. Valid options are 'data', 'mc', 'afii'")
+athArgs = athArgsParser.parse_args()
+
+dataType = athArgs.data_type
+if not dataType in ["data", "mc", "afii"] :
+    raise Exception ("invalid data type: " + dataType)
+
+print("Running on data type: " + dataType)
+
+inputfile = {"data": 'ASG_TEST_FILE_DATA',
+             "mc":   'ASG_TEST_FILE_MC',
+             "afii": 'ASG_TEST_FILE_MC_AFII'}
+
+# Set up the reading of the input file:
+import AthenaRootComps.ReadAthenaxAODHybrid
+theApp.EvtMax = 500
+testFile = os.getenv ( inputfile[dataType] )
+svcMgr.EventSelector.InputCollections = [testFile]
+
+from TauAnalysisAlgorithms.TauAnalysisAlgorithmsTest import makeSequence
+algSeq = makeSequence (dataType)
+print algSeq # For debugging
+
+# Add all algorithms from the sequence to the job.
+athAlgSeq += algSeq
+
+# Set up a histogram output file for the job:
+ServiceMgr += CfgMgr.THistSvc()
+ServiceMgr.THistSvc.Output += [
+    "ANALYSIS DATAFILE='TauAnalysisAlgorithmsTest." + dataType + ".hist.root' OPT='RECREATE'"
+    ]
+
+# Reduce the printout from Athena:
+include( "AthAnalysisBaseComps/SuppressLogging.py" )
diff --git a/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/src/components/TauAnalysisAlgorithms_entries.cxx b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/src/components/TauAnalysisAlgorithms_entries.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..2100a92e42f2491eee51b2041fb5786d81a11248
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/src/components/TauAnalysisAlgorithms_entries.cxx
@@ -0,0 +1,26 @@
+// AsgExampleTools_entries.cxx
+
+#include <GaudiKernel/DeclareFactoryEntries.h>
+
+#include <TauAnalysisAlgorithms/DiTauEfficiencyCorrectionsAlg.h>
+#include <TauAnalysisAlgorithms/DiTauSmearingAlg.h>
+#include <TauAnalysisAlgorithms/DiTauTruthMatchingAlg.h>
+#include <TauAnalysisAlgorithms/TauEfficiencyCorrectionsAlg.h>
+#include <TauAnalysisAlgorithms/TauSmearingAlg.h>
+#include <TauAnalysisAlgorithms/TauTruthMatchingAlg.h>
+
+DECLARE_ALGORITHM_FACTORY (CP::DiTauEfficiencyCorrectionsAlg)
+DECLARE_ALGORITHM_FACTORY (CP::DiTauSmearingAlg)
+DECLARE_ALGORITHM_FACTORY (CP::DiTauTruthMatchingAlg)
+DECLARE_ALGORITHM_FACTORY (CP::TauEfficiencyCorrectionsAlg)
+DECLARE_ALGORITHM_FACTORY (CP::TauSmearingAlg)
+DECLARE_ALGORITHM_FACTORY (CP::TauTruthMatchingAlg)
+
+DECLARE_FACTORY_ENTRIES(TauAnalysisAlgorithms) {
+  DECLARE_ALGORITHM (CP::DiTauEfficiencyCorrectionsAlg)
+  DECLARE_ALGORITHM (CP::DiTauSmearingAlg)
+  DECLARE_ALGORITHM (CP::DiTauTruthMatchingAlg)
+  DECLARE_ALGORITHM (CP::TauEfficiencyCorrectionsAlg)
+  DECLARE_ALGORITHM (CP::TauSmearingAlg)
+  DECLARE_ALGORITHM (CP::TauTruthMatchingAlg)
+}
diff --git a/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/src/components/TauAnalysisAlgorithms_load.cxx b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/src/components/TauAnalysisAlgorithms_load.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..30ea7bbd909d6498c1dec164c40961ed0b717acb
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TauAnalysisAlgorithms/src/components/TauAnalysisAlgorithms_load.cxx
@@ -0,0 +1,5 @@
+// AsgExampleTools_load.cxx
+
+#include "GaudiKernel/LoadFactoryEntries.h"
+
+LOAD_FACTORY_ENTRIES(TauAnalysisAlgorithms)
diff --git a/PhysicsAnalysis/Algorithms/TrackingAnalysisAlgorithms/CMakeLists.txt b/PhysicsAnalysis/Algorithms/TrackingAnalysisAlgorithms/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..70e73676e4d5941da8e471cf143a53ef17d5b471
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TrackingAnalysisAlgorithms/CMakeLists.txt
@@ -0,0 +1,29 @@
+# Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+
+# The name of the package:
+atlas_subdir( TrackingAnalysisAlgorithms )
+
+# The package's dependencies:
+atlas_depends_on_subdirs(
+   PUBLIC
+   PhysicsAnalysis/D3PDTools/AnaAlgorithm
+   PRIVATE
+   Event/xAOD/xAODTracking )
+
+# Components in the package:
+atlas_add_library( TrackingAnalysisAlgorithmsLib
+   TrackingAnalysisAlgorithms/*.h Root/*.cxx
+   PUBLIC_HEADERS TrackingAnalysisAlgorithms
+   LINK_LIBRARIES AnaAlgorithmLib
+   PRIVATE_LINK_LIBRARIES xAODTracking )
+
+atlas_add_dictionary( TrackingAnalysisAlgorithmsDict
+   TrackingAnalysisAlgorithms/TrackingAnalysisAlgorithmsDict.h
+   TrackingAnalysisAlgorithms/selection.xml
+   LINK_LIBRARIES TrackingAnalysisAlgorithmsLib )
+
+if( NOT XAOD_STANDALONE )
+   atlas_add_component( TrackingAnalysisAlgorithms
+      src/*.h src/*.cxx src/components/*.cxx
+      LINK_LIBRARIES GaudiKernel TrackingAnalysisAlgorithmsLib )
+endif()
diff --git a/PhysicsAnalysis/Algorithms/TrackingAnalysisAlgorithms/README.md b/PhysicsAnalysis/Algorithms/TrackingAnalysisAlgorithms/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..eb7a319d1dab7dc54009b3806322bfa513c5f34b
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TrackingAnalysisAlgorithms/README.md
@@ -0,0 +1,10 @@
+Tracking Analysis Algorithms
+============================
+
+This package is meant to collect dual-use/analysis algorithms that are
+related to tracking.
+
+Current algorithm(s) in the package:
+  - [CP::VertexSelectionAlg](TrackingAlgorithms/VertexSelectionAlg.h):
+    Filter algorithm requiring a certain number of primary vertices in
+    the event.
diff --git a/PhysicsAnalysis/Algorithms/TrackingAnalysisAlgorithms/Root/VertexSelectionAlg.cxx b/PhysicsAnalysis/Algorithms/TrackingAnalysisAlgorithms/Root/VertexSelectionAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..33c6098aa6b3851724e530f361c97a661fcac9b2
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TrackingAnalysisAlgorithms/Root/VertexSelectionAlg.cxx
@@ -0,0 +1,82 @@
+//
+// Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+//
+
+// Local include(s):
+#include "TrackingAnalysisAlgorithms/VertexSelectionAlg.h"
+
+// EDM include(s):
+#include "xAODTracking/VertexContainer.h"
+
+#include "AnaAlgorithm/FilterReporter.h"
+
+namespace CP {
+
+   VertexSelectionAlg::VertexSelectionAlg( const std::string& name,
+                                           ISvcLocator* svcLoc )
+   : EL::AnaAlgorithm( name, svcLoc ) {
+
+      // Declare the algorithm's properties:
+      declareProperty( "VertexContainer", m_vertexKey = "PrimaryVertices",
+                       "Vertex container to check" );
+      declareProperty( "MinVertices", m_minVertices = 1,
+                       "Minimum number of vertices required" );
+      declareProperty( "MinTracks", m_minTracks = 0,
+                       "Minimum number of track particles required per "
+                       "vertex" );
+   }
+
+   StatusCode VertexSelectionAlg::initialize() {
+
+      // Greet the user:
+      ATH_MSG_INFO( "Requiring " << m_minVertices << " vertex/vertices from \""
+                    << m_vertexKey << "\" with " << m_minTracks
+                    << " track(s) each" );
+
+      ANA_CHECK (m_filterParams.initialize());
+
+      // Return gracefully:
+      return StatusCode::SUCCESS;
+   }
+
+   StatusCode VertexSelectionAlg::finalize() {
+      ANA_CHECK (m_filterParams.finalize());
+
+      // Return gracefully:
+      return StatusCode::SUCCESS;
+   }
+
+   StatusCode VertexSelectionAlg::execute() {
+
+      EL::FilterReporter filter (m_filterParams, false);
+
+      // Retrieve the vertex container:
+      const xAOD::VertexContainer* vertices = nullptr;
+      ATH_CHECK( evtStore()->retrieve( vertices, m_vertexKey ) );
+
+      // The number of "good" vertices found:
+      unsigned goodVertices = 0;
+
+      // Check how many "good" vertices we have in the current event:
+      for( const xAOD::Vertex* vx : *vertices ) {
+         // It has to be either a primary or a pileup vertex:
+         if( ( vx->vertexType() != xAOD::VxType::PriVtx ) &&
+             ( vx->vertexType() != xAOD::VxType::PileUp ) ) {
+            continue;
+         }
+         // With the minimum number of tracks specified:
+         if( vx->nTrackParticles() < m_minTracks ) {
+            continue;
+         }
+         // Apparently this is a "good" vertex:
+         ++goodVertices;
+      }
+
+      // Decide about the event:
+      filter.setPassed( goodVertices >= m_minVertices );
+
+      // Return gracefully:
+      return StatusCode::SUCCESS;
+   }
+
+} // namespace CP
diff --git a/PhysicsAnalysis/Algorithms/TrackingAnalysisAlgorithms/TrackingAnalysisAlgorithms/TrackingAnalysisAlgorithmsDict.h b/PhysicsAnalysis/Algorithms/TrackingAnalysisAlgorithms/TrackingAnalysisAlgorithms/TrackingAnalysisAlgorithmsDict.h
new file mode 100644
index 0000000000000000000000000000000000000000..05600c0795bde4f634529dbcc0668c5f1ba78ba2
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TrackingAnalysisAlgorithms/TrackingAnalysisAlgorithms/TrackingAnalysisAlgorithmsDict.h
@@ -0,0 +1,11 @@
+// Dear emacs, this is -*- c++ -*-
+//
+// Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+//
+#ifndef TRACKINGANALYSISALGORITHMS_TRACKINGANALYSISALGORITHMSDICT_H
+#define TRACKINGANALYSISALGORITHMS_TRACKINGANALYSISALGORITHMSDICT_H
+
+// Local include(s):
+#include "TrackingAnalysisAlgorithms/VertexSelectionAlg.h"
+
+#endif // TRACKINGANALYSISALGORITHMS_TRACKINGANALYSISALGORITHMSDICT_H
diff --git a/PhysicsAnalysis/Algorithms/TrackingAnalysisAlgorithms/TrackingAnalysisAlgorithms/VertexSelectionAlg.h b/PhysicsAnalysis/Algorithms/TrackingAnalysisAlgorithms/TrackingAnalysisAlgorithms/VertexSelectionAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..669118234ec529640477c52ed3fc97a8ebc5e471
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TrackingAnalysisAlgorithms/TrackingAnalysisAlgorithms/VertexSelectionAlg.h
@@ -0,0 +1,64 @@
+// Dear emacs, this is -*- c++ -*-
+//
+// Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+//
+#ifndef TRACKINGANALYSISALGORITHMS_VERTEXSELECTIONALG_H
+#define TRACKINGANALYSISALGORITHMS_VERTEXSELECTIONALG_H
+
+// Framework include(s):
+#include "AnaAlgorithm/AnaAlgorithm.h"
+#include "AnaAlgorithm/FilterReporterParams.h"
+
+// System include(s):
+#include <string>
+
+namespace CP {
+
+   /// Algorithm meant to select events with "some" vertex requirements
+   ///
+   /// This algorithm is meant to be used to pose vertex requirements for the
+   /// analysed event. Most commonly requiring at least one primary vertex with
+   /// some number of track particles associated to it.
+   ///
+   /// @author Attila Krasznahorkay <Attila.Krasznahorkay@cern.ch>
+   ///
+   class VertexSelectionAlg final : public EL::AnaAlgorithm {
+
+   public:
+      /// Algorithm constructor
+      VertexSelectionAlg( const std::string& name, ISvcLocator* svcLoc );
+
+      /// @name Function(s) inherited from @c EL::AnaAlgorithm
+      /// @{
+
+      /// Function initialising the algorithm
+      virtual StatusCode initialize() override;
+
+      /// Function executing the algorithm
+      virtual StatusCode execute() override;
+
+      /// Function finalize the algorithm
+      virtual StatusCode finalize() override;
+
+      /// @}
+
+   private:
+      /// @name Algorithm properties
+      /// @{
+
+      /// Event store key of the vertex container
+      std::string m_vertexKey;
+      /// Number of vertices required in the event
+      unsigned m_minVertices;
+      /// Number of track particles required per vertex
+      unsigned m_minTracks;
+
+     EL::FilterReporterParams m_filterParams {this, "vertex selection"};
+
+      /// @}
+
+   }; // class VertexSelectionAlg
+
+} // namespace CP
+
+#endif // TRACKINGANALYSISALGORITHMS_VERTEXSELECTIONALG_H
diff --git a/PhysicsAnalysis/Algorithms/TrackingAnalysisAlgorithms/TrackingAnalysisAlgorithms/selection.xml b/PhysicsAnalysis/Algorithms/TrackingAnalysisAlgorithms/TrackingAnalysisAlgorithms/selection.xml
new file mode 100644
index 0000000000000000000000000000000000000000..1b18e4184646d3966d9a309e6dd99504ab7b99e4
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TrackingAnalysisAlgorithms/TrackingAnalysisAlgorithms/selection.xml
@@ -0,0 +1,6 @@
+<!-- Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration -->
+<lcgdict>
+
+  <class name="CP::VertexSelectionAlg" />
+
+</lcgdict>
diff --git a/PhysicsAnalysis/Algorithms/TrackingAnalysisAlgorithms/src/components/TrackingAnalysisAlgorithms_entries.cxx b/PhysicsAnalysis/Algorithms/TrackingAnalysisAlgorithms/src/components/TrackingAnalysisAlgorithms_entries.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..e536726f483a2a9851883e1864af62787e181cc8
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TrackingAnalysisAlgorithms/src/components/TrackingAnalysisAlgorithms_entries.cxx
@@ -0,0 +1,18 @@
+//
+// Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+//
+
+// Local include(s):
+#include "TrackingAnalysisAlgorithms/VertexSelectionAlg.h"
+
+// Framework include(s):
+#include "GaudiKernel/DeclareFactoryEntries.h"
+
+// Declare the component(s) of the package:
+DECLARE_NAMESPACE_ALGORITHM_FACTORY( CP, VertexSelectionAlg )
+
+DECLARE_FACTORY_ENTRIES( TrackingAnalysisAlgorithms ) {
+
+   DECLARE_NAMESPACE_ALGORITHM( CP, VertexSelectionAlg )
+
+}
diff --git a/PhysicsAnalysis/Algorithms/TrackingAnalysisAlgorithms/src/components/TrackingAnalysisAlgorithms_load.cxx b/PhysicsAnalysis/Algorithms/TrackingAnalysisAlgorithms/src/components/TrackingAnalysisAlgorithms_load.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..241f0be8c02ed62c86ae19560cbf89880bbcad14
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TrackingAnalysisAlgorithms/src/components/TrackingAnalysisAlgorithms_load.cxx
@@ -0,0 +1,9 @@
+//
+// Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+//
+
+// Framework include(s):
+#include "GaudiKernel/LoadFactoryEntries.h"
+
+// Declare the library to Gaudi:
+LOAD_FACTORY_ENTRIES( TrackingAnalysisAlgorithms )
diff --git a/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/CMakeLists.txt b/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..3a32fb71c0055535e837f92e0ed95fbc66e96d8a
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/CMakeLists.txt
@@ -0,0 +1,63 @@
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+#
+# @author Tadej Novak
+
+atlas_subdir( TriggerAnalysisAlgorithms )
+
+atlas_depends_on_subdirs(
+   PUBLIC
+   Event/xAOD/xAODEventInfo
+   PhysicsAnalysis/Algorithms/SelectionHelpers
+   PhysicsAnalysis/Algorithms/SystematicsHandles
+   PhysicsAnalysis/D3PDTools/AnaAlgorithm
+   PhysicsAnalysis/Interfaces/AsgAnalysisInterfaces
+   Trigger/TrigEvent/TrigDecisionInterface
+   PRIVATE
+   PhysicsAnalysis/D3PDTools/RootCoreUtils )
+
+atlas_add_library( TriggerAnalysisAlgorithmsLib
+   TriggerAnalysisAlgorithms/*.h TriggerAnalysisAlgorithms/*.icc Root/*.cxx
+   PUBLIC_HEADERS TriggerAnalysisAlgorithms
+   LINK_LIBRARIES xAODEventInfo SelectionHelpersLib SystematicsHandlesLib
+      AnaAlgorithmLib AsgAnalysisInterfaces
+   PRIVATE_LINK_LIBRARIES RootCoreUtils )
+
+atlas_add_dictionary( TriggerAnalysisAlgorithmsDict
+   TriggerAnalysisAlgorithms/TriggerAnalysisAlgorithmsDict.h
+   TriggerAnalysisAlgorithms/selection.xml
+   LINK_LIBRARIES TriggerAnalysisAlgorithmsLib )
+
+if( NOT XAOD_STANDALONE )
+   atlas_add_component( TriggerAnalysisAlgorithms
+      src/*.h src/*.cxx src/components/*.cxx
+      LINK_LIBRARIES GaudiKernel TriggerAnalysisAlgorithmsLib )
+endif()
+
+atlas_install_python_modules( python/*.py )
+atlas_install_joboptions( share/*_jobOptions.py )
+atlas_install_scripts( share/*_eljob.py )
+
+if( XAOD_STANDALONE )
+  atlas_add_test( TriggerAlgorithmsTestJobData
+     SCRIPT TriggerAlgorithmsTest_eljob.py --data-type data --unit-test
+     PROPERTIES TIMEOUT 600 )
+  atlas_add_test( TriggerAlgorithmsTestJobFullSim
+     SCRIPT TriggerAlgorithmsTest_eljob.py --data-type mc --unit-test
+     PROPERTIES TIMEOUT 600 )
+  atlas_add_test( TriggerAlgorithmsTestJobFastSim
+     SCRIPT TriggerAlgorithmsTest_eljob.py --data-type afii --unit-test
+     PROPERTIES TIMEOUT 600 )
+else()
+   atlas_add_test( TriggerAlgorithmsTestJobData
+      SCRIPT athena.py
+             TriggerAnalysisAlgorithms/TriggerAlgorithmsTest_jobOptions.py - --data-type data
+      PROPERTIES TIMEOUT 600 )
+   atlas_add_test( TriggerAlgorithmsTestJobFullSim
+      SCRIPT athena.py
+             TriggerAnalysisAlgorithms/TriggerAlgorithmsTest_jobOptions.py - --data-type mc
+      PROPERTIES TIMEOUT 600 )
+   atlas_add_test( TriggerAlgorithmsTestJobFastSim
+      SCRIPT athena.py
+             TriggerAnalysisAlgorithms/TriggerAlgorithmsTest_jobOptions.py - --data-type afii
+      PROPERTIES TIMEOUT 600 )
+endif()
diff --git a/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/Root/TrigEventSelectorAlg.cxx b/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/Root/TrigEventSelectorAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..a66ce3f0369b823e11ba83781d1958bceb1927b3
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/Root/TrigEventSelectorAlg.cxx
@@ -0,0 +1,71 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Tadej Novak
+
+#include <AnaAlgorithm/FilterReporter.h>
+#include <RootCoreUtils/StringUtil.h>
+#include <TriggerAnalysisAlgorithms/TrigEventSelectionAlg.h>
+#include <xAODEventInfo/EventInfo.h>
+
+CP::TrigEventSelectionAlg::TrigEventSelectionAlg(const std::string &name,
+                                             ISvcLocator *svcLoc)
+  : EL::AnaAlgorithm(name, svcLoc),
+    m_trigDecisionTool("Trig::TrigDecisionTool/TrigDecisionTool")
+{
+  declareProperty("tool", m_trigDecisionTool, "trigger decision tool");
+  declareProperty("triggers", m_trigList, "trigger selection list");
+  declareProperty("selectionDecoration", m_selectionDecoration, "the decoration the trigger pass status");
+}
+
+StatusCode CP::TrigEventSelectionAlg::initialize()
+{
+  if (m_trigList.empty()) {
+    ATH_MSG_ERROR("A list of triggers needs to be provided");
+    return StatusCode::FAILURE;
+  }
+
+  ANA_CHECK(m_trigDecisionTool.retrieve());
+
+  if (!m_selectionDecoration.empty()) {
+    for (const std::string &chain : m_trigList) {
+      m_selectionAccessors.emplace_back(m_selectionDecoration + "_" + RCU::substitute (chain, "-", "_"));
+    }
+  }
+
+  ANA_CHECK (m_filterParams.initialize());
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode CP::TrigEventSelectionAlg::execute()
+{
+  EL::FilterReporter filter (m_filterParams, false);
+
+  if (m_trigList.empty()) {
+    filter.setPassed(true);
+    return StatusCode::SUCCESS;
+  }
+
+  const xAOD::EventInfo *evtInfo = 0;
+  ANA_CHECK(evtStore()->retrieve(evtInfo, "EventInfo"));
+
+  for (size_t i = 0; i < m_trigList.size(); i++) {
+    bool trigPassed = m_trigDecisionTool->isPassed(m_trigList[i]);
+    if (!m_selectionDecoration.empty()) {
+      m_selectionAccessors[i](*evtInfo) = trigPassed;
+    }
+    if (trigPassed)
+      filter.setPassed (true);
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode CP::TrigEventSelectionAlg::finalize()
+{
+  ANA_CHECK (m_filterParams.finalize());
+
+  return StatusCode::SUCCESS;
+}
diff --git a/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/Root/TrigPrescalesAlg.cxx b/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/Root/TrigPrescalesAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..db63776700c525fd4cc495bff87658a529f245b9
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/Root/TrigPrescalesAlg.cxx
@@ -0,0 +1,98 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Tadej Novak
+
+
+//
+// includes
+//
+
+#include <TriggerAnalysisAlgorithms/TrigPrescalesAlg.h>
+
+#include <RootCoreUtils/StringUtil.h>
+#include <xAODEventInfo/EventInfo.h>
+
+//
+// method implementations
+//
+
+namespace CP
+{
+  TrigPrescalesAlg ::
+  TrigPrescalesAlg (const std::string& name, 
+                    ISvcLocator* pSvcLocator)
+    : AnaAlgorithm (name, pSvcLocator)
+    , m_pileupReweightingTool ("CP::PileupReweightingTool", this)
+  {
+    declareProperty ("pileupReweightingTool", m_pileupReweightingTool, "the pileup reweighting tool to be used");
+    declareProperty ("triggers", m_trigList, "trigger list");
+    declareProperty ("triggersAll", m_trigListAll, "all trigger list");
+    declareProperty ("prescaleDecoration", m_prescaleDecoration, "decoration to store prescales");
+  }
+
+
+
+  StatusCode TrigPrescalesAlg ::
+  initialize ()
+  {
+    if (m_prescaleDecoration.empty())
+    {
+      ANA_MSG_ERROR ("The decoration should not be empty");
+      return StatusCode::FAILURE;
+    }
+
+    if (m_trigList.empty())
+    {
+      ANA_MSG_ERROR ("A list of triggers needs to be provided");
+      return StatusCode::FAILURE;
+    }
+
+    if (m_trigListAll.empty())
+    {
+      m_trigListAll = m_trigList;
+    }
+
+    for (const std::string &chain : m_trigListAll)
+    {
+      m_prescaleAccessors.emplace_back(m_prescaleDecoration + "_" + RCU::substitute (chain, "-", "_"));
+
+      // Generate helper functions
+      if (std::find(m_trigList.begin(), m_trigList.end(), chain) != m_trigList.end())
+      {
+        m_prescaleFunctions.emplace_back([this](const xAOD::EventInfo *evtInfo, const std::string &trigger)
+        {
+          return m_pileupReweightingTool->getDataWeight (*evtInfo, trigger, true);
+        });
+      }
+      else
+      {
+        m_prescaleFunctions.emplace_back([](const xAOD::EventInfo *, const std::string &)
+        {
+          return invalidTriggerPrescale();
+        });
+      }
+    }
+
+    ANA_CHECK (m_pileupReweightingTool.retrieve());
+
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode TrigPrescalesAlg ::
+  execute ()
+  {
+    const xAOD::EventInfo *evtInfo{};
+    ANA_CHECK (evtStore()->retrieve(evtInfo, "EventInfo"));
+
+    for (size_t i = 0; i < m_trigListAll.size(); i++)
+    {
+      (m_prescaleAccessors[i]) (*evtInfo) = (m_prescaleFunctions[i]) (evtInfo, m_trigListAll[i]);
+    }
+
+    return StatusCode::SUCCESS;
+  }
+}
diff --git a/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/TriggerAnalysisAlgorithms/TrigEventSelectionAlg.h b/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/TriggerAnalysisAlgorithms/TrigEventSelectionAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..8ed2a020d793a81fce07e2f330968b8c9784dc1a
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/TriggerAnalysisAlgorithms/TrigEventSelectionAlg.h
@@ -0,0 +1,46 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Tadej Novak
+
+#ifndef TRIGGER_ANALYSIS_ALGORITHMS__TRIG_EVENT_SELECTION_ALG_H
+#define TRIGGER_ANALYSIS_ALGORITHMS__TRIG_EVENT_SELECTION_ALG_H
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <AnaAlgorithm/FilterReporterParams.h>
+#include <AsgTools/ToolHandle.h> 
+#include <AthContainers/AuxElement.h>
+#include <TrigDecisionInterface/ITrigDecisionTool.h>
+
+namespace CP
+{
+  class TrigEventSelectionAlg : public EL::AnaAlgorithm
+  {
+  public:
+    TrigEventSelectionAlg(const std::string &name,
+                          ISvcLocator *svcLoc = nullptr);
+
+    virtual StatusCode initialize() final;
+    virtual StatusCode execute() final;
+    virtual StatusCode finalize() final;
+
+  private:
+    /// \brief trigger decision tool handle
+    ToolHandle<Trig::ITrigDecisionTool> m_trigDecisionTool;
+
+    /// \brief list of triggers or trigger chains
+    std::vector<std::string> m_trigList;
+    
+    /// \brief the decoration for trigger selection
+    std::string m_selectionDecoration;
+
+    /// \brief the accessors for \ref m_selectionDecoration and \ref m_trigList combination
+    std::vector<SG::AuxElement::Decorator<bool>> m_selectionAccessors;
+
+    /// \brief the filter reporter parameters
+    EL::FilterReporterParams m_filterParams {this, "trigger event selection"};
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/TriggerAnalysisAlgorithms/TrigPrescalesAlg.h b/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/TriggerAnalysisAlgorithms/TrigPrescalesAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..a6148e03355c72ff8248badd1846dc279baa05cb
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/TriggerAnalysisAlgorithms/TrigPrescalesAlg.h
@@ -0,0 +1,67 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Tadej Novak
+
+
+#ifndef TRIGGER_ANALYSIS_ALGORITHMS__TRIG_PRESCALES_ALG_H
+#define TRIGGER_ANALYSIS_ALGORITHMS__TRIG_PRESCALES_ALG_H
+
+#include <functional>
+
+#include <AnaAlgorithm/AnaAlgorithm.h>
+#include <AsgAnalysisInterfaces/IPileupReweightingTool.h>
+
+namespace CP
+{
+  /// \brief the decoration value to use if there is no valid
+  /// trigger prescale information
+  constexpr float invalidTriggerPrescale () {return -1;}
+
+
+
+  /// \brief an algorithm for retrieving trigger prescales
+
+  class TrigPrescalesAlg final : public EL::AnaAlgorithm
+  {
+    /// \brief the standard constructor
+  public:
+    TrigPrescalesAlg (const std::string& name, 
+                      ISvcLocator* pSvcLocator);
+
+
+  public:
+    StatusCode initialize () override;
+
+  public:
+    StatusCode execute () override;
+
+
+    /// \brief the pile-up reweighting tool
+  private:
+    ToolHandle<IPileupReweightingTool> m_pileupReweightingTool;
+
+    /// \brief list of prescaled triggers or trigger chains
+  private:
+    std::vector<std::string> m_trigList;
+  
+    /// \brief list of all triggers or trigger chains
+  private:
+    std::vector<std::string> m_trigListAll;
+
+    /// \brief list of helper functions to compute the prescales
+  private:
+    std::vector<std::function<float(const xAOD::EventInfo *, const std::string &)>> m_prescaleFunctions;
+
+    /// \brief the decoration for trigger prescales
+  private:
+    std::string m_prescaleDecoration;
+
+    /// \brief the accessors for \ref m_prescaleDecoration and \ref m_trigList combination
+  private:
+    std::vector<SG::AuxElement::Decorator<float>> m_prescaleAccessors;
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/TriggerAnalysisAlgorithms/TriggerAnalysisAlgorithmsDict.h b/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/TriggerAnalysisAlgorithms/TriggerAnalysisAlgorithmsDict.h
new file mode 100644
index 0000000000000000000000000000000000000000..e86f6bb0300e7f053194ccaff336275923bbad3e
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/TriggerAnalysisAlgorithms/TriggerAnalysisAlgorithmsDict.h
@@ -0,0 +1,14 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Tadej Novak
+
+
+#ifndef TRIGGER_ANALYSIS_ALGORITHMS__TRIGGER_ANALYSIS_ALGORITHMS_DICT_H
+#define TRIGGER_ANALYSIS_ALGORITHMS__TRIGGER_ANALYSIS_ALGORITHMS_DICT_H
+
+#include <TriggerAnalysisAlgorithms/TrigEventSelectionAlg.h>
+#include <TriggerAnalysisAlgorithms/TrigPrescalesAlg.h>
+
+#endif
diff --git a/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/TriggerAnalysisAlgorithms/selection.xml b/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/TriggerAnalysisAlgorithms/selection.xml
new file mode 100644
index 0000000000000000000000000000000000000000..677efcf89427a25b3066bad75f590d0da329561a
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/TriggerAnalysisAlgorithms/selection.xml
@@ -0,0 +1,6 @@
+<lcgdict>
+
+   <class name="CP::TrigEventSelectionAlg" />
+   <class name="CP::TrigPrescalesAlg" />
+   
+</lcgdict>
diff --git a/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/python/TriggerAnalysisAlgorithmsTest.py b/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/python/TriggerAnalysisAlgorithmsTest.py
new file mode 100644
index 0000000000000000000000000000000000000000..c0d82335ddd0c6c11bafdfa3e860b04a253974fb
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/python/TriggerAnalysisAlgorithmsTest.py
@@ -0,0 +1,49 @@
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+#
+# @author Tadej Novak
+# @author Nils Krumnack
+
+from AnaAlgorithm.AlgSequence import AlgSequence
+from AnaAlgorithm.DualUseConfig import createAlgorithm
+
+def makeSequence (dataType) :
+
+    # Config:
+    triggerChains = [
+        'HLT_2mu14',
+        'HLT_mu20_mu8noL1',
+        'HLT_2e17_lhvloose_nod0'
+        ]
+
+
+    algSeq = AlgSequence()
+
+    # Set up the systematics loader/handler algorithm:
+    alg = createAlgorithm( 'CP::SysListLoaderAlg', 'SysLoaderAlg' )
+    alg.sigmaRecommended = 1
+    algSeq += alg
+
+    # Include, and then set up the pileup analysis sequence:
+    from TriggerAnalysisAlgorithms.TriggerAnalysisSequence import \
+        makeTriggerAnalysisSequence
+    triggerSequence = makeTriggerAnalysisSequence( dataType, triggerChains=triggerChains )
+    algSeq += triggerSequence
+
+    # Set up an ntuple to check the job with:
+    treeMaker = createAlgorithm( 'CP::TreeMakerAlg', 'TreeMaker' )
+    treeMaker.TreeName = 'events'
+    algSeq += treeMaker
+    ntupleMaker = createAlgorithm( 'CP::AsgxAODNTupleMakerAlg', 'NTupleMaker' )
+    ntupleMaker.TreeName = 'events'
+    ntupleMaker.Branches = [
+        'EventInfo.runNumber   -> runNumber',
+        'EventInfo.eventNumber -> eventNumber',
+        ]
+    ntupleMaker.Branches += ['EventInfo.trigPassed_' + t + ' -> trigPassed_' + t for t in triggerChains]
+    ntupleMaker.systematicsRegex = '.*'
+    algSeq += ntupleMaker
+    treeFiller = createAlgorithm( 'CP::TreeFillerAlg', 'TreeFiller' )
+    treeFiller.TreeName = 'events'
+    algSeq += treeFiller
+
+    return algSeq
diff --git a/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/python/TriggerAnalysisSequence.py b/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/python/TriggerAnalysisSequence.py
new file mode 100644
index 0000000000000000000000000000000000000000..68e6dcfbc0bcacdcb3e35ebe39813ba29e8853be
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/python/TriggerAnalysisSequence.py
@@ -0,0 +1,58 @@
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+
+# AnaAlgorithm import(s):
+from AnaAlgorithm.AnaAlgSequence import AnaAlgSequence
+from AnaAlgorithm.DualUseConfig import addPrivateTool, createAlgorithm, createPublicTool
+
+
+def makeTriggerAnalysisSequence( dataType,
+                                 triggerChains = [],
+                                 prescaleLumiCalcFiles = []):
+    """Create a basic trigger analysis algorithm sequence
+
+    Keyword arguments:
+      dataType -- The data type to run on ("data", "mc" or "afii")
+      triggerChains -- a list of trigger chains
+      prescaleLumiCalcFiles -- a list of lumicalc files to calculate trigger prescales
+    """
+
+    if dataType not in ["data", "mc", "afii"] :
+        raise ValueError ("invalid data type: " + dataType)
+
+    # Create the analysis algorithm sequence object:
+    seq = AnaAlgSequence( "TriggerAnalysisSequence" )
+
+    # Create public trigger tools
+    xAODConfTool = createPublicTool( 'TrigConf::xAODConfigTool', 'xAODConfigTool' )
+    decisionTool = createPublicTool( 'Trig::TrigDecisionTool', 'TrigDecisionTool')
+    decisionTool.ConfigTool = '%s/%s' % \
+        ( xAODConfTool.getType(), xAODConfTool.getName() )
+
+    seq.addPublicTool( xAODConfTool )
+    seq.addPublicTool( decisionTool )
+
+    if triggerChains:
+        # Set up the trigger selection:
+        alg = createAlgorithm( 'CP::TrigEventSelectionAlg', 'TrigEventSelectorAlg' )
+        alg.tool = '%s/%s' % \
+            ( decisionTool.getType(), decisionTool.getName() )
+        alg.triggers = list(triggerChains)
+        alg.selectionDecoration = 'trigPassed'
+
+        seq.append( alg, inputPropName = None )
+
+        # Calculate trigger prescales
+        if dataType == 'data' and prescaleLumiCalcFiles:
+            alg = createAlgorithm( 'CP::TrigPrescalesAlg', 'TrigPrescalesAlg' )
+            addPrivateTool( alg, 'pileupReweightingTool', 'CP::PileupReweightingTool' )
+            alg.pileupReweightingTool.LumiCalcFiles = prescaleLumiCalcFiles
+            alg.pileupReweightingTool.TrigDecisionTool = '%s/%s' % \
+                ( decisionTool.getType(), decisionTool.getName() )
+            alg.triggers = [lumicalc.split(':')[-1] for lumicalc in prescaleLumiCalcFiles if ':' in lumicalc]
+            alg.triggersAll = list(triggerChains)
+            alg.prescaleDecoration = 'prescale'
+
+            seq.append( alg, inputPropName = None )
+
+    # Return the sequence:
+    return seq
diff --git a/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/python/__init__.py b/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/python/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..a37d36f3512db5b18b69e65df7aff2568fa8d2aa
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/python/__init__.py
@@ -0,0 +1,3 @@
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+
+__version__ = '1.0.0'
diff --git a/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/share/TriggerAlgorithmsTest_eljob.py b/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/share/TriggerAlgorithmsTest_eljob.py
new file mode 100755
index 0000000000000000000000000000000000000000..ffcf2f3e48d094cfd01f7a222259706ee2cfa53c
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/share/TriggerAlgorithmsTest_eljob.py
@@ -0,0 +1,76 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+#
+# @author Tadej Novak
+
+
+# Read the submission directory as a command line argument. You can
+# extend the list of arguments with your private ones later on.
+import optparse
+parser = optparse.OptionParser()
+parser.add_option( '-d', '--data-type', dest = 'data_type',
+                   action = 'store', type = 'string', default = 'data',
+                   help = 'Type of data to run over. Valid options are data, mc, afii' )
+parser.add_option( '-s', '--submission-dir', dest = 'submission_dir',
+                   action = 'store', type = 'string', default = 'submitDir',
+                   help = 'Submission directory for EventLoop' )
+parser.add_option( '-u', '--unit-test', dest='unit_test',
+                   action = 'store_true', default = False,
+                   help = 'Run the job in "unit test mode"' )
+( options, args ) = parser.parse_args()
+
+# Set up (Py)ROOT.
+import ROOT
+ROOT.xAOD.Init().ignore()
+
+# ideally we'd run over all of them, but we don't have a mechanism to
+# configure per-sample right now
+
+dataType = options.data_type
+
+inputfile = {"data": 'ASG_TEST_FILE_DATA',
+             "mc":   'ASG_TEST_FILE_MC',
+             "afii": 'ASG_TEST_FILE_MC_AFII'}
+
+if dataType not in ["data", "mc", "afii"] :
+    raise ValueError ("invalid data type: " + dataType)
+
+# Set up the sample handler object. See comments from the C++ macro
+# for the details about these lines.
+import os
+sh = ROOT.SH.SampleHandler()
+sh.setMetaString( 'nc_tree', 'CollectionTree' )
+sample = ROOT.SH.SampleLocal (dataType)
+sample.add (os.getenv (inputfile[dataType]))
+sh.add (sample)
+sh.printContent()
+
+# Create an EventLoop job.
+job = ROOT.EL.Job()
+job.sampleHandler( sh )
+job.options().setDouble( ROOT.EL.Job.optMaxEvents, 500 )
+job.options().setString( ROOT.EL.Job.optSubmitDirMode, "unique" )
+
+from TriggerAnalysisAlgorithms.TriggerAnalysisAlgorithmsTest import makeSequence
+algSeq = makeSequence (dataType)
+print algSeq # For debugging
+for alg in algSeq:
+    job.algsAdd( alg )
+    pass
+
+# Set up an output file for the job:
+job.outputAdd( ROOT.EL.OutputStream( 'ANALYSIS' ) )
+
+# Find the right output directory:
+submitDir = options.submission_dir
+if options.unit_test:
+    import os
+    import tempfile
+    submitDir = tempfile.mkdtemp( prefix = 'triggerTest_', dir = os.getcwd() )
+    os.rmdir( submitDir )
+    pass
+
+# Run the job using the direct driver.
+driver = ROOT.EL.DirectDriver()
+driver.submit( job, submitDir )
diff --git a/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/share/TriggerAlgorithmsTest_jobOptions.py b/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/share/TriggerAlgorithmsTest_jobOptions.py
new file mode 100644
index 0000000000000000000000000000000000000000..a2244c9537c4367bf0920072a0f7b72391ca0c6f
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/share/TriggerAlgorithmsTest_jobOptions.py
@@ -0,0 +1,44 @@
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+#
+# @author Tadej Novak
+
+# User options, which can be set from command line after a "-" character
+# athena EgammaAlgorithmsTest_jobOptions.py - --myOption ...
+from AthenaCommon.AthArgumentParser import AthArgumentParser
+athArgsParser = AthArgumentParser()
+athArgsParser.add_argument("--data-type", action = "store", dest = "data_type",
+                           default = "data",
+                           help = "Type of input to run over. Valid options are 'data', 'mc', 'afii'")
+athArgs = athArgsParser.parse_args()
+
+dataType = athArgs.data_type
+if not dataType in ["data", "mc", "afii"] :
+    raise Exception ("invalid data type: " + dataType)
+
+print("Running on data type: " + dataType)
+
+inputfile = {"data": 'ASG_TEST_FILE_DATA',
+             "mc":   'ASG_TEST_FILE_MC',
+             "afii": 'ASG_TEST_FILE_MC_AFII'}
+
+# Set up the reading of the input file:
+import AthenaRootComps.ReadAthenaxAODHybrid
+theApp.EvtMax = 500
+testFile = os.getenv ( inputfile[dataType] )
+svcMgr.EventSelector.InputCollections = [testFile]
+
+from TriggerAnalysisAlgorithms.TriggerAnalysisAlgorithmsTest import makeSequence
+algSeq = makeSequence (dataType)
+print algSeq # For debugging
+
+# Add all algorithms from the sequence to the job.
+athAlgSeq += algSeq
+
+# Set up a histogram output file for the job:
+ServiceMgr += CfgMgr.THistSvc()
+ServiceMgr.THistSvc.Output += [
+    "ANALYSIS DATAFILE='TriggerAlgorithmsTest." + dataType + ".hist.root' OPT='RECREATE'"
+    ]
+
+# Reduce the printout from Athena:
+include( "AthAnalysisBaseComps/SuppressLogging.py" )
diff --git a/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/src/components/TriggerAnalysisAlgorithms_entries.cxx b/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/src/components/TriggerAnalysisAlgorithms_entries.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..aece9a7e751ec8175aa6ec410eb0b793f92ff366
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/src/components/TriggerAnalysisAlgorithms_entries.cxx
@@ -0,0 +1,20 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Tadej Novak
+
+
+#include <GaudiKernel/DeclareFactoryEntries.h>
+
+#include <TriggerAnalysisAlgorithms/TrigEventSelectionAlg.h>
+#include <TriggerAnalysisAlgorithms/TrigPrescalesAlg.h>
+
+
+DECLARE_NAMESPACE_ALGORITHM_FACTORY (CP, TrigEventSelectionAlg)
+DECLARE_NAMESPACE_ALGORITHM_FACTORY (CP, TrigPrescalesAlg)
+
+DECLARE_FACTORY_ENTRIES(TriggerAnalysisAlgorithms) {
+  DECLARE_NAMESPACE_ALGORITHM (CP, TrigEventSelectionAlg)
+  DECLARE_NAMESPACE_ALGORITHM (CP, TrigPrescalesAlg)
+}
diff --git a/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/src/components/TriggerAnalysisAlgorithms_load.cxx b/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/src/components/TriggerAnalysisAlgorithms_load.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..1ba6022270b61d9a459b7d658eabc026ae7190dc
--- /dev/null
+++ b/PhysicsAnalysis/Algorithms/TriggerAnalysisAlgorithms/src/components/TriggerAnalysisAlgorithms_load.cxx
@@ -0,0 +1,10 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Tadej Novak
+
+
+#include "GaudiKernel/LoadFactoryEntries.h"
+
+LOAD_FACTORY_ENTRIES(TriggerAnalysisAlgorithms)
diff --git a/Projects/Athena/package_filters.txt b/Projects/Athena/package_filters.txt
index e4383323a0ac882711d458b6235737f56e843f1a..9a7107070e1b4d83e6cf19f43f886b523cdc7722 100644
--- a/Projects/Athena/package_filters.txt
+++ b/Projects/Athena/package_filters.txt
@@ -29,6 +29,7 @@
 
 # Some analysis packages that are not part of Athena
 - Control/AthLinksSA
+- PhysicsAnalysis/Algorithms/.*
 - PhysicsAnalysis/AnalysisCommon/CPAnalysisExamples
 - PhysicsAnalysis/AnalysisCommon/PMGTools
 - PhysicsAnalysis/D3PDTools/EventLoop.*