diff --git a/Trigger/TrigCost/EnhancedBiasWeighter/EnhancedBiasWeighter/EnhancedBiasWeighter.h b/Trigger/TrigCost/EnhancedBiasWeighter/EnhancedBiasWeighter/EnhancedBiasWeighter.h
index f0801633642207d239dc4b589f61b77f531d11fe..c330031086695ffd206e8b296e538b1616e5e81b 100644
--- a/Trigger/TrigCost/EnhancedBiasWeighter/EnhancedBiasWeighter/EnhancedBiasWeighter.h
+++ b/Trigger/TrigCost/EnhancedBiasWeighter/EnhancedBiasWeighter/EnhancedBiasWeighter.h
@@ -136,6 +136,11 @@ class EnhancedBiasWeighter: public asg::AsgTool, public virtual IEnhancedBiasWei
    virtual bool isGoodLB(const xAOD::EventInfo* eventInfo) const override; 
    virtual bool isGoodLB(const EventContext& context) const override; 
 
+  /**
+   * @return If the tool is configured to process MC
+   */
+   virtual bool isMC() const override;
+
   /**
    * @brief Parse a presscale XML and return a ordered summary of its content
    * To make most use of the XML parsing available already in this class
@@ -158,26 +163,26 @@ class EnhancedBiasWeighter: public asg::AsgTool, public virtual IEnhancedBiasWei
 
     ToolHandle<Trig::IBunchCrossingTool> m_bcTool; //!< Tool to get distance into bunch train
 
-    uint32_t m_runNumber; //!< Run we're processing - needed at initialize to locate and read in extra configuration.  
+    Gaudi::Property<uint32_t> m_runNumber{this, "RunNumber", 0, "Run we're processing (if data), needed at initialize to locate and read in extra configuration."};
+    Gaudi::Property<bool> m_calculateWeightingData{this, "CalculateWeightingData", true, "If true, read from COOL, CONDBR2 and XMLs. If false, read directly from decorated TRIG1 dAOD."};
+    Gaudi::Property<bool> m_enforceEBGRL{this, "EnforceEBGRL", true, "Each Enhanced Bias run has a 'good run list' style veto on some LB. If this flag is true, events in these LB get weight 0"};
+    Gaudi::Property<bool> m_useBunchCrossingTool{this, "UseBunchCrossingTool", true, "BunchCrossing tool requires CONDBR2 access. Can be disabled here if this is a problem."};
+    Gaudi::Property<bool> m_isMC{this, "IsMC", false,  "MC mode? If so we need a cross section and filter efficiency"};
+    Gaudi::Property<double> m_mcCrossSection{this, "MCCrossSection", 0.0, "If running over MC. The process cross section in nb (AMI gives thins in nb)"};
+    Gaudi::Property<double> m_mcFilterEfficiency{this, "MCFilterEfficiency", 1.0, "If running over MC. The process filter efficiency (0.0-1.0)"}; 
+    Gaudi::Property<double> m_mcKFactor{this, "MCKFactor", 1.0, "If running over MC. The process filter efficiency (0.0-1.0)"}; 
+    Gaudi::Property<double> m_inelasticCrossSection{this, "InelasticCrossSection", 8e-26, "Inelastic cross section in units cm^2. Default 80 mb at 13 TeV."}; 
+
     double m_deadtime; //!< Online deadtime to correct for in rate prediction. Currently a constant for the EB period
     uint32_t m_pairedBunches; //!< Online number of paired bunches.
-    bool m_calculateWeightingData; //!< True if data is to be loaded from COOL and CVMFS. False if it is to be fetched from a TRIG1 decorated dAOD
-    bool m_enforceEBGRL; //!< Always return weight 0 if the event is vetoed due to enhanced bias 'good run list'
-    bool m_useBunchCrossingTool; //!< Allow disabling of bunch crossing tool for situations when CONDBR2 is not accessible
-
-    bool m_isMC; //!< True if supplying weights for MC
-    double m_mcCrossSection; //!< If MC, the cross section in nb
-    double m_mcFilterEfficiency; //!< If MC, the filter efficiency
-    double m_mcKFactor; //!< If MC, any k factor
     double m_mcModifiedCrossSection; //!< Product of xsec, filter & kfactor. In units of cm
-    double m_inelasticCrossSection; //!< Sigma_inel in units of cm^2. Default of 80 mb @ 13 TeV = 8e-26 cm^2
-
     std::unordered_map<uint64_t, int32_t> m_eventNumberToIdMap; //!< Map event number to a weighting ID
 
     mutable std::unordered_map<uint32_t, float> m_eventLivetime; //!< Cache of per-event livetime as a function of LB [LB -> effective walltime per event]
     mutable double   m_lumiAverageNum; //!< The average instantaneous lumionosity over all events. Numerator
     mutable double   m_muAverageNum; //!< The average mu over all events. Numerator
     mutable uint32_t m_averageDenom; //!< The average over all events. Denominator
+    mutable std::mutex m_mutex; //!< Protect my mutable parts in a MT envirnment
 
     std::unordered_map<int32_t, double>    m_idToWeightMap; //!< Map a weighting ID to a Enhanced Bias event weight
     std::unordered_map<int32_t, uint8_t>   m_idToUnbiasedMap; //!< Map a weighting ID to a flag if this weight is from an unbiased (RD) trigger online
@@ -186,8 +191,6 @@ class EnhancedBiasWeighter: public asg::AsgTool, public virtual IEnhancedBiasWei
     std::unordered_map<uint32_t, uint8_t>  m_goodLB;  //!< Like a Good Run List flag for EnhancedBias runs.
 
     ReadLumiBlock m_readLumiBlock; //!< Cache lumi block lengths. Get this from COOL.
-
-    mutable std::mutex m_mutex; //!< Protect my mutable parts in a MT envirnment
 }; 
 
 #endif //> !ENHANCEDBIASWEIGHTER_ENHANCEDBIASWEIGHTER_H
diff --git a/Trigger/TrigCost/EnhancedBiasWeighter/EnhancedBiasWeighter/IEnhancedBiasWeighter.h b/Trigger/TrigCost/EnhancedBiasWeighter/EnhancedBiasWeighter/IEnhancedBiasWeighter.h
index 9f83446830217bd7e463be8fd6bef8ea9fa48788..14bd03b9f36dd12ffd4738b426d2ae237abeb83d 100644
--- a/Trigger/TrigCost/EnhancedBiasWeighter/EnhancedBiasWeighter/IEnhancedBiasWeighter.h
+++ b/Trigger/TrigCost/EnhancedBiasWeighter/EnhancedBiasWeighter/IEnhancedBiasWeighter.h
@@ -35,6 +35,7 @@ class IEnhancedBiasWeighter : public virtual DerivationFramework::IAugmentationT
       virtual bool     isUnbiasedEvent(const xAOD::EventInfo* eventInfo) const = 0;
       virtual bool     isGoodLB(const xAOD::EventInfo* eventInfo) const = 0;
       virtual bool     isGoodLB(const EventContext& context) const = 0;
+      virtual bool     isMC() const = 0;
       virtual std::unordered_map<std::string, ChainDetail> parsePrescaleXML(const std::string& prescaleXML) const = 0;
 
 };
diff --git a/Trigger/TrigCost/EnhancedBiasWeighter/Root/EnhancedBiasWeighter.cxx b/Trigger/TrigCost/EnhancedBiasWeighter/Root/EnhancedBiasWeighter.cxx
index 73843cab92264a1f501d72171d0604d8b960ade9..d4a64d2369ed55977e8160b7cf662fed7e7645a8 100644
--- a/Trigger/TrigCost/EnhancedBiasWeighter/Root/EnhancedBiasWeighter.cxx
+++ b/Trigger/TrigCost/EnhancedBiasWeighter/Root/EnhancedBiasWeighter.cxx
@@ -33,17 +33,7 @@ EnhancedBiasWeighter::EnhancedBiasWeighter( const std::string& name )
   m_lumiAverageNum(0),
   m_muAverageNum(0),
   m_averageDenom(0)
-{
-  declareProperty( "RunNumber", m_runNumber = 0);
-  declareProperty( "CalculateWeightingData", m_calculateWeightingData = true, "If true, read from COOL, CONDBR2 and XMLs. If false, read directly from decorated TRIG1 dAOD.");
-  declareProperty( "EnforceEBGRL", m_enforceEBGRL = true, "Each Enhanced Bias run has a 'good run list' style veto on some LB. If this flag is true, events in these LB get weight 0");
-  declareProperty( "IsMC", m_isMC = false, "MC mode? If so we need a cross section and filter efficiency");
-  declareProperty( "MCCrossSection", m_mcCrossSection = 0, "If running over MC. The process cross section in nb (AMI gives thins in nb)");
-  declareProperty( "MCFilterEfficiency", m_mcFilterEfficiency = 0, "If running over MC. The process filter efficiency (0.0-1.0)");
-  declareProperty( "MCKFactor", m_mcKFactor = 1., "If running over MC. Higher-order corrections fudge factor to the cross section");
-  declareProperty( "InelasticCrossSection", m_inelasticCrossSection = 8e-26, "Inelastic cross section in units cm^2. Default 80 mb at 13 TeV.");
-  declareProperty( "UseBunchCrossingTool", m_useBunchCrossingTool = true, "BunchCrossing tool requires CONDBR2 access. Can be disabled here if this is a problem.");
-}
+{}
 
 StatusCode EnhancedBiasWeighter::initialize() 
 {
@@ -95,8 +85,9 @@ StatusCode EnhancedBiasWeighter::loadWeights()
 {
   // Construct name
   std::stringstream fileNameDev, fileName;
-  fileName    << "TrigCostRootAnalysis/EnhancedBiasWeights_" << m_runNumber << ".xml";
-  fileNameDev << "dev/TrigCostRootAnalysis/EnhancedBiasWeights_" << m_runNumber << ".xml";
+  const uint32_t runNumber = m_runNumber; // This is because Gaudi::Properties have special behaviour with the << operator
+  fileName    << "TrigCostRootAnalysis/EnhancedBiasWeights_" << runNumber << ".xml";
+  fileNameDev << "dev/TrigCostRootAnalysis/EnhancedBiasWeights_" << runNumber << ".xml";
 
   std::string weightingFile = PathResolverFindCalibFile( fileName.str() );  // Check standard area
   if (weightingFile == "") {
@@ -159,14 +150,15 @@ StatusCode EnhancedBiasWeighter::loadWeights()
     eventNode = xml->GetNext(eventNode);
   }
 
-  ATH_MSG_INFO ("Loaded " << m_eventNumberToIdMap.size() << " event weights for run " << m_runNumber);
+  ATH_MSG_INFO ("Loaded " << m_eventNumberToIdMap.size() << " event weights for run " << runNumber);
   return StatusCode::SUCCESS;
 }
 
 StatusCode EnhancedBiasWeighter::loadLumi()
 {
   // Fetch LB time from COOL for this run
-  if (m_readLumiBlock.updateLumiBlocks(m_runNumber, msg()) == false) {
+  const uint32_t runNumber = m_runNumber;
+  if (m_readLumiBlock.updateLumiBlocks(runNumber, msg()) == false) {
     ATH_MSG_FATAL("Unable to load this runs luminosity values from COOL.");
     return StatusCode::FAILURE;
   }
@@ -174,8 +166,8 @@ StatusCode EnhancedBiasWeighter::loadLumi()
   // Read in number of events to expect 
   // Construct name
   std::stringstream fileNameDev, fileName;
-  fileName    << "TrigCostRootAnalysis/enhanced_bias_run_" << m_runNumber << ".xml";
-  fileNameDev << "dev/TrigCostRootAnalysis/enhanced_bias_run_" << m_runNumber << ".xml";
+  fileName    << "TrigCostRootAnalysis/enhanced_bias_run_" << runNumber << ".xml";
+  fileNameDev << "dev/TrigCostRootAnalysis/enhanced_bias_run_" << runNumber << ".xml";
 
   std::string runFile = PathResolverFindCalibFile( fileName.str() );  // Check standard area
   if (runFile == "") {
@@ -259,7 +251,7 @@ StatusCode EnhancedBiasWeighter::loadLumi()
     listNode = xml->GetNext(listNode);
   }
 
-  ATH_MSG_INFO ("Loaded " << m_eventsPerLB.size() << " EnhancedBias lumi block's info for run " << m_runNumber);
+  ATH_MSG_INFO ("Loaded " << m_eventsPerLB.size() << " EnhancedBias lumi block's info for run " << runNumber);
   return StatusCode::SUCCESS;
 }
 
@@ -677,6 +669,11 @@ bool EnhancedBiasWeighter::isGoodLB(const EventContext& context) const
   }
 }
 
+bool EnhancedBiasWeighter::isMC() const {
+  return m_isMC;
+}
+
+
 double EnhancedBiasWeighter::getLBLumi(const xAOD::EventInfo* eventInfo) const
 {
   if (m_calculateWeightingData) {
diff --git a/Trigger/TrigCost/RatesAnalysis/CMakeLists.txt b/Trigger/TrigCost/RatesAnalysis/CMakeLists.txt
index cc69a35572b69a9466d7682c96e7ffab94a4acaf..456fed105a2db430757c0f516b8f612768c6cd0a 100644
--- a/Trigger/TrigCost/RatesAnalysis/CMakeLists.txt
+++ b/Trigger/TrigCost/RatesAnalysis/CMakeLists.txt
@@ -12,20 +12,29 @@ atlas_depends_on_subdirs( PUBLIC
                           Trigger/TrigAnalysis/TrigDecisionTool
                           Trigger/TrigCost/EnhancedBiasWeighter
                           PRIVATE
-                          Event/EventInfo
                           Event/xAOD/xAODEventInfo )
 
 # External dependencies:
 find_package( ROOT COMPONENTS Core Tree MathCore Hist RIO pthread )
 
-# Component(s) in the package:
-atlas_add_library( RatesAnalysis
-                   src/*.cxx
+# Package library:
+atlas_add_library( RatesAnalysisLib
+                   src/Rates*.cxx
                    PUBLIC_HEADERS RatesAnalysis
                    INCLUDE_DIRS ${ROOT_INCLUDE_DIRS}
                    LINK_LIBRARIES ${ROOT_LIBRARIES} GaudiKernel AthAnalysisBaseCompsLib TrigDecisionToolLib EnhancedBiasWeighterLib
-                   PRIVATE_LINK_LIBRARIES EventInfo xAODEventInfo )
+                   PRIVATE_LINK_LIBRARIES xAODEventInfo )
+
+# Component(s) in the package:
+atlas_add_component( RatesAnalysis
+                     src/FullMenu.cxx
+                     src/components/RatesAnalysis_entries.cxx
+                     LINK_LIBRARIES GaudiKernel RatesAnalysisLib )
 
 atlas_add_test( RatesAnalysis_test
                 SOURCES test/RatesAnalysis_test.cxx
-                LINK_LIBRARIES ${ROOT_LIBRARIES} GaudiKernel AthAnalysisBaseCompsLib RatesAnalysis TrigDecisionToolLib EnhancedBiasWeighterLib EventInfo xAODEventInfo )
+                LINK_LIBRARIES ${ROOT_LIBRARIES} GaudiKernel AthAnalysisBaseCompsLib RatesAnalysisLib TrigDecisionToolLib EnhancedBiasWeighterLib xAODEventInfo )
+
+# Install files from the package:
+atlas_install_python_modules( python/*.py )
+atlas_install_scripts( share/RatesAnalysisFullMenu.py )
diff --git a/Trigger/TrigCost/RatesAnalysis/RatesAnalysis/RatesAnalysisAlg.h b/Trigger/TrigCost/RatesAnalysis/RatesAnalysis/RatesAnalysisAlg.h
index 58da2957e528cfd152e6795a79d1603a2e75c408..a12de7ed902be4480d61e371cce4173c4aec9fab 100644
--- a/Trigger/TrigCost/RatesAnalysis/RatesAnalysis/RatesAnalysisAlg.h
+++ b/Trigger/TrigCost/RatesAnalysis/RatesAnalysis/RatesAnalysisAlg.h
@@ -181,8 +181,7 @@ class RatesAnalysisAlg: public ::AthAnalysisAlgorithm {
 
   virtual StatusCode initialize(); //!< Get the trigger decision tool and set up global groups
   virtual StatusCode execute(); //!< In first call - register all triggers. Then load event weighting parameters, fill trigger decisions, compute group rates.
-  virtual StatusCode finalize(); //!< Print rates and normalise histograms
-  virtual StatusCode beginInputFile(); //!< Setup of the Enhanced Bias weighting tool. Needs AOD metadata so can not be set up before. Also read any prescale XML supplied.
+  virtual StatusCode finalize(); //!< Print rates
 
   StatusCode populateTriggers(); //!< Register all triggers to emulate. This is actually done at the start of the event loop such that the TDT has access to the configuration.
   StatusCode executeTrigDecisionToolTriggers(); //!< Internal call to get the pass/fail for all TDT triggers
@@ -229,11 +228,11 @@ class RatesAnalysisAlg: public ::AthAnalysisAlgorithm {
   
   bool isZero(double v) const { return fabs(v) < 1e-10; } //!< Helper function for floating point subtraction
 
-  std::unordered_map<std::string, RatesTrigger> m_triggers; //!< All individual triggers (L1 or HLT)
-  std::unordered_map<std::string, RatesScanTrigger> m_scanTriggers; //!< All individual rates-scan triggers (L1 or HLT)
-  std::unordered_map<std::string, RatesGroup> m_groups; //!< All regular and CPS groups 
-  std::unordered_map<std::string, RatesGroup> m_globalGroups; //!< Big (master) groups which do the OR of the whole menu 
-  std::unordered_map<std::string, RatesGroup> m_uniqueGroups; //!< Groups used to obtain unique rates for chains.
+  std::unordered_map<std::string, std::unique_ptr<RatesTrigger>> m_triggers; //!< All individual triggers (L1 or HLT)
+  std::unordered_map<std::string, std::unique_ptr<RatesScanTrigger>> m_scanTriggers; //!< All individual rates-scan triggers (L1 or HLT)
+  std::unordered_map<std::string, std::unique_ptr<RatesGroup>> m_groups; //!< All regular and CPS groups 
+  std::unordered_map<std::string, std::unique_ptr<RatesGroup>> m_globalGroups; //!< Big (master) groups which do the OR of the whole menu 
+  std::unordered_map<std::string, std::unique_ptr<RatesGroup>> m_uniqueGroups; //!< Groups used to obtain unique rates for chains.
 
   std::unordered_set<RatesTrigger*> m_activatedTriggers; //!< Triggers which were changed & hence need to be reset at the event end.
   std::unordered_set<RatesTrigger*> m_expressTriggers; //!< Triggers with non-zero express PS, used to print them at the end.
@@ -248,29 +247,28 @@ class RatesAnalysisAlg: public ::AthAnalysisAlgorithm {
   const std::string m_l2GroupName = "Main";
   const std::string m_expressGroupName = "Express";
 
-  ToolHandle<IEnhancedBiasWeighter> m_enhancedBiasRatesTool; //!< Tool to access weighting information required for trigger rates.
-  ToolHandle<Trig::TrigDecisionTool> m_tdt;
+  ToolHandle<IEnhancedBiasWeighter> m_enhancedBiasRatesTool{this, "EnhancedBiasRatesTool", "EnhancedBiasWeighter/EnhancedBiasRatesTool"};
+  ToolHandle<Trig::TrigDecisionTool> m_tdt{this, "TrigDecisionTool", "Trig::TrigDecisionTool/TrigDecisionTool"};
+
+  Gaudi::Property<double> m_expoScalingFactor{this, "ExpoScalingFactor", 0.1, "Optional. Exponential factor if using exponential-mu rates scaling."};
+  Gaudi::Property<double> m_inelasticCrossSection{this, "InelasticCrossSection", 8e-26, "Inelastic cross section in units cm^2. Default 80 mb at 13 TeV."};
+  Gaudi::Property<bool> m_doUniqueRates{this, "DoUniqueRates", false, "Calculate unique rates for all chains (slow). Requires DoGlobalGroups=True too."}; 
+  Gaudi::Property<bool> m_doGlobalGroups{this, "DoGlobalGroups", false, "Calculate total rates for each trigger level."};
+  Gaudi::Property<bool> m_doTriggerGroups{this, "DoTriggerGroups", false, "Calculate total rates for each group of triggers."};
+  Gaudi::Property<bool> m_doExpressRates{this, "DoExpressRates", false, "Calculate total rates for the express stream."};
+  Gaudi::Property<bool> m_useBunchCrossingTool{this, "UseBunchCrossingTool", true, "BunchCrossing tool requires CONDBR2 access. Can be disabled here if this is a problem."};
+  Gaudi::Property<bool> m_currentEventIsUnbiased; //!< If the current event was triggered online by RDx or not. Random seeded HLT chains must only see these
+  Gaudi::Property<bool> m_doHistograms{this, "DoHistograms", true, "Switch on histogram output of rate vs. mu and position in train."};
+  Gaudi::Property<bool> m_enableLumiExtrapolation{this, "EnableLumiExtrapolation", true, "If false then no extrapolation in L, N_bunch or <mu> will be performed.."};
+  Gaudi::Property<uint32_t> m_vetoStartOfTrain{this, "VetoStartOfTrain", 0, "How many BCID to veto at the start of a bunch train."};
+  Gaudi::Property<std::string> m_prescaleXML{this, "PrescaleXML", "",  "Optional XML of prescales from the TrigRuleBook to apply."};
 
-  double m_inelasticCrossSection; //!< Sigma_inel in units of cm^2. Default of 80 mb @ 13 TeV = 8e-26 cm^2
   double m_targetMu; //!< What pileup level the prediction is targeting
   double m_targetBunches; //!< How many bunches the prediction is targeting
   double m_targetLumi; //!< What instantaneous luminosity the prediction is targeting
-  double m_expoScalingFactor; //!< Exponential factor for exponential-in-mu chains
-  bool m_doUniqueRates; //!< What rate is unique to each trigger. More computationally taxing.
-  bool m_useBunchCrossingTool; //!< If rates should be done vs. position in the train. Requires DB access
-  bool m_currentEventIsUnbiased; //!< If the current event was triggered online by RDx or not. Random seeded HLT chains must only see these
-  bool m_doHistograms; //!< If histogram export is enabled or not.
-  bool m_isMC; //!< If input is Monte Carlo
-  bool m_normaliseHistograms; //!< Flag to apply normalisation to histograms. Easier to set this to FALSE if will be needing to hadd
-  bool m_enableLumiExtrapolation; //!< Global flag if extrapolation is to be used. If not, results will be presented at the input file's lumi
-  uint32_t m_vetoStartOfTrain; //!< If > 0, then veto this many BCIDs from the start of a train
   double m_ratesDenominator; //!< How much walltime is seen by the algorithm. This is what we need to normalise to.
-  double m_mcCrossSection; //!< If MC, the cross section in nb
-  double m_mcFilterEfficiency; //!< If MC, the filter efficiency
-  double m_mcKFactor; //!< If MC, any higher order k-factor
   uint32_t m_eventCounter; //!< Count how many events processed
-  double   m_weightedEventCounter; //!< Count how many weighted events were processed
-  std::string m_prescaleXML; //!< Filename of XML of prescales to load
+  double m_weightedEventCounter; //!< Count how many weighted events were processed
 
   TH1D* m_scalingHist; //!< One-bin histogram to store the normalisation of the sample, for use in later combinations
   TH1D* m_bcidHist; //!< Histogram of the BCIDs distribution of the processing
diff --git a/Trigger/TrigCost/RatesAnalysis/RatesAnalysis/RatesGroup.h b/Trigger/TrigCost/RatesAnalysis/RatesAnalysis/RatesGroup.h
index fc4abbb8bc1c325fe0f16eef7d59993200e37e9c..10258b0e50ad5bbe83e058bb819f09b15a494fdb 100644
--- a/Trigger/TrigCost/RatesAnalysis/RatesAnalysis/RatesGroup.h
+++ b/Trigger/TrigCost/RatesAnalysis/RatesAnalysis/RatesGroup.h
@@ -39,7 +39,11 @@ class RatesGroup : public RatesHistoBase {
    */
   RatesGroup(const std::string& name, const MsgStream& log, const bool doHistograms = true, const bool doExtrapolation = true);
 
-  ~RatesGroup();
+  virtual ~RatesGroup();
+
+  RatesGroup(const RatesGroup&) = delete;
+
+  RatesGroup& operator=(const RatesGroup&) = delete;
 
   /**
    * @brief Add a trigger to this group. It will be stored in a set mapped to its L1 seed. 
@@ -114,7 +118,7 @@ class RatesGroup : public RatesHistoBase {
   bool m_doCachedWeights; //!< Used in the global rates group. Cache extra information for the benefit of the unique rate groups
   std::unordered_map<size_t, double> m_cachedWeights; //!< Cached weight of the OR of all triggers *except* for the L1 seed-hash of the key here.
   bool m_useCachedWeights; //!< Efficiency. Required m_masterGroup to have been set.
-  const bool m_doLumiExtrapolation; //!< If we are using lumi extrapolation or not 
+  const ExtrapStrat_t m_extrapolationStrategy; //!< How this group is to scale with luminosity. Currently supported are linear and none.
   const RatesGroup* m_masterGroup; //!< If not nullptr, then use the cached weights info in this master group object
   RatesTrigger* m_uniqueTrigger; //!< If not nullptr, then a trigger this group is calculating the unique rate for. Needs non-const as fills a histo
 
diff --git a/Trigger/TrigCost/RatesAnalysis/RatesAnalysis/RatesHistoBase.h b/Trigger/TrigCost/RatesAnalysis/RatesAnalysis/RatesHistoBase.h
index 6e888eddde797b7e0d17bd9949b6beea06113f29..1e8e213a8a96b7c759fbd988a2b48d8608fb5735 100644
--- a/Trigger/TrigCost/RatesAnalysis/RatesAnalysis/RatesHistoBase.h
+++ b/Trigger/TrigCost/RatesAnalysis/RatesAnalysis/RatesHistoBase.h
@@ -9,6 +9,7 @@
 #include "TString.h"
 
 #include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/ServiceHandle.h"
 
 #include <string>
 #include <unordered_map>
@@ -19,6 +20,8 @@
 #include <iomanip>
 #include <memory>
 
+class ITHistSvc; // Forward
+
 /**
  * Extrapolation strategy to apply to each emulated trigger.
  * @see RatesAnalysisAlg::setTargetLumi
@@ -80,11 +83,18 @@ class RatesHistoBase {
   RatesHistoBase(const std::string& name, const MsgStream& log, const bool doHistograms = true);
   virtual ~RatesHistoBase();
 
-  TH1D* getMuHist(bool clientIsTHistSvc = false); //!< @return histogram pointer or nullptr and an error
-  TH1D* getDataHist(bool clientIsTHistSvc = false); //!< @return histogram pointer or nullptr. Does not cause error
-  TH1D* getTrainHist(bool clientIsTHistSvc = false); //!< @return histogram pointer or nullptr and an error
-  virtual void normaliseHist(const double ratesDenominator); //!< Normalise to walltime to get rate.
+  RatesHistoBase(const RatesHistoBase&) = delete;
+
+  RatesHistoBase& operator=(const RatesHistoBase&) = delete;
+
+  StatusCode giveMuHist(const ServiceHandle<ITHistSvc>& svc, const std::string& name); 
+  StatusCode giveTrainHist(const ServiceHandle<ITHistSvc>& svc, const std::string& name); 
+  StatusCode giveDataHist(const ServiceHandle<ITHistSvc>& svc, const std::string& name); 
+  void clearTrainHist(); //!< In some jobs we don't want to do the rates vs. position in train
+  TH1* getDataHist(); //!< @return cached, non-owning, histogram pointer or nullptr. Does not cause error
   bool doHistograms() const { return m_doHistograms; } //!< If histogramming was enabled in this rates object
+  const std::string& getExtrapolationFactorString(ExtrapStrat_t strat) const;
+  double getExtrapolationFactor(const WeightingValuesSummary_t& weights, const ExtrapStrat_t strat) const;
 
   static bool isZero(double v) { return fabs(v) < 1e-10; } //<! Helper fn
 
@@ -92,13 +102,15 @@ class RatesHistoBase {
 
   std::string m_name; //!< My name
   bool m_doHistograms; //!< If histogramming is switched on
-  TH1D* m_rateVsMu; //!< Histogram of rate as a fn. of the input event's mu
-  TH1D* m_rateVsTrain; //!< Histogram of rate as a fn. of position in bunch train
-  TH1D* m_data; //!< Histogram of raw rates quantites, for when we need to normalise offline (e.g. grid processing)
+  std::unique_ptr<TH1> m_rateVsMu; //!< Histogram of rate as a fn. of the input event's mu
+  std::unique_ptr<TH1> m_rateVsTrain; //!< Histogram of rate as a fn. of position in bunch train
+  std::unique_ptr<TH1> m_data;  //!< Histogram of raw rates quantites, for when we need to normalise offline (e.g. grid processing)
+  TH1* m_rateVsMuCachedPtr; //!< Cached, non-owning pointer
+  TH1* m_rateVsTrainCachedPtr;  //!< Cached, non-owning pointer
+  TH1* m_dataCachedPtr; //!< Cached, non-owning pointer
   bool  m_givenRateVsMu; //!< m_rateVsMu has been given to the THistSvc and should not be deleted
   bool  m_givenRateVsTrain; //!< m_rateVsTrain has been given to the THistSvc and should not be deleted
   bool  m_givenData; //!< m_data has been given to the THistSvc and should not be deleted
-  static uint32_t m_histoID; //!< Give every histo a unique name using this
 
   mutable MsgStream m_log; //!< For ATHENA messaging
 };
diff --git a/Trigger/TrigCost/RatesAnalysis/RatesAnalysis/RatesScanTrigger.h b/Trigger/TrigCost/RatesAnalysis/RatesAnalysis/RatesScanTrigger.h
index b1966f7b69290c8631ff963028be873a7bb7db8c..dd55384a94c3d818476ffc1436fd3ee93315c25b 100644
--- a/Trigger/TrigCost/RatesAnalysis/RatesAnalysis/RatesScanTrigger.h
+++ b/Trigger/TrigCost/RatesAnalysis/RatesAnalysis/RatesScanTrigger.h
@@ -66,7 +66,11 @@ class RatesScanTrigger : public RatesTrigger {
     const double seedPrecale = 1.,
     const ExtrapStrat_t extrapolation = ExtrapStrat_t::kLINEAR);
 
-  ~RatesScanTrigger();
+  virtual ~RatesScanTrigger();
+
+  RatesScanTrigger(const RatesScanTrigger&) = delete;
+
+  RatesScanTrigger& operator=(const RatesScanTrigger&) = delete;
 
   void reset() override { m_thresholdPassed = std::numeric_limits<double>::min(); } //!< If I was used in an event, reset me
 
@@ -92,20 +96,18 @@ class RatesScanTrigger : public RatesTrigger {
    */
   void execute(const WeightingValuesSummary_t& weights) override;
 
-  void normaliseHist(const double ratesDenominator) override; //!< Apply normalisation scaling to histograms
-
   /**
    * @brief Prints the RatesScanTrigger's rate (different output to a regular trigger)
    * @param ratesDenominator The walltime for the run, needed to normalise from integrated weighted counts to a rate.
    */
   const std::string printRate(const double ratesDenominator) const override;
 
-  TH1D* getThresholdHist(bool clientIsTHistSvc = false); //!< Get a pointer to the rate as a fn. of threshold
+  StatusCode giveThresholdHist(const ServiceHandle<ITHistSvc>& svc, const std::string& name); 
 
  private: 
 
-  TH1D* m_rateScanHist; //!< Even if we are not exporting it - we still need this histo 
-  bool m_givenRateScanHist; //!< m_rateScanHist has been given to the THistSvc and should not be deleted
+  std::unique_ptr<TH1> m_rateScanHist; //!< Even if we are not exporting it - we still need this histo 
+  TH1* m_rateScanHistCachedPtr;
   double m_thresholdPassed; //!< Analogous to m_pass. This is the threshold that the trigger passed in the event
   TriggerBehaviour_t m_behaviour; //!< If we need to be above or below the threshold to cause the trigger to fire
 
diff --git a/Trigger/TrigCost/RatesAnalysis/RatesAnalysis/RatesTrigger.h b/Trigger/TrigCost/RatesAnalysis/RatesAnalysis/RatesTrigger.h
index 8f0279b1a6ad586e0017d67dee5fc2be355b0cbd..6ffccd3301688073c1c2439706053224ac017370 100644
--- a/Trigger/TrigCost/RatesAnalysis/RatesAnalysis/RatesTrigger.h
+++ b/Trigger/TrigCost/RatesAnalysis/RatesAnalysis/RatesTrigger.h
@@ -37,6 +37,10 @@ class RatesTrigger : public RatesHistoBase {
     const ExtrapStrat_t extrapolation = ExtrapStrat_t::kLINEAR);
   ~RatesTrigger();
 
+  RatesTrigger(const RatesTrigger&) = delete;
+
+  RatesTrigger& operator=(const RatesTrigger&) = delete;
+
   virtual void reset() { m_pass = false; } //!< If I was used in an event, reset me.
 
   void setSeedsFromRandom(const bool i) { m_seedsFromRandom = i; } //!< Set if this trigger is to behave as if it seeds from a random L1 item
@@ -103,13 +107,6 @@ class RatesTrigger : public RatesHistoBase {
    */
   const std::string printExpressRate(const double ratesDenominator) const;
 
-  /**
-   * @brief Use my m_extrapolationStrategy to select the correct weight from the supplied WeightingValuesSummary_t
-   * @param weights Struct of weights from which to choose the correct one
-   * @return The extrapolation weight
-   */
-  double getExtrapolationFactor(const WeightingValuesSummary_t& weights) const;
-
  protected: 
 
   bool m_pass; //!< Did the trigger pass or not?
diff --git a/Trigger/TrigCost/RatesAnalysis/share/RatesAnalysisFullMenu.py b/Trigger/TrigCost/RatesAnalysis/share/RatesAnalysisFullMenu.py
new file mode 100755
index 0000000000000000000000000000000000000000..58322ee6dab6921d6fbc711e05e460163c2f485d
--- /dev/null
+++ b/Trigger/TrigCost/RatesAnalysis/share/RatesAnalysisFullMenu.py
@@ -0,0 +1,149 @@
+#!/usr/bin/env python
+#
+#  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+#
+
+if __name__=='__main__':
+  import sys
+  from argparse import ArgumentParser
+  parser = ArgumentParser()
+  parser.add_argument('--disableHistograms', action='store_false', help='Turn off histograming')
+  parser.add_argument('--disableGlobalGroups', action='store_false', help='Turn off global groups')
+  parser.add_argument('--disableTriggerGroups', action='store_false', help='Turn off per-trigger groups')
+  parser.add_argument('--disableExpressGroup', action='store_false', help='Turn off express stream rates')
+  parser.add_argument('--disableUniqueRates', action='store_false', help='Turn off unique rates (much faster!)')
+  parser.add_argument('--disableLumiExtrapolation', action='store_false', help='Turn off luminosity extrapolation')
+  #
+  parser.add_argument('--doRatesVsPositionInTrain', action='store_true', help='Study rates vs BCID position in bunch train')
+  parser.add_argument('--vetoStartOfTrain', default=0, type=int, help='Number of BCIDs at the start of the train to veto, implies doRatesVsPositionInTrain')
+  #
+  parser.add_argument('--outputHist', default='RatesHistograms.root', type=str, help='Histogram output ROOT file')
+  parser.add_argument('--inputPrescalesXML', default='', type=str, help='XML of prescales to simulate applying when computing rates')
+  #
+  parser.add_argument('--targetLuminosity', default=2e34, type=float)
+  #
+  parser.add_argument('--MCDatasetName', default='', type=str, help='For MC input: Name of the dataset, can be used instead of MCCrossSection, MCFilterEfficiency')
+  parser.add_argument('--MCCrossSection', default=0.0, type=float, help='For MC input: Cross section of process in nb')
+  parser.add_argument('--MCFilterEfficiency', default=1.0, type=float, help='For MC input: Filter efficiency of any MC filter (0.0 - 1.0)')
+  parser.add_argument('--MCKFactor', default=1.0, type=float, help='For MC input: Additional multiplicitive fudge-factor to the supplied cross section.')
+  #
+  parser.add_argument('--maxEvents', type=int, help='Maximum number of events to process')
+  parser.add_argument('--loglevel', type=int, default=3, help='Verbosity level')
+  parser.add_argument('flags', nargs='*', help='Config flag overrides')
+  args = parser.parse_args()
+
+  # Setup the Run III behavior
+  from AthenaCommon.Configurable import Configurable
+  Configurable.configurableRun3Behavior = 1
+
+  # Set the Athena configuration flags
+  from AthenaConfiguration.AllConfigFlags import ConfigFlags
+  from AthenaConfiguration.AutoConfigFlags import GetFileMD
+
+  # Set the Athena configuration flags
+  ConfigFlags.Input.Files = ["root://eosatlas.cern.ch//eos/atlas/atlasdatadisk/rucio/data16_13TeV/8d/de/AOD.10654269._000566.pool.root.1"]
+  ConfigFlags.fillFromArgs(args.flags)
+  from PyUtils import AthFile
+  af = AthFile.fopen(ConfigFlags.Input.Files[0]) 
+  isMC = ('IS_SIMULATION' in af.fileinfos['evt_type'])
+  runNumber = af.fileinfos['run_number'][0]
+
+  ConfigFlags.Input.isMC = isMC
+  useBunchCrossingTool = (args.doRatesVsPositionInTrain or args.vetoStartOfTrain > 0)
+
+  ConfigFlags.lock()
+
+  # Initialize configuration object, add accumulator, merge, and run.
+  from AthenaConfiguration.MainServicesConfig import MainServicesSerialCfg 
+  from AthenaPoolCnvSvc.PoolReadConfig import PoolReadCfg
+  cfg = MainServicesSerialCfg()
+  cfg.merge(PoolReadCfg(ConfigFlags))
+
+  from GaudiSvc.GaudiSvcConf import THistSvc
+  histSvc = THistSvc()
+  histSvc.Output += ["RATESTREAM DATAFILE='" + args.outputHist + "' OPT='RECREATE'"]
+  cfg.addService(histSvc)
+
+  # Minimal config needed to read metadata: MetaDataSvc & ProxyProviderSvc
+  from AthenaServices.AthenaServicesConf import MetaDataSvc
+  mdSvc = MetaDataSvc( "MetaDataSvc" )
+  mdSvc.MetaDataContainer = "MetaDataHdr"
+  cfg.addService(mdSvc)
+  #
+  from SGComps.SGCompsConf import ProxyProviderSvc
+  pdps = ProxyProviderSvc( "ProxyProviderSvc" )
+  pdps.ProviderNames += [ "MetaDataSvc" ]
+  cfg.addService(pdps)
+
+  from TrigConfxAOD.TrigConfxAODConf import TrigConf__xAODConfigTool
+  trigcfgtool = TrigConf__xAODConfigTool('xAODConfigTool')
+  cfg.addPublicTool(trigcfgtool);
+
+  from TrigDecisionTool.TrigDecisionToolConf import Trig__TrigDecisionTool
+  from TrigEDMConfig.TriggerEDM import EDMLibraries
+  tdt = Trig__TrigDecisionTool('TrigDecisionTool')
+  tdt.ConfigTool = trigcfgtool
+  tdt.NavigationFormat = "TrigComposite"
+  tdt.Navigation.Dlls = [e for e in  EDMLibraries if 'TPCnv' not in e]
+  cfg.addPublicTool(tdt)
+
+  # If the dataset name is in the input files path, then it will be fetched from there
+  # Note to enable autolookup, first run "lsetup pyami; voms-proxy-init -voms atlas" and enter your grid pass phrase
+  xsec = args.MCCrossSection
+  fEff = args.MCFilterEfficiency
+  dset = args.MCDatasetName
+  if isMC and xsec == 0: # If the input file is MC then make sure we have the needed info
+    from .RatesGetCrossSectionMC import GetCrossSectionAMI
+    amiTool = GetCrossSectionAMI()
+    if dset == "": # Can we get the dataset name from the input file path?
+      dset = amiTool.getDatasetNameFromPath(ConfigFlags.Input.Files[0])
+    amiTool.queryAmi(dset)
+    xsec = amiTool.getCrossSection()
+    fEff = amiTool.getFilterEfficiency()
+
+  from EnhancedBiasWeighter.EnhancedBiasWeighterConf import EnhancedBiasWeighter
+  ebw = EnhancedBiasWeighter('EnhancedBiasRatesTool')
+  ebw.RunNumber = runNumber
+  ebw.UseBunchCrossingTool = useBunchCrossingTool
+  ebw.IsMC = isMC
+  # The following three are only needed if isMC == true
+  ebw.MCCrossSection = xsec
+  ebw.MCFilterEfficiency = fEff
+  ebw.MCKFactor = args.MCKFactor
+  cfg.addPublicTool(ebw)
+
+  from RatesAnalysis.RatesAnalysisConf import FullMenu
+  rates = FullMenu()
+  rates.PrescaleXML = args.inputPrescalesXML
+  rates.DoTriggerGroups = args.disableTriggerGroups
+  rates.DoGlobalGroups = args.disableGlobalGroups
+  rates.DoExpressRates = args.disableExpressGroup
+  rates.DoUniqueRates = args.disableUniqueRates
+  rates.DoHistograms = args.disableHistograms
+  rates.UseBunchCrossingTool = useBunchCrossingTool
+  rates.TargetLuminosity = args.targetLuminosity
+  rates.VetoStartOfTrain = args.vetoStartOfTrain
+  rates.EnableLumiExtrapolation = args.disableLumiExtrapolation
+  rates.EnhancedBiasRatesTool = ebw
+  rates.TrigDecisionTool = tdt
+  cfg.addEventAlgo(rates)
+
+  # Setup for accessing bunchgroup data from the DB
+  if useBunchCrossingTool:
+    from TrigBunchCrossingTool.BunchCrossingTool import BunchCrossingTool
+    if isMC:
+      cfg.addPublicTool(BunchCrossingTool("MC"))
+    else:
+      cfg.addPublicTool(BunchCrossingTool("LHC"))
+
+  from AthenaServices.AthenaServicesConf import AthenaEventLoopMgr
+  eventLoop = AthenaEventLoopMgr()
+  eventLoop.EventPrintoutInterval = 1000
+  cfg.addService(eventLoop)
+
+  # If you want to turn on more detailed messages ...
+  # exampleMonitorAcc.getEventAlgo('ExampleMonAlg').OutputLevel = 2 # DEBUG
+  cfg.printConfig(withDetails=False) # set True for exhaustive info
+
+  sc = cfg.run(args.maxEvents, args.loglevel)
+  sys.exit(0 if sc.isSuccess() else 1)
diff --git a/Trigger/TrigCost/RatesAnalysis/share/RatesAnalysis_test.ref b/Trigger/TrigCost/RatesAnalysis/share/RatesAnalysis_test.ref
index 2646787b78d08efbb77d77aaf386b410e6e26599..7e17f915e3d4ec03c757358a2462323213b8b79a 100644
--- a/Trigger/TrigCost/RatesAnalysis/share/RatesAnalysis_test.ref
+++ b/Trigger/TrigCost/RatesAnalysis/share/RatesAnalysis_test.ref
@@ -1,6 +1,6 @@
-Rate:      30050 +- 25495.1     Hz : TriggerA1 [PS:1] <- SeedA [PS:2]
-Rate:      15000 +- 12747.5     Hz : TriggerA2 [PS:2] <- SeedA [PS:2]
-Rate:    4591.67 +- 4187.46     Hz : TriggerB1 [PS:3] <- SeedB [PS:4]
-Rate:     3437.5 +- 3140.59     Hz : TriggerB2 [PS:4] <- SeedB [PS:4]
-RateOR:      30050 +- 25495.1     Hz,  RateAND:      15000 +- 12747.5     Hz : GroupA
-RateOR:    6883.33 +- 6281.18     Hz,  RateAND:    1145.83 +- 1046.86     Hz : GroupB
+Rate:       30050 +- 25495.1     Hz : TriggerA1 [PS:1] <- SeedA [PS:2] (Extrap:LINEAR_L)
+Rate:       15000 +- 12747.5     Hz : TriggerA2 [PS:2] <- SeedA [PS:2] (Extrap:LINEAR_L)
+Rate:     4591.67 +- 4187.46     Hz : TriggerB1 [PS:3] <- SeedB [PS:4] (Extrap:LINEAR_L)
+Rate:      3437.5 +- 3140.59     Hz : TriggerB2 [PS:4] <- SeedB [PS:4] (Extrap:LINEAR_L)
+RateOR:       30050 +- 25495.1     Hz,  RateAND:       15000 +- 12747.5     Hz : GroupA (Extrap:LINEAR_L)
+RateOR:     6883.33 +- 6281.18     Hz,  RateAND:     1145.83 +- 1046.86     Hz : GroupB (Extrap:LINEAR_L)
diff --git a/Trigger/TrigCost/RatesAnalysisExamplesXAOD/src/ExampleRatesFullMenu.cxx b/Trigger/TrigCost/RatesAnalysis/src/FullMenu.cxx
similarity index 67%
rename from Trigger/TrigCost/RatesAnalysisExamplesXAOD/src/ExampleRatesFullMenu.cxx
rename to Trigger/TrigCost/RatesAnalysis/src/FullMenu.cxx
index 6016638439e41d09b8b57050fbfba7af385e1957..cf17d47506a00d9d93f7ba8ba33277b0579cf4e2 100644
--- a/Trigger/TrigCost/RatesAnalysisExamplesXAOD/src/ExampleRatesFullMenu.cxx
+++ b/Trigger/TrigCost/RatesAnalysis/src/FullMenu.cxx
@@ -1,17 +1,16 @@
 /*
-  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
 */
 
-#include "ExampleRatesFullMenu.h"
+#include "FullMenu.h"
 
-ExampleRatesFullMenu::ExampleRatesFullMenu( const std::string& name, ISvcLocator* pSvcLocator ) : RatesAnalysisAlg(name, pSvcLocator) {
-  declareProperty( "TargetLuminosity", m_lumi = 1e34);
+FullMenu::FullMenu( const std::string& name, ISvcLocator* pSvcLocator ) : RatesAnalysisAlg(name, pSvcLocator) {
 }
 
-ExampleRatesFullMenu::~ExampleRatesFullMenu() {
+FullMenu::~FullMenu() {
 }
 
-StatusCode  ExampleRatesFullMenu::ratesInitialize() {
+StatusCode  FullMenu::ratesInitialize() {
   ATH_MSG_INFO("In ratesInitialize()");
 
   // Here we assume a full-ring, other functions are available to change this assumption.
@@ -31,13 +30,13 @@ StatusCode  ExampleRatesFullMenu::ratesInitialize() {
   return StatusCode::SUCCESS;
 }
 
-StatusCode  ExampleRatesFullMenu::ratesExecute() {
+StatusCode  FullMenu::ratesExecute() {
   // Triggers added are of type kEXISTING so here we rely on the Trigger Decision Tool for pass/fail. 
   // This is automatic so there is nothing to do here.
   return StatusCode::SUCCESS;
 }
 
-StatusCode  ExampleRatesFullMenu::ratesFinalize() {
+StatusCode  FullMenu::ratesFinalize() {
   ATH_MSG_INFO("In ratesFinalize()");
   return StatusCode::SUCCESS;
 }
diff --git a/Trigger/TrigCost/RatesAnalysis/src/FullMenu.h b/Trigger/TrigCost/RatesAnalysis/src/FullMenu.h
new file mode 100644
index 0000000000000000000000000000000000000000..718dde97f0ad8645afe9ec92ab8dad6544cb7725
--- /dev/null
+++ b/Trigger/TrigCost/RatesAnalysis/src/FullMenu.h
@@ -0,0 +1,25 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef RATESANALYSIS_FULLMENU_H
+#define RATESANALYSIS_FULLMENU_H 1
+
+#include "RatesAnalysis/RatesAnalysisAlg.h"
+
+class FullMenu: public ::RatesAnalysisAlg { 
+ public: 
+  FullMenu( const std::string& name, ISvcLocator* pSvcLocator );
+  virtual ~FullMenu(); 
+
+  virtual StatusCode  ratesInitialize() override;
+  virtual StatusCode  ratesExecute() override;
+  virtual StatusCode  ratesFinalize() override;
+
+ private:
+
+  Gaudi::Property<float> m_lumi{this, "TargetLuminosity", 2e34, "Targer inst. luminosity, assuming full ring."};
+
+}; 
+
+#endif //> !RATESANALYSIS_FULLMENU_H
diff --git a/Trigger/TrigCost/RatesAnalysis/src/RatesAnalysisAlg.cxx b/Trigger/TrigCost/RatesAnalysis/src/RatesAnalysisAlg.cxx
index 8eeff4c31927c8e7867f0a7ec999be859068b38b..b3be62eebdc93327a522086f530926b81265e080 100644
--- a/Trigger/TrigCost/RatesAnalysis/src/RatesAnalysisAlg.cxx
+++ b/Trigger/TrigCost/RatesAnalysis/src/RatesAnalysisAlg.cxx
@@ -16,8 +16,6 @@
 
 RatesAnalysisAlg::RatesAnalysisAlg( const std::string& name, ISvcLocator* pSvcLocator ) : 
   AthAnalysisAlgorithm( name, pSvcLocator ),
-  m_enhancedBiasRatesTool("EnhancedBiasWeighter/EnhancedBiasRatesTool"),
-  m_tdt("Trig::TrigDecisionTool/TrigDecisionTool"),
   m_targetMu(0.),
   m_targetBunches(0.),
   m_targetLumi(0.),
@@ -26,26 +24,7 @@ RatesAnalysisAlg::RatesAnalysisAlg( const std::string& name, ISvcLocator* pSvcLo
   m_weightedEventCounter(0),
   m_scalingHist(nullptr),
   m_bcidHist(nullptr)
-{
-  declareProperty("DoUniqueRates", m_doUniqueRates = true, "Calculate unique rates for all chains (slow)");
-  declareProperty("DoHistograms", m_doHistograms = true, "Switch on histogram output of rate vs. mu and position in train.");
-  declareProperty("UseBunchCrossingTool", m_useBunchCrossingTool = true, "BunchCrossing tool requires CONDBR2 access. Can be disabled here if this is a problem.");
-  declareProperty("PrescaleXML", m_prescaleXML = "", "Optional XML of prescales from the TrigRuleBook to apply.");
-  declareProperty("NormaliseHistograms", m_normaliseHistograms = true, "Set this to false if you are going to be merging outputs.");
-  declareProperty("EnableLumiExtrapolation", m_enableLumiExtrapolation = true, "If false then no extrapolation in L, N_bunch or <mu> will be performed..");
-  declareProperty("VetoStartOfTrain", m_vetoStartOfTrain = 0, "How many BCID to veto at the start of a bunch train.");
-
-  declareProperty("ExpoScalingFactor", m_expoScalingFactor = 0.1, "Optional. Exponential factor if using exponential-mu rates scaling.");
-  
-  declareProperty("IsMC", m_isMC = false, "MC mode? If so we need a cross section and filter efficiency");
-  declareProperty("MCCrossSection", m_mcCrossSection = 0, "If running over MC. The process cross section in nb. Required.");
-  declareProperty("MCFilterEfficiency", m_mcFilterEfficiency = 0, "If running over MC. The process filter efficiency (0.0-1.0). Required.");
-  declareProperty("MCKFactor", m_mcKFactor = 1., "If running over MC. Higher-order corrections fudge factor to the cross section. Optional.");
-  declareProperty("InelasticCrossSection", m_inelasticCrossSection = 8e-26, "Inelastic cross section in units cm^2. Default 80 mb at 13 TeV.");
-
-  declareProperty( "TrigDecisionTool", m_tdt, "The tool to access TrigDecision" );
-  declareProperty( "EnhancedBiasRatesTool", m_enhancedBiasRatesTool, "The tool to access enhanced bias weighting data" );
-}
+{}
 
 RatesAnalysisAlg::~RatesAnalysisAlg() {}
 
@@ -66,8 +45,8 @@ StatusCode RatesAnalysisAlg::newScanTrigger(const std::string& name,
   }
 
   const ExtrapStrat_t e = (m_enableLumiExtrapolation ? extrapolation : ExtrapStrat_t::kNONE); 
-  m_scanTriggers.emplace(std::piecewise_construct, std::forward_as_tuple(name), std::forward_as_tuple(name, msg(), thresholdMin, thresholdMax, thresholdBins, behaviour, prescale, seedName, seedPrecale, e));
-  RatesScanTrigger* newScanTrigger = &(m_scanTriggers.at(name));
+  m_scanTriggers.emplace(name, std::make_unique<RatesScanTrigger>(name, msg(), thresholdMin, thresholdMax, thresholdBins, behaviour, prescale, seedName, seedPrecale, e));
+  RatesScanTrigger* newScanTrigger = m_scanTriggers.at(name).get();
   if (isRandomSeed(name, seedName)) newScanTrigger->setSeedsFromRandom(true);
   ATH_MSG_DEBUG("newScanTrigger " <<  name << " added");
   return StatusCode::SUCCESS;
@@ -90,8 +69,8 @@ StatusCode RatesAnalysisAlg::newScanTrigger(const std::string& name,
 
   const ExtrapStrat_t e = (m_enableLumiExtrapolation ? extrapolation : ExtrapStrat_t::kNONE); 
 
-  m_scanTriggers.emplace(std::piecewise_construct, std::forward_as_tuple(name), std::forward_as_tuple(name, msg(), thresholdBinEdges, behaviour, prescale, seedName, seedPrecale, e));
-  RatesScanTrigger* newScanTrigger = &(m_scanTriggers.at(name));
+  m_scanTriggers.emplace(name, std::make_unique<RatesScanTrigger>(name, msg(), thresholdBinEdges, behaviour, prescale, seedName, seedPrecale, e));
+  RatesScanTrigger* newScanTrigger = m_scanTriggers.at(name).get();
   if (isRandomSeed(name, seedName)) newScanTrigger->setSeedsFromRandom(true);
   ATH_MSG_DEBUG("newScanTrigger " <<  name << " added");
   return StatusCode::SUCCESS;
@@ -142,8 +121,8 @@ StatusCode RatesAnalysisAlg::newTrigger(const std::string& name,
 
   const ExtrapStrat_t e = (m_enableLumiExtrapolation ? extrapolation : ExtrapStrat_t::kNONE); 
 
-  m_triggers.emplace(std::piecewise_construct, std::forward_as_tuple(name), std::forward_as_tuple(name, msg(), prescale, expressPrescale, seedName, seedPrecale, m_doHistograms, e));
-  RatesTrigger* newTriggerPtr = &(m_triggers.at(name));
+  m_triggers.emplace(name, std::make_unique<RatesTrigger>(name, msg(), prescale, expressPrescale, seedName, seedPrecale, m_doHistograms, e));
+  RatesTrigger* newTriggerPtr = m_triggers.at(name).get();
 
   if (isRandomSeed(name, seedName)) newTriggerPtr->setSeedsFromRandom(true);
 
@@ -160,32 +139,36 @@ StatusCode RatesAnalysisAlg::newTrigger(const std::string& name,
   }
 
   // Add this trigger to its groups
-  for (const std::string& group : groups) {
-    if (m_groups.count(group) == 0) {
-      m_groups.emplace(std::piecewise_construct, std::forward_as_tuple(group), std::forward_as_tuple(group, msg(), m_doHistograms, m_enableLumiExtrapolation));
-      // As the group is formed from at least one active trigger - it must be active itself (counter example - CPS group of a PS=-1 trigger)
-      m_activeGroups.insert( &(m_groups.at(group)) );
-    }
-    m_groups.at(group).addToGroup( newTriggerPtr );
-    // For CPS, we let the trigger know that it is special
-    if (isCPS(group)) {
-      if (newTriggerPtr->getCPSID() != 0) ATH_MSG_WARNING("Trigger " << name << " can only be in one coherent prescale group.");
-      newTriggerPtr->setCPS(group); // This changes the CPSID
-      const size_t CPSID = newTriggerPtr->getCPSID();
-      // Find the lowest prescale of any member in this CPS group
-      if (m_lowestPrescale.count(CPSID) == 0) m_lowestPrescale[CPSID] = FLT_MAX;
-      if (prescale < m_lowestPrescale[CPSID]) m_lowestPrescale[CPSID] = prescale;
+  if (m_doTriggerGroups) {
+    for (const std::string& group : groups) {
+      if (m_groups.count(group) == 0) {
+        m_groups.emplace(group, std::make_unique<RatesGroup>(group, msg(), m_doHistograms, m_enableLumiExtrapolation));
+        // As the group is formed from at least one active trigger - it must be active itself (counter example - CPS group of a PS=-1 trigger)
+        m_activeGroups.insert( m_groups.at(group).get() );
+      }
+      m_groups.at(group)->addToGroup( newTriggerPtr );
+      // For CPS, we let the trigger know that it is special
+      if (isCPS(group)) {
+        if (newTriggerPtr->getCPSID() != 0) ATH_MSG_WARNING("Trigger " << name << " can only be in one coherent prescale group.");
+        newTriggerPtr->setCPS(group); // This changes the CPSID
+        const size_t CPSID = newTriggerPtr->getCPSID();
+        // Find the lowest prescale of any member in this CPS group
+        if (m_lowestPrescale.count(CPSID) == 0) m_lowestPrescale[CPSID] = FLT_MAX;
+        if (prescale < m_lowestPrescale[CPSID]) m_lowestPrescale[CPSID] = prescale;
+      }
     }
   }
 
   // Add to total rates
   const uint32_t level = getLevel(name);
-  if      (level == 2) m_globalGroups.at(m_l2GroupName).addToGroup( newTriggerPtr );
-  else if (level == 1) m_globalGroups.at(m_l1GroupName).addToGroup( newTriggerPtr );
+  if (m_doGlobalGroups) {
+    if      (level == 2) m_globalGroups.at(m_l2GroupName)->addToGroup( newTriggerPtr );
+    else if (level == 1) m_globalGroups.at(m_l1GroupName)->addToGroup( newTriggerPtr );
+  }
   // Add to express group - if express prescale is enabled
   if (level == 2 && expressPrescale >= 1) {
-    m_globalGroups.at(m_expressGroupName).addToGroup( newTriggerPtr );
-    m_expressTriggers.insert( newTriggerPtr );
+    if (m_doGlobalGroups) m_globalGroups.at(m_expressGroupName)->addToGroup( newTriggerPtr );
+    if (m_doExpressRates) m_expressTriggers.insert( newTriggerPtr );
   }
 
   ATH_MSG_DEBUG("newTrigger " <<  name << " added");
@@ -275,11 +258,6 @@ StatusCode RatesAnalysisAlg::addExisting(const std::string pattern) {
 }
 
 StatusCode RatesAnalysisAlg::checkGotTDT() {
-  // if (!m_tdt.isSet()) { // We used to have to actually fetch the TDT here. Leaving this in should the behaviour change, we can at least confirm the TDT is configured here.
-    // ATH_MSG_INFO("Setting up TDT");
-    // ATH_CHECK(m_tdt.retrieve());
-    // m_tdt->ExperimentalAndExpertMethods()->enable();
-  // }
   static bool printed = false;
   if (!printed) ATH_MSG_INFO("TDT contains: " << m_tdt->getListOfTriggers().size() << " triggers, " 
     << m_tdt->getListOfStreams().size() << " streams and " 
@@ -315,8 +293,8 @@ StatusCode RatesAnalysisAlg::setTriggerDesicison(const std::string& name, const
       ATH_MSG_ERROR("Cannot find trigger " << name << " did you call newTrigger for this in initialize?");
       return StatusCode::FAILURE;
     }
-    iterator->second.setPassedAndExecute(triggerIsPassed, m_weightingValues); // There is logic in the RatesTrigger to prevent multiple calls per event by accident.
-    m_activatedTriggers.insert( &(iterator->second) );
+    iterator->second->setPassedAndExecute(triggerIsPassed, m_weightingValues); // There is logic in the RatesTrigger to prevent multiple calls per event by accident.
+    m_activatedTriggers.insert( iterator->second.get() );
   }
   return StatusCode::SUCCESS;
 }
@@ -328,67 +306,22 @@ StatusCode RatesAnalysisAlg::setTriggerDesicison(const std::string& name, const
     ATH_MSG_ERROR("Cannot find scan-trigger " << name << " did you call newScanTrigger for this in initialize?");
     return StatusCode::FAILURE;
   }
-  iterator->second.setPassedAndExecute(threshold, m_weightingValues); // There is logic in the RatesScanTrigger to prevent multiple calls per event by accident.
-  m_activatedTriggers.insert( static_cast<RatesTrigger*>( &(iterator->second) ) );
+  iterator->second->setPassedAndExecute(threshold, m_weightingValues); // There is logic in the RatesScanTrigger to prevent multiple calls per event by accident.
+  m_activatedTriggers.insert( static_cast<RatesTrigger*>( iterator->second.get() ) );
   return StatusCode::SUCCESS;
 }
 
 StatusCode RatesAnalysisAlg::initialize() {
   ATH_MSG_INFO ("Initializing " << name() << "...");
 
-  m_globalGroups.emplace(std::piecewise_construct, std::forward_as_tuple(m_l1GroupName),      std::forward_as_tuple(m_l1GroupName, msg(),      m_doHistograms, m_enableLumiExtrapolation));
-  m_globalGroups.emplace(std::piecewise_construct, std::forward_as_tuple(m_l2GroupName),      std::forward_as_tuple(m_l2GroupName, msg(),      m_doHistograms, m_enableLumiExtrapolation));
-  m_globalGroups.emplace(std::piecewise_construct, std::forward_as_tuple(m_expressGroupName), std::forward_as_tuple(m_expressGroupName, msg(), m_doHistograms, m_enableLumiExtrapolation));
-  m_globalGroups.at(m_l2GroupName).setDoCachedWeights( m_doUniqueRates ); // This extra sub-weight caching is only utilised by unique-rate groups 
-  m_globalGroups.at(m_expressGroupName).setExpressGroup( true );
-  return StatusCode::SUCCESS;
-}
-
-StatusCode RatesAnalysisAlg::beginInputFile() { 
-  ATH_MSG_INFO ("beginInputFile " << name() << "...");
-
-  if (m_enhancedBiasRatesTool.isSet() == false) {
-    ATH_MSG_INFO("Setting up EnhancedBiasRatesTool");
-
-    const EventStreamInfo* esi = 0;
-    ATH_CHECK( inputMetaStore()->retrieve(esi) );
+  ATH_CHECK( m_tdt.retrieve() );
+  m_tdt->ExperimentalAndExpertMethods()->enable();
 
-    if ( !m_isMC && esi->getRunNumbers().size() > 1 ) {
-      ATH_MSG_FATAL("File with multiple run numbers - please only use a single EnhancedBias run.");
-      return StatusCode::FAILURE;
-    }
-
-    const uint32_t runNum = (m_isMC ? esi->getEventTypes().begin()->mc_channel_number() : *(esi->getRunNumbers().begin()));
-
-    ATH_CHECK( AAH::setProperty( m_enhancedBiasRatesTool, "RunNumber", runNum ) );
-    ATH_CHECK( AAH::setProperty( m_enhancedBiasRatesTool, "UseBunchCrossingTool", m_useBunchCrossingTool ) );
-    ATH_CHECK( AAH::setProperty( m_enhancedBiasRatesTool, "IsMC", m_isMC ) );
-    ATH_CHECK( AAH::setProperty( m_enhancedBiasRatesTool, "InelasticCrossSection", m_inelasticCrossSection ) );
-    if (m_isMC) {
-      if (m_mcCrossSection == 0 && m_mcFilterEfficiency == 0) {
-        ATH_MSG_FATAL("For MC rates, a cross section and filter efficiency must be supplied.");
-        return StatusCode::FAILURE;
-      }
-      ATH_CHECK( AAH::setProperty( m_enhancedBiasRatesTool, "MCCrossSection", m_mcCrossSection ) );
-      ATH_CHECK( AAH::setProperty( m_enhancedBiasRatesTool, "MCFilterEfficiency", m_mcFilterEfficiency ) );
-      ATH_CHECK( AAH::setProperty( m_enhancedBiasRatesTool, "MCKFactor", m_mcKFactor ) );
-    }
-    ATH_CHECK( m_enhancedBiasRatesTool.retrieve() ); 
-
-    if (m_prescaleXML != "") {
-      m_loadedXML = m_enhancedBiasRatesTool->parsePrescaleXML( m_prescaleXML );
-      if (m_loadedXML.size() == 0) {
-        ATH_MSG_ERROR("Reading and parsing of " << m_prescaleXML << " failed.");
-        return StatusCode::FAILURE;
-      }
-      ATH_MSG_INFO("Parsed " << m_loadedXML.size() << " triggers from prescale XML.");
-    }
-  }
+  ATH_CHECK( m_enhancedBiasRatesTool.retrieve() ); 
 
-  if (m_tdt.isSet() == false) {
-    ATH_MSG_INFO("Setting up TDT in initialize()");
-    ATH_CHECK(m_tdt.retrieve());
-    m_tdt->ExperimentalAndExpertMethods()->enable();
+  if (m_doUniqueRates && !m_doGlobalGroups) {
+    ATH_MSG_ERROR("DoUniqueRates=True requires DoGlobalGroups=True");
+    return StatusCode::FAILURE;
   }
 
   return StatusCode::SUCCESS;
@@ -397,24 +330,37 @@ StatusCode RatesAnalysisAlg::beginInputFile() {
 StatusCode RatesAnalysisAlg::populateTriggers() {  
   // Let user add their triggers
   ATH_MSG_INFO("Initializing User's Triggers (note: we are actually now in the event loop)");
+
+  if (m_doGlobalGroups) {
+    m_globalGroups.emplace(m_l1GroupName,      std::make_unique<RatesGroup>(m_l1GroupName, msg(),      m_doHistograms, m_enableLumiExtrapolation));
+    m_globalGroups.emplace(m_l2GroupName,      std::make_unique<RatesGroup>(m_l2GroupName, msg(),      m_doHistograms, m_enableLumiExtrapolation));
+    m_globalGroups.at(m_l2GroupName)->setDoCachedWeights( m_doUniqueRates ); // This extra sub-weight caching is only utilised by unique-rate groups
+    if (m_doExpressRates) {
+      m_globalGroups.emplace(m_expressGroupName, std::make_unique<RatesGroup>(m_expressGroupName, msg(), m_doHistograms, m_enableLumiExtrapolation));
+      m_globalGroups.at(m_expressGroupName)->setExpressGroup( true );
+    }
+  }
+
+  // This runs the derived class's code to add whatever triggers are desired.
+  // Should be calling newTrigger(...), newScanTrigger(...) or addExisting(...), addAllExisting().
   ATH_CHECK( ratesInitialize() ); 
 
   ATH_MSG_INFO("Computing coherent factors for coherent prescale groups.");
   // Now we are not going to get any more chains - we can fill in the coherent prescale factors
-  for (auto& iterator : m_triggers) {
-    const size_t CPSID = iterator.second.getCPSID(); 
-    if (CPSID != 0) iterator.second.setCoherentFactor( m_lowestPrescale.at(CPSID) );
+  for (const auto& trigger : m_triggers) {
+    const size_t CPSID = trigger.second->getCPSID(); 
+    if (CPSID != 0) trigger.second->setCoherentFactor( m_lowestPrescale.at(CPSID) );
   }
 
-  if (m_doUniqueRates) {
+  if (m_doUniqueRates && m_doGlobalGroups) {
     ATH_MSG_INFO("Creating extra groups to calculate unique rates.");
-    const RatesGroup* l2GroupPtr = &(m_globalGroups.at(m_l2GroupName)); // The finalised list of all HLT chains
-    const RatesGroup* l1GroupPtr = &(m_globalGroups.at(m_l1GroupName)); // The finalised list of all L1 chains
+    const RatesGroup* l2GroupPtr = m_globalGroups.at(m_l2GroupName).get(); // The finalised list of all HLT chains
+    const RatesGroup* l1GroupPtr = m_globalGroups.at(m_l1GroupName).get(); // The finalised list of all L1 chains
     for (const auto& trigger : m_triggers) {
       const uint32_t level = getLevel(trigger.first);
-      m_uniqueGroups.emplace(std::piecewise_construct, std::forward_as_tuple(trigger.first), std::forward_as_tuple(trigger.first, msg(), false, m_enableLumiExtrapolation)); // Each trigger gets its own unique group. No hist needed
-      RatesTrigger* triggerPtr = &(m_triggers.at(trigger.first));
-      RatesGroup* uniqueGroupPtr = &(m_uniqueGroups.at(trigger.first));
+      m_uniqueGroups.emplace(trigger.first, std::make_unique<RatesGroup>(trigger.first, msg(), false, m_enableLumiExtrapolation)); // Each trigger gets its own unique group. No hist needed
+      RatesTrigger* triggerPtr = m_triggers.at(trigger.first).get();
+      RatesGroup* uniqueGroupPtr = m_uniqueGroups.at(trigger.first).get();
       triggerPtr->setUniqueGroup( uniqueGroupPtr ); // Create two-way links
       uniqueGroupPtr->setUniqueTrigger( triggerPtr ); // Create two-way links
       // Copy in the global rates topology and make note of the unique rates master group
@@ -432,46 +378,73 @@ StatusCode RatesAnalysisAlg::populateTriggers() {
         uniqueGroupPtr->setUseCachedWeights(true);
       }
       // Efficiency - if the trigger is disabled, no need to actually calculate anything for it.
-      if (trigger.second.getDisabled() == false) {
+      if (trigger.second->getDisabled() == false) {
         m_activeGroups.insert( uniqueGroupPtr ); // Add this to the event loop
       }
     }
   }
 
   // Print all triggers
-  ATH_MSG_DEBUG("################## Configured to estimate rates for the following triggers:");
-  for (const auto& trigger : m_triggers) ATH_MSG_DEBUG(trigger.second.printConfig());
-  ATH_MSG_DEBUG("################## Configured to estimate rates for the following triggers:");
-  for (const auto& trigger : m_triggers) ATH_MSG_DEBUG(trigger.second.printConfig());
-  ATH_MSG_DEBUG("################## Configured to estimate rates for the following groups of triggers:");
-  for (const auto& group : m_groups) ATH_MSG_DEBUG(group.second.printConfig());
+  if (msgLevel(MSG::DEBUG)) {
+    if (m_triggers.size()) {
+      ATH_MSG_DEBUG("################## Configured to estimate rates for the following triggers:");
+      for (const auto& trigger : m_triggers) ATH_MSG_DEBUG(trigger.second->printConfig());
+    }
+    if (m_scanTriggers.size()) {
+      ATH_MSG_DEBUG("################## Configured to estimate rates for the following scan triggers:");
+      for (const auto& trigger : m_scanTriggers) ATH_MSG_DEBUG(trigger.second->printConfig());
+    }
+    if (m_groups.size()) {
+      ATH_MSG_DEBUG("################## Configured to estimate rates for the following groups of triggers:");
+      for (const auto& group : m_groups) ATH_MSG_DEBUG(group.second->printConfig());
+    }
+    if (m_globalGroups.size()) {
+      ATH_MSG_DEBUG("################## Configured to estimate rates for the following global groups of triggers:");
+      for (const auto& group : m_globalGroups) ATH_MSG_DEBUG(group.second->printConfig());
+    }
+  }
 
   if (m_doHistograms) {
-    ServiceHandle<ITHistSvc> histSvc("THistSvc", name());
+    ATH_MSG_DEBUG("################## Registering normalisation histogram:");
     m_scalingHist = new TH1D("normalisation","normalisation;;sample walltime [s]",1,0.,1.);
-    ATH_CHECK( histSvc->regHist(std::string("/RATESTREAM/normalisation"), m_scalingHist) );
+    ATH_CHECK( histSvc()->regHist(std::string("/RATESTREAM/normalisation"), m_scalingHist) );
     m_bcidHist = new TH1D("bcid",";BCID;Events",3565,-.5,3564.5);
-    ATH_CHECK( histSvc->regHist(std::string("/RATESTREAM/bcid"), m_bcidHist) );
-    for (auto& trigger : m_triggers) {
-      if (!trigger.second.doHistograms()) continue; // Not all may be doing histograming
-      ATH_CHECK( histSvc->regHist(std::string("/RATESTREAM/Triggers/" + trigger.first + "/data"), trigger.second.getDataHist(true)) );
-      ATH_CHECK( histSvc->regHist(std::string("/RATESTREAM/Triggers/" + trigger.first + "/rateVsMu"), trigger.second.getMuHist(true)) );
-      if (m_useBunchCrossingTool) ATH_CHECK( histSvc->regHist(std::string("/RATESTREAM/Triggers/" + trigger.first + "/rateVsTrain"), trigger.second.getTrainHist(true)) );
+    ATH_CHECK( histSvc()->regHist(std::string("/RATESTREAM/bcid"), m_bcidHist) );
+    if (m_triggers.size()) {
+    ATH_MSG_DEBUG("################## Registering trigger histograms:");
+      for (const auto& trigger : m_triggers) {
+        if (!trigger.second->doHistograms()) continue; // Not all may be doing histograming
+        ATH_CHECK( trigger.second->giveDataHist(histSvc(), std::string("/RATESTREAM/Triggers/" + trigger.first + "/data")) );
+        ATH_CHECK( trigger.second->giveMuHist(histSvc(), std::string("/RATESTREAM/Triggers/" + trigger.first + "/rateVsMu")) );
+        if (m_useBunchCrossingTool) ATH_CHECK( trigger.second->giveTrainHist(histSvc(), std::string("/RATESTREAM/Triggers/" + trigger.first + "/rateVsTrain")) );
+        else trigger.second->clearTrainHist();
+      }
     }
-    for (auto& trigger : m_scanTriggers) {
-      ATH_CHECK( histSvc->regHist(std::string("/RATESTREAM/ScanTriggers/" + trigger.first + "/rateVsThreshold"), trigger.second.getThresholdHist(true)) );
+    if (m_scanTriggers.size()) {
+    ATH_MSG_DEBUG("################## Registering scan trigger histograms:");
+      for (const auto& trigger : m_scanTriggers) {
+        ATH_CHECK( trigger.second->giveThresholdHist(histSvc(), std::string("/RATESTREAM/ScanTriggers/" + trigger.first + "/rateVsThreshold")) );
+      }
     }
-    for (auto& group : m_groups) {
-      if (!group.second.doHistograms()) continue;
-      ATH_CHECK( histSvc->regHist(std::string("/RATESTREAM/Groups/" + group.first + "/data"),     group.second.getDataHist(true)) );
-      ATH_CHECK( histSvc->regHist(std::string("/RATESTREAM/Groups/" + group.first + "/rateVsMu"), group.second.getMuHist(true)) );
-      if (m_useBunchCrossingTool) ATH_CHECK( histSvc->regHist(std::string("/RATESTREAM/Groups/" + group.first + "/rateVsTrain"), group.second.getTrainHist(true)) );
+    if (m_groups.size()) {
+      ATH_MSG_DEBUG("################## Registering group histograms:");
+      for (const auto& group : m_groups) {
+        if (!group.second->doHistograms()) continue;
+        ATH_CHECK( group.second->giveDataHist(histSvc(), std::string("/RATESTREAM/Groups/" + group.first + "/data")) );
+        ATH_CHECK( group.second->giveMuHist(histSvc(), std::string("/RATESTREAM/Groups/" + group.first + "/rateVsMu")) );
+        if (m_useBunchCrossingTool) ATH_CHECK( group.second->giveTrainHist(histSvc(), std::string("/RATESTREAM/Groups/" + group.first + "/rateVsTrain")) );
+        else group.second->clearTrainHist();
+      }
     }
-    for (auto& group : m_globalGroups) {
-      if (!group.second.doHistograms()) continue;
-      ATH_CHECK( histSvc->regHist(std::string("/RATESTREAM/Globals/" + group.first + "/data"), group.second.getDataHist(true)) );
-      ATH_CHECK( histSvc->regHist(std::string("/RATESTREAM/Globals/" + group.first + "/rateVsMu"), group.second.getMuHist(true)) );
-      if (m_useBunchCrossingTool) ATH_CHECK( histSvc->regHist(std::string("/RATESTREAM/Globals/" + group.first + "/rateVsTrain"), group.second.getTrainHist(true)) );
+    if (m_globalGroups.size()) {
+      ATH_MSG_DEBUG("################## Registering global group histograms:");
+      for (const auto& group : m_globalGroups) {
+        if (!group.second->doHistograms()) continue;
+        ATH_CHECK( group.second->giveDataHist(histSvc(), std::string("/RATESTREAM/Globals/" + group.first + "/data")) );
+        ATH_CHECK( group.second->giveMuHist(histSvc(), std::string("/RATESTREAM/Globals/" + group.first + "/rateVsMu")) );
+        if (m_useBunchCrossingTool) ATH_CHECK( group.second->giveTrainHist(histSvc(), std::string("/RATESTREAM/Globals/" + group.first + "/rateVsTrain")) );
+        else group.second->clearTrainHist();
+      }
     }
   }
 
@@ -506,7 +479,7 @@ StatusCode RatesAnalysisAlg::execute() {
 
   if (m_useBunchCrossingTool && m_vetoStartOfTrain > 0 && m_weightingValues.m_distanceInTrain < m_vetoStartOfTrain) return StatusCode::SUCCESS;
 
-  // Bunch factor doesn't change as a fn. of the run.
+  // Bunch factor doesn't change as a fn. of the run. Reminder: m_bunchFactor = m_targetBunches / (double)ebPairedBunches;
   m_weightingValues.m_muFactor = m_targetMu / m_weightingValues.m_eventMu;
   m_weightingValues.m_linearLumiFactor = m_targetLumi / m_weightingValues.m_eventLumi;
   m_weightingValues.m_expoMuFactor = m_weightingValues.m_bunchFactor * exp( m_expoScalingFactor * (m_targetMu - m_weightingValues.m_eventMu) );
@@ -524,18 +497,21 @@ StatusCode RatesAnalysisAlg::execute() {
   ATH_CHECK( ratesExecute() );
 
   // Execute groups
-  for (auto& group : m_globalGroups) group.second.execute(m_weightingValues); // Physics, L1, express: Must execute before m_uniqueGroups (which are in active groups). Map.
-  for (auto& group : m_activeGroups) group->execute(m_weightingValues); // Individual groups, CPS groups and active unique groups. Set.
+  for (const auto& group : m_globalGroups) group.second->execute(m_weightingValues); // Physics, L1, express: Must execute before m_uniqueGroups (which are in active groups). Map.
+  for (const auto& group : m_activeGroups) group->execute(m_weightingValues); // Individual groups, CPS groups and active unique groups. Set.
 
   // Reset triggers
-  for (auto& trigger : m_activatedTriggers) trigger->reset();
+  for (const auto& trigger : m_activatedTriggers) trigger->reset();
   m_activatedTriggers.clear();
 
   // Keep track of elapsed walltime
   m_ratesDenominator += m_weightingValues.m_eventLiveTime;
   m_weightedEventCounter += m_weightingValues.m_enhancedBiasWeight;
 
-  if (m_doHistograms) m_bcidHist->Fill(eventInfo->bcid(), m_weightingValues.m_enhancedBiasWeight);
+  if (m_doHistograms) {
+    m_bcidHist->Fill(eventInfo->bcid(), m_weightingValues.m_enhancedBiasWeight);
+    m_scalingHist->Fill(m_weightingValues.m_eventLiveTime);
+  }
 
   // Some debug info
   if (++m_eventCounter % 1000 == 0) {
@@ -563,16 +539,17 @@ StatusCode RatesAnalysisAlg::executeTriggerEmulation() {
 
 StatusCode RatesAnalysisAlg::finalize() {
   ATH_MSG_INFO ("Finalizing " << name() << "...");
+
   ATH_CHECK( ratesFinalize() );
   if (m_scanTriggers.size()) {
     ATH_MSG_INFO("################## Computed Rate Scans for Threshold-Scan Items:");
-    for (const auto& trigger : m_scanTriggers) ATH_MSG_INFO(trigger.second.printRate(m_ratesDenominator));
+    for (const auto& trigger : m_scanTriggers) ATH_MSG_INFO(trigger.second->printRate(m_ratesDenominator));
   }
   if (m_triggers.size()) {
     ATH_MSG_INFO("################## Computed Rate Estimations for Single Items:");
     std::set<std::string> keys; // Used an unordered map for speed, but now we'd like the items in order
     for (const auto& trigger : m_triggers) keys.insert(trigger.first);
-    for (const std::string& key : keys) ATH_MSG_INFO(m_triggers.at(key).printRate(m_ratesDenominator));
+    for (const std::string& key : keys) ATH_MSG_INFO(m_triggers.at(key)->printRate(m_ratesDenominator));
   }
   if (m_expressTriggers.size()) {
     ATH_MSG_INFO("################## Computed Express Rate Estimations for Single Items:");
@@ -580,11 +557,11 @@ StatusCode RatesAnalysisAlg::finalize() {
   }
   if (m_groups.size()) {
     ATH_MSG_INFO("################## Computed Rate Estimations for Groups:");
-    for (const auto& group : m_groups) ATH_MSG_INFO(group.second.printRate(m_ratesDenominator));
+    for (const auto& group : m_groups) ATH_MSG_INFO(group.second->printRate(m_ratesDenominator));
   }
   if (m_globalGroups.size()) {
     ATH_MSG_INFO("################## Computed Rate Estimations for Global Groups:");
-    for (const auto& group : m_globalGroups) ATH_MSG_INFO(group.second.printRate(m_ratesDenominator));
+    for (const auto& group : m_globalGroups) ATH_MSG_INFO(group.second->printRate(m_ratesDenominator));
   }
   ATH_MSG_INFO("################## LHC Conditions and weighting information:");
   printInputSummary();
@@ -592,20 +569,6 @@ StatusCode RatesAnalysisAlg::finalize() {
   printStatistics();
   ATH_MSG_INFO("##################");
 
-  if (m_doHistograms && m_scalingHist != nullptr) {
-    m_scalingHist->SetBinContent(1, m_ratesDenominator);
-    m_scalingHist->SetBinError(1, 0.);
-  }
-  if (m_doHistograms && m_normaliseHistograms) {
-    for (auto& trigger : m_triggers)        trigger.second.normaliseHist(m_ratesDenominator);
-    for (auto& trigger : m_scanTriggers)    trigger.second.normaliseHist(m_ratesDenominator);
-    // for (auto& trigger : m_expressTriggers) // Express triggers don't do histograms at the moment
-    for (auto& group   : m_groups)          group.second.normaliseHist(m_ratesDenominator);
-    for (auto& group   : m_globalGroups)    group.second.normaliseHist(m_ratesDenominator);
-  }
-
-  //if (m_tdt.isSet()) ATH_CHECK(m_tdt->finalize()); // Has issues
-  ATH_CHECK(m_enhancedBiasRatesTool->finalize());
   return StatusCode::SUCCESS;
 }
 
@@ -680,7 +643,7 @@ void RatesAnalysisAlg::printStatistics() const {
 }
 
 void RatesAnalysisAlg::printInputSummary() const {
-  ATH_MSG_INFO("Input " << (m_isMC ? "MC" : "EB Data") 
+  ATH_MSG_INFO("Input " << (m_enhancedBiasRatesTool->isMC() ? "MC" : "EB Data") 
     << " with <L_inst.> = "
     << m_enhancedBiasRatesTool->getAverageLumi() 
     << " cm-2s-1, <mu> = "
diff --git a/Trigger/TrigCost/RatesAnalysis/src/RatesGroup.cxx b/Trigger/TrigCost/RatesAnalysis/src/RatesGroup.cxx
index b7a8ebd3c68ef358bb7aeb5270489693122a4e46..6ff11843b9517854f9dfdd25a61db7c183f6d6c4 100644
--- a/Trigger/TrigCost/RatesAnalysis/src/RatesGroup.cxx
+++ b/Trigger/TrigCost/RatesAnalysis/src/RatesGroup.cxx
@@ -24,7 +24,7 @@ RatesGroup::RatesGroup(const std::string& name, const MsgStream& log, const bool
   m_doCachedWeights(false),
   m_cachedWeights(),
   m_useCachedWeights(false),
-  m_doLumiExtrapolation(doExtrapolation),
+  m_extrapolationStrategy(doExtrapolation ? ExtrapStrat_t::kLINEAR : ExtrapStrat_t::kNONE),
   m_masterGroup(nullptr),
   m_uniqueTrigger(nullptr),
   m_isExpressGroup(false),
@@ -47,11 +47,12 @@ const std::string RatesGroup::printConfig() const {
 
 const std::string RatesGroup::printRate(const double ratesDenominator) const {
   std::stringstream ss;
-  ss <<"RateOR:" << std::setw(11) << std::right << m_rateAccumulatorOR/ratesDenominator 
+  ss <<"RateOR: " << std::setw(11) << std::right << m_rateAccumulatorOR/ratesDenominator 
      << " +- " << std::setw(11) << std::left << sqrt(m_rateAccumulatorOR2)/ratesDenominator << " Hz, "
-     << " RateAND:" << std::setw(11) << std::right << m_rateAccumulatorAND/ratesDenominator 
+     << " RateAND: " << std::setw(11) << std::right << m_rateAccumulatorAND/ratesDenominator 
      << " +- " << std::setw(11) << std::left << sqrt(m_rateAccumulatorAND2)/ratesDenominator << " Hz"
-     << " : " <<  m_name;
+     << " : " <<  m_name
+     << " (Extrap:"<< getExtrapolationFactorString(m_extrapolationStrategy) <<")";
   return ss.str();
 }
 
@@ -148,22 +149,24 @@ void RatesGroup::execute(const WeightingValuesSummary_t& weights) {
   }
 
   //TODO - we currently only let groups scale linearly. Should change this.
-  const double w = weights.m_enhancedBiasWeight * (m_doLumiExtrapolation ? weights.m_linearLumiFactor : 1.);
+  const double w = weights.m_enhancedBiasWeight * getExtrapolationFactor(weights, m_extrapolationStrategy);
   const double wOR  = w * (1. - weightOR);
   const double wAND = w * weightAND;
 
-  //if (m_name == "Main") std::cout << ">>>>M>weightOR:" << weightOR << ",wOR:" << wOR << ",w:" << w << std::endl;
-
   m_rateAccumulatorOR   += wOR;
   m_rateAccumulatorAND  += wAND;
   m_rateAccumulatorOR2  += wOR * wOR;
   m_rateAccumulatorAND2 += wAND * wAND;
 
-  if (m_doHistograms) {
-    m_rateVsMu->Fill(weights.m_eventMu, wOR);
-    m_rateVsTrain->Fill(weights.m_distanceInTrain, wOR);
-    m_data->Fill(RatesBinIdentifier_t::kRATE_BIN_OR, wOR);
-    m_data->Fill(RatesBinIdentifier_t::kRATE_BIN_AND, wAND);
+  if (m_rateVsMuCachedPtr != nullptr) {
+    m_rateVsMuCachedPtr->Fill(weights.m_eventMu, wOR);
+  }
+  if (m_rateVsTrainCachedPtr != nullptr) {
+    m_rateVsTrainCachedPtr->Fill(weights.m_distanceInTrain, wOR);
+  }
+  if (m_dataCachedPtr != nullptr) {
+    m_dataCachedPtr->Fill(RatesBinIdentifier_t::kRATE_BIN_OR, wOR);
+    m_dataCachedPtr->Fill(RatesBinIdentifier_t::kRATE_BIN_AND, wAND);
   }
 
   if (m_uniqueTrigger != nullptr && m_uniqueTrigger->getDataHist() != nullptr) {
diff --git a/Trigger/TrigCost/RatesAnalysis/src/RatesHistoBase.cxx b/Trigger/TrigCost/RatesAnalysis/src/RatesHistoBase.cxx
index 6c8656d8f05a3677eebcbf500d65847d3ad94129..de0df84496f34a34d987babe6d73f9176f4abdf3 100644
--- a/Trigger/TrigCost/RatesAnalysis/src/RatesHistoBase.cxx
+++ b/Trigger/TrigCost/RatesAnalysis/src/RatesHistoBase.cxx
@@ -4,80 +4,91 @@
 
 #include "RatesAnalysis/RatesHistoBase.h"
 
-uint32_t RatesHistoBase::m_histoID = 0;
+#include "GaudiKernel/ITHistSvc.h"
+#include "AthenaBaseComps/AthCheckMacros.h"
 
 RatesHistoBase::RatesHistoBase(const std::string& name, const MsgStream& log, const bool doHistograms) : 
   m_name(name),
   m_doHistograms(doHistograms), m_rateVsMu(nullptr), m_rateVsTrain(nullptr), m_data(nullptr),
-  m_givenRateVsMu(false), m_givenRateVsTrain(false), m_givenData(false),
+  m_rateVsMuCachedPtr(nullptr), m_rateVsTrainCachedPtr(nullptr), m_dataCachedPtr(nullptr),
   m_log(log)
 {
-  if (m_doHistograms) {
-    m_rateVsMu = new TH1D(TString(std::to_string(m_histoID++)),TString(name + ";#mu;Rate / Unit #mu [Hz]"),226,-.5,225.5) ;
-    m_rateVsMu->SetName("rateVsMu");
+  if (doHistograms) {
+    m_rateVsMu = std::make_unique<TH1D>("",TString(name + ";#mu;Rate / Unit #mu [Hz]"),226,-.5,225.5);
     m_rateVsMu->Sumw2(true);
+    m_rateVsMuCachedPtr = m_rateVsMu.get();
 
-    m_rateVsTrain = new TH1D(TString(std::to_string(m_histoID++)),TString(name + ";Distance Into Train;Rate / BCID [Hz]"),100,-0.5,99.5) ;
-    m_rateVsTrain->SetName("rateVsTrainPosition");
+    m_rateVsTrain = std::make_unique<TH1D>("",TString(name + ";Distance Into Train;Rate / BCID [Hz]"),100,-0.5,99.5);
     m_rateVsTrain->Sumw2(true);
+    m_rateVsTrainCachedPtr = m_rateVsTrain.get();
 
-    m_data = new TH1D(TString(std::to_string(m_histoID++)),TString(";Variable;Raw Data"), RatesBinIdentifier_t::kNRATES_BINS, -.5, RatesBinIdentifier_t::kNRATES_BINS - .5) ;
-    m_data->SetName("data");
+    m_data =  std::make_unique<TH1D>("",TString(";Variable;Raw Data"), RatesBinIdentifier_t::kNRATES_BINS, -.5, RatesBinIdentifier_t::kNRATES_BINS - .5);
     m_data->Sumw2(true);
+    m_dataCachedPtr = m_data.get();
 
-    m_log << MSG::DEBUG << " For " << name << "(" << this << ") I have made histograms " 
-      << (uint64_t) m_rateVsMu << " " << m_rateVsMu->GetName() << " and " 
-      << (uint64_t) m_rateVsTrain << " " << m_rateVsTrain->GetName() << " and " 
-      << (uint64_t) m_data << " "  << m_data->GetName() << endmsg; 
   }
 }
 
+
 RatesHistoBase::~RatesHistoBase() {
-  m_log << MSG::DEBUG << "Deleting " << m_name << " (" << this << ")" << endmsg;
-  if (m_doHistograms) {
-    if (!m_givenData) {
-      delete m_data;
-      m_data = nullptr;
-    }
-    if (!m_givenRateVsMu) {
-      delete m_rateVsMu;
-      m_rateVsMu = nullptr;
-    }
-    if (!m_givenRateVsTrain) {
-      delete m_rateVsTrain;
-      m_rateVsTrain = nullptr;
-    }
+}
+
+StatusCode RatesHistoBase::giveMuHist(const ServiceHandle<ITHistSvc>& svc, const std::string& name) { 
+  if (!m_rateVsMu.get()) { 
+    m_log << MSG::ERROR << "RatesHistoBase::giveMuHist Warning requested histograms when histograming is OFF here." << endmsg;
+    return StatusCode::FAILURE;
   }
+  ATH_CHECK( svc->regHist(name, std::move(m_rateVsMu), m_rateVsMuCachedPtr) );
+  m_log << MSG::DEBUG << "For " << m_name << "(" << this << ") m_rateVsMuCachedPtr is updated to " << (uint64_t) m_rateVsMuCachedPtr << endmsg; 
+  return StatusCode::SUCCESS;
 }
 
-TH1D* RatesHistoBase::getMuHist(bool clientIsTHistSvc) { 
-  if (!m_doHistograms) { 
-    m_log << MSG::ERROR << "RatesHistoBase::getTrainHist Warning requested histograms when histograming is OFF here." << endmsg;
-    return nullptr;
+
+StatusCode RatesHistoBase::giveTrainHist(const ServiceHandle<ITHistSvc>& svc, const std::string& name) { 
+  if (!m_rateVsTrain.get()) {
+    m_log << MSG::ERROR << "RatesHistoBase::giveTrainHist Warning requested histograms when histograming is OFF here." << endmsg;
+    return StatusCode::FAILURE;
   }
-  if (clientIsTHistSvc) m_givenRateVsMu = true;
-  return m_rateVsMu; 
+  ATH_CHECK( svc->regHist(name, std::move(m_rateVsTrain), m_rateVsTrainCachedPtr) );
+  return StatusCode::SUCCESS;
 }
 
-TH1D* RatesHistoBase::getTrainHist(bool clientIsTHistSvc) { 
-  if (!m_doHistograms) {
-    m_log << MSG::ERROR << "RatesHistoBase::getTrainHist Warning requested histograms when histograming is OFF here." << endmsg;
-    return nullptr;
+
+StatusCode RatesHistoBase::giveDataHist(const ServiceHandle<ITHistSvc>& svc, const std::string& name) { 
+  if (!m_data.get()) {
+    m_log << MSG::ERROR << "RatesHistoBase::giveDataHist Warning requested histograms when histograming is OFF here." << endmsg;
+    return StatusCode::FAILURE;
   }
-  if (clientIsTHistSvc) m_givenRateVsTrain = true;
-  return m_rateVsTrain;
+  ATH_CHECK( svc->regHist(name, std::move(m_data), m_dataCachedPtr) );
+  return StatusCode::SUCCESS;
+}
+
+
+void RatesHistoBase::clearTrainHist() {
+  m_rateVsTrain.reset();
+  m_rateVsTrainCachedPtr = nullptr;
+}
+
+
+TH1* RatesHistoBase::getDataHist() {
+  return m_dataCachedPtr; // Caller must be able to deal with nullptr (e.g unique rates groups calling on their trigger)
 }
 
-TH1D* RatesHistoBase::getDataHist(bool clientIsTHistSvc) { 
-  if (clientIsTHistSvc) m_givenData = true;
-  return m_data; // Do not flag an errors about nullptr here as this is fetched by the unique rates groups on their trigger
+
+const std::string& RatesHistoBase::getExtrapolationFactorString(ExtrapStrat_t strat) const {
+  static const std::vector<std::string> values{"LINEAR_L","LINEAR_BUNCH_EXPO_MU","LINEAR_BUNCHES","LINEAR_MU","NONE"};
+  return values.at(static_cast<size_t>(strat)); 
 }
 
 
-void RatesHistoBase::normaliseHist(const double ratesDenominator) {
-  if (m_doHistograms) {
-    m_rateVsMu->Scale(1. / ratesDenominator);
-    m_rateVsTrain->Scale(1. / ratesDenominator);
-    //m_data does not get scaled as it contains raw data
+double RatesHistoBase::getExtrapolationFactor(const WeightingValuesSummary_t& weights, const ExtrapStrat_t strat) const {
+  switch (strat) {
+    case kLINEAR: return weights.m_linearLumiFactor;
+    case kEXPO_MU: return weights.m_expoMuFactor;
+    case kBUNCH_SCALING: return weights.m_bunchFactor;
+    case kMU_SCALING: return weights.m_muFactor;
+    case kNONE: return weights.m_noScaling;
+    default: m_log << MSG::ERROR << "Error in getExtrapolationFactor. Unknown ExtrapStrat_t ENUM supplied " << strat << endmsg;
   }
+  return 0.;
 }
diff --git a/Trigger/TrigCost/RatesAnalysis/src/RatesScanTrigger.cxx b/Trigger/TrigCost/RatesAnalysis/src/RatesScanTrigger.cxx
index 3acc45e3e00bed6e7125161c0335d3e2c6ea32b3..446ea1c8414cfbce5acd2b019eff4bb5029ee343 100644
--- a/Trigger/TrigCost/RatesAnalysis/src/RatesScanTrigger.cxx
+++ b/Trigger/TrigCost/RatesAnalysis/src/RatesScanTrigger.cxx
@@ -4,6 +4,9 @@
 
 #include "RatesAnalysis/RatesScanTrigger.h"
 
+#include "GaudiKernel/ITHistSvc.h"
+#include "AthenaBaseComps/AthCheckMacros.h"
+
 RatesScanTrigger::RatesScanTrigger( const std::string& name, 
                                     const MsgStream& log,
                                     const double thresholdMin, const double thresholdMax, const uint32_t thresholdBins,  
@@ -11,11 +14,13 @@ RatesScanTrigger::RatesScanTrigger( const std::string& name,
                                     const double prescale,
                                     const std::string& seedName, const double seedPrescale,
                                     const ExtrapStrat_t extrapolation) :
-  RatesTrigger(name, log, prescale, -1, seedName, seedPrescale, false, extrapolation),
-  m_rateScanHist(nullptr), m_givenRateScanHist(false), m_thresholdPassed(0), m_behaviour(behaviour)
+  RatesTrigger(name, log, prescale, -1, seedName, seedPrescale, /*base histograms*/false, extrapolation),
+  m_rateScanHist(nullptr), m_rateScanHistCachedPtr(nullptr), m_thresholdPassed(0), m_behaviour(behaviour)
   {
-    m_rateScanHist = new TH1D(std::to_string(m_histoID++).data(),TString(name + ";Threshold;Rate [Hz]"), thresholdBins, thresholdMin, thresholdMax);
-    m_rateScanHist->SetName("rateVsThreshold");
+    m_rateScanHist = std::make_unique<TH1D>("", TString(name + ";Threshold;Rate [Hz]"), thresholdBins, thresholdMin, thresholdMax);
+    m_rateScanHist->Sumw2(true);
+
+    m_rateScanHistCachedPtr = m_rateScanHist.get();
   }
 
 RatesScanTrigger::RatesScanTrigger( const std::string& name, 
@@ -26,19 +31,20 @@ RatesScanTrigger::RatesScanTrigger( const std::string& name,
                                     const std::string& seedName, const double seedPrescale,
                                     const ExtrapStrat_t extrapolation) :
   RatesTrigger(name, log, prescale, -1, seedName, seedPrescale, false, extrapolation),
-  m_rateScanHist(nullptr), m_thresholdPassed(0), m_behaviour(behaviour)
+  m_rateScanHist(nullptr), m_rateScanHistCachedPtr(nullptr), m_thresholdPassed(0), m_behaviour(behaviour)
   {
     if (thresholdBinEdged.size() < 2) {
       m_log << MSG::ERROR << "Need more than one entry in thresholdBinEdged to define histogram binning." << endmsg;
       return;
     }
     size_t nBins = thresholdBinEdged.size() - 1;
-    m_rateScanHist = new TH1D(std::to_string(m_histoID++).data(),TString(name + ";Threshold;Rate [Hz]"), nBins, thresholdBinEdged.data());
-    m_rateScanHist->SetName("rateVsThreshold");
+    m_rateScanHist = std::make_unique<TH1D>("", TString(name + ";Threshold;Rate [Hz]"), nBins, thresholdBinEdged.data());
+    m_rateScanHist->Sumw2(true);
+
+    m_rateScanHistCachedPtr = m_rateScanHist.get();
   }
 
 RatesScanTrigger::~RatesScanTrigger() {
-  if (!m_givenRateScanHist) delete m_rateScanHist;
 }
 
 void RatesScanTrigger::passThreshold(const double t, const bool unbiasedEvent) {
@@ -53,15 +59,16 @@ void RatesScanTrigger::setPassedAndExecute(const double t, const WeightingValues
   execute(weights);
 }
 
-TH1D* RatesScanTrigger::getThresholdHist(bool clientIsTHistSvc) { 
-  if (clientIsTHistSvc) m_givenRateScanHist = true;
-  return m_rateScanHist;
+
+StatusCode RatesScanTrigger::giveThresholdHist(const ServiceHandle<ITHistSvc>& svc, const std::string& name) { 
+  ATH_CHECK( svc->regHist(name, std::move(m_rateScanHist), m_rateScanHistCachedPtr) );
+  return StatusCode::SUCCESS;
 }
 
 void RatesScanTrigger::execute(const WeightingValuesSummary_t& weights) {
   if (m_thresholdPassed == std::numeric_limits<double>::min()) return; // Did not pass
   // This histogram we *do* include the extrapolation weight as we plot vs. some trigger property, not some event property
-  double w = m_totalPrescaleWeight * weights.m_enhancedBiasWeight * getExtrapolationFactor(weights);
+  double w = m_totalPrescaleWeight * weights.m_enhancedBiasWeight * getExtrapolationFactor(weights, m_extrapolationStrategy);
   // Fill the histogram cumulatively
   // We match exactly with the *lower* edge of all bins
   const int nBins = m_rateScanHist->GetNbinsX();
@@ -70,67 +77,63 @@ void RatesScanTrigger::execute(const WeightingValuesSummary_t& weights) {
     const double width = m_rateScanHist->GetBinWidth(bin);
     if ( (m_behaviour == kTriggerAboveThreshold && m_thresholdPassed < (low + width)) ||
          (m_behaviour == kTriggerBelowThreshold && m_thresholdPassed > low)) {
-      m_rateScanHist->Fill(m_rateScanHist->GetBinCenter(bin), w);
+      m_rateScanHistCachedPtr->Fill(m_rateScanHist->GetBinCenter(bin), w);
     }
   }
   // Underflow && Overflow
   const double xMin = m_rateScanHist->GetXaxis()->GetXmin();
   const double xMax = m_rateScanHist->GetXaxis()->GetXmax();
   if ( (m_behaviour == kTriggerAboveThreshold && m_thresholdPassed < xMin) || m_behaviour == kTriggerBelowThreshold ) {
-    m_rateScanHist->Fill(xMin - 1., w);
+    m_rateScanHistCachedPtr->Fill(xMin - 1., w);
   }
   if ( m_behaviour == kTriggerAboveThreshold || (m_behaviour == kTriggerBelowThreshold && m_thresholdPassed >= xMax) ) {
-    m_rateScanHist->Fill(xMax + 1, w);
+    m_rateScanHistCachedPtr->Fill(xMax + 1, w);
   }
 }
 
-void RatesScanTrigger::normaliseHist(const double ratesDenominator) {
-  RatesHistoBase::normaliseHist(ratesDenominator);
-  m_rateScanHist->Scale(1. / ratesDenominator);
-}
-
 const std::string RatesScanTrigger::printRate(const double ratesDenominator) const {
   std::stringstream ss;
   const int nBins = m_rateScanHist->GetNbinsX();
   ss << std::setfill(' '); 
-  ss << m_name << " [PS:" << m_prescale << "]" << std::endl;
+  ss << m_name << " [PS:" << m_prescale << "]";
   if (m_seed != "") ss << " <- " << m_seed << " [PS:" << m_seedPrescale << "]";
+  ss << " (Extrap:"<< getExtrapolationFactorString(m_extrapolationStrategy) << ")" << std::endl;
 
   if (m_behaviour == kTriggerBelowThreshold) {
 
-    ss << "    Threshold <= " << std::setw(11) << std::left << m_rateScanHist->GetXaxis()->GetXmin();
-    ss << " Rate:" << std::setw(11) << std::right << m_rateScanHist->GetBinContent(0)/ratesDenominator;
-    ss << " +- "   << std::setw(11) << std::left << m_rateScanHist->GetBinError(0)/ratesDenominator << " Hz" << std::endl;
+    ss << "    Threshold <= " << std::setw(11) << std::left << m_rateScanHistCachedPtr->GetXaxis()->GetXmin();
+    ss << " Rate :" << std::setw(11) << std::right << m_rateScanHistCachedPtr->GetBinContent(0)/ratesDenominator;
+    ss << " +- "   << std::setw(11) << std::left << m_rateScanHistCachedPtr->GetBinError(0)/ratesDenominator << " Hz" << std::endl;
 
     for (int bin = 1; bin <= nBins; ++bin) {
       ss << "    Threshold <= ";
-      ss << std::setw(11) << std::left << m_rateScanHist->GetBinLowEdge(bin) + m_rateScanHist->GetBinWidth(bin);
-      ss << " Rate:" << std::setw(11) << std::right << m_rateScanHist->GetBinContent(bin)/ratesDenominator;
-      ss << " +- "   << std::setw(11) << std::left << m_rateScanHist->GetBinError(bin)/ratesDenominator << " Hz";
+      ss << std::setw(11) << std::left << m_rateScanHistCachedPtr->GetBinLowEdge(bin) + m_rateScanHist->GetBinWidth(bin);
+      ss << " Rate :" << std::setw(11) << std::right << m_rateScanHistCachedPtr->GetBinContent(bin)/ratesDenominator;
+      ss << " +- "   << std::setw(11) << std::left << m_rateScanHistCachedPtr->GetBinError(bin)/ratesDenominator << " Hz";
       ss << std::endl;
     }
 
-    ss << "    Threshold >  " << std::setw(11) << std::left << m_rateScanHist->GetXaxis()->GetXmax();
-    ss << " Rate:" << std::setw(11) << std::right << m_rateScanHist->GetBinContent(nBins+1)/ratesDenominator;
-    ss << " +- "   << std::setw(11) << std::left << m_rateScanHist->GetBinError(nBins+1)/ratesDenominator << " Hz";
+    ss << "    Threshold >  " << std::setw(11) << std::left << m_rateScanHistCachedPtr->GetXaxis()->GetXmax();
+    ss << " Rate :" << std::setw(11) << std::right << m_rateScanHistCachedPtr->GetBinContent(nBins+1)/ratesDenominator;
+    ss << " +- "   << std::setw(11) << std::left << m_rateScanHistCachedPtr->GetBinError(nBins+1)/ratesDenominator << " Hz";
   
   } else if (m_behaviour == kTriggerAboveThreshold) {
 
-    ss << "    Threshold <  " << std::setw(11) << std::left << m_rateScanHist->GetXaxis()->GetXmin();
-    ss << " Rate:" << std::setw(11) << std::right << m_rateScanHist->GetBinContent(0)/ratesDenominator;
-    ss << " +- "   << std::setw(11) << std::left << m_rateScanHist->GetBinError(0)/ratesDenominator << " Hz" << std::endl;
+    ss << "    Threshold <  " << std::setw(11) << std::left << m_rateScanHistCachedPtr->GetXaxis()->GetXmin();
+    ss << " Rate: " << std::setw(11) << std::right << m_rateScanHistCachedPtr->GetBinContent(0)/ratesDenominator;
+    ss << " +- "   << std::setw(11) << std::left << m_rateScanHistCachedPtr->GetBinError(0)/ratesDenominator << " Hz" << std::endl;
   
     for (int bin = 1; bin <= nBins; ++bin) {
       ss << "    Threshold >= ";
-      ss << std::setw(11) << std::left << m_rateScanHist->GetBinLowEdge(bin);
-      ss << " Rate:" << std::setw(11) << std::right << m_rateScanHist->GetBinContent(bin)/ratesDenominator;
-      ss << " +- "   << std::setw(11) << std::left << m_rateScanHist->GetBinError(bin)/ratesDenominator << " Hz";
+      ss << std::setw(11) << std::left << m_rateScanHistCachedPtr->GetBinLowEdge(bin);
+      ss << " Rate: " << std::setw(11) << std::right << m_rateScanHistCachedPtr->GetBinContent(bin)/ratesDenominator;
+      ss << " +- "   << std::setw(11) << std::left << m_rateScanHistCachedPtr->GetBinError(bin)/ratesDenominator << " Hz";
       ss << std::endl;
     }
 
-    ss << "    Threshold >= " << std::setw(11) << std::left << m_rateScanHist->GetXaxis()->GetXmax();
-    ss << " Rate:" << std::setw(11) << std::right << m_rateScanHist->GetBinContent(nBins+1)/ratesDenominator;
-    ss << " +- "   << std::setw(11) << std::left << m_rateScanHist->GetBinError(nBins+1)/ratesDenominator << " Hz";
+    ss << "    Threshold >= " << std::setw(11) << std::left << m_rateScanHistCachedPtr->GetXaxis()->GetXmax();
+    ss << " Rate: " << std::setw(11) << std::right << m_rateScanHistCachedPtr->GetBinContent(nBins+1)/ratesDenominator;
+    ss << " +- "   << std::setw(11) << std::left << m_rateScanHistCachedPtr->GetBinError(nBins+1)/ratesDenominator << " Hz";
   
   }
 
diff --git a/Trigger/TrigCost/RatesAnalysis/src/RatesTrigger.cxx b/Trigger/TrigCost/RatesAnalysis/src/RatesTrigger.cxx
index a40c5e916828f598cc5b849c094ca3146d138444..30ebd1922356c5abdf56bcaaf2e7493ffe78952e 100644
--- a/Trigger/TrigCost/RatesAnalysis/src/RatesTrigger.cxx
+++ b/Trigger/TrigCost/RatesAnalysis/src/RatesTrigger.cxx
@@ -49,19 +49,19 @@ void RatesTrigger::execute(const WeightingValuesSummary_t& weights) {
   if (m_pass == false) return; 
   double w =  m_totalPrescaleWeight * weights.m_enhancedBiasWeight;
   // The vs. mu histogram is a property of the INPUT event so we don't apply any L scaling here
-  if (m_rateVsMu != nullptr) m_rateVsMu->Fill(weights.m_eventMu, w);
-  w *= getExtrapolationFactor(weights);
+  if (m_rateVsMuCachedPtr != nullptr) m_rateVsMuCachedPtr->Fill(weights.m_eventMu, w);
+  w *= getExtrapolationFactor(weights, m_extrapolationStrategy);
   // The vs. position in train is agnostic to INPUT event & TARGET conditions - i.e. the bunch train structure is not
   // re-weighted in any way. Hence we can apply whatever extrapolation strategy we want here.
-  if (m_rateVsTrain != nullptr) m_rateVsTrain->Fill(weights.m_distanceInTrain, w);
+  if (m_rateVsTrainCachedPtr != nullptr) m_rateVsTrainCachedPtr->Fill(weights.m_distanceInTrain, w);
   m_rateAccumulator  += w;
   m_rateAccumulator2 += w * w;
-  if (m_data != nullptr) m_data->Fill(RatesBinIdentifier_t::kRATE_BIN_OR, w);
+  if (m_dataCachedPtr != nullptr) m_dataCachedPtr->Fill(RatesBinIdentifier_t::kRATE_BIN_OR, w);
   if (m_expressPrescale >= 1) {
-    const double wExp = m_totalPrescaleWeightExpress * weights.m_enhancedBiasWeight * getExtrapolationFactor(weights);
+    const double wExp = m_totalPrescaleWeightExpress * weights.m_enhancedBiasWeight * getExtrapolationFactor(weights, m_extrapolationStrategy);
     m_rateExpressAccumulator  += wExp;
     m_rateExpressAccumulator2 += wExp * wExp;
-    if (m_data != nullptr) m_data->Fill(RatesBinIdentifier_t::kEXPRESS_BIN, wExp);
+    if (m_dataCachedPtr != nullptr) m_dataCachedPtr->Fill(RatesBinIdentifier_t::kEXPRESS_BIN, wExp);
   }
 }
 
@@ -70,18 +70,6 @@ double RatesTrigger::getPrescale(const bool includeExpress) const {
   return m_prescale;
 }
 
-double RatesTrigger::getExtrapolationFactor(const WeightingValuesSummary_t& weights) const {
-  switch (m_extrapolationStrategy) {
-    case kLINEAR: return weights.m_linearLumiFactor;
-    case kEXPO_MU: return weights.m_expoMuFactor;
-    case kBUNCH_SCALING: return weights.m_bunchFactor;
-    case kMU_SCALING: return weights.m_muFactor;
-    case kNONE: return weights.m_noScaling;
-    default: m_log << MSG::ERROR << "Error in getExtrapolationFactor. Unknown ExtrapStrat_t ENUM supplied " << m_extrapolationStrategy << endmsg;
-  }
-  return 0.;
-}
-
 const std::string RatesTrigger::printConfig() const {
   std::stringstream ss;
   ss << std::setfill(' ') 
@@ -96,30 +84,32 @@ const std::string RatesTrigger::printConfig() const {
 const std::string RatesTrigger::printRate(const double ratesDenominator) const {
   std::stringstream ss;
   ss << std::setfill(' '); 
-  ss << "Rate:" << std::setw(11) << std::right << m_rateAccumulator/ratesDenominator 
+  ss << "Rate: " << std::setw(11) << std::right << m_rateAccumulator/ratesDenominator 
      << " +- "    << std::setw(11) << std::left << sqrt(m_rateAccumulator2)/ratesDenominator << " Hz";
   if (m_uniqueGroup != nullptr) {
     const double unique = (getDisabled() == true ? 0. : m_uniqueGroup->getUniqueWeight(ratesDenominator));
     //const double unique = m_uniqueGroup->m_rateAccumulatorOR / ratesDenominator; // For dbg - this is the rate of N-1 
     // Getting the fractional error of refgular rate and applying it to the unique rate
     const double uniqueErr = (isZero(m_rateAccumulator) ? 0. : (sqrt(m_rateAccumulator2)/m_rateAccumulator) * unique);
-    ss << ", Unique Rate:" << std::setw(11) << std::right << unique
+    ss << ", Unique Rate: " << std::setw(11) << std::right << unique
        << " +- "           << std::setw(11) << std::left << uniqueErr << " Hz";
   }
   ss << " : ";
   ss << m_name << " [PS:" << m_prescale << "]";
   if (m_seed != "") ss << " <- " << m_seed << " [PS:" << m_seedPrescale << "]";
+  ss << " (Extrap:"<< getExtrapolationFactorString(m_extrapolationStrategy) <<")";
   return ss.str();
 }
 
 const std::string RatesTrigger::printExpressRate(const double ratesDenominator) const {
   std::stringstream ss;
   ss << std::setfill(' '); 
-  ss << "Express Rate:" << std::setw(11) << std::right << m_rateExpressAccumulator/ratesDenominator 
+  ss << "Express Rate: " << std::setw(11) << std::right << m_rateExpressAccumulator/ratesDenominator 
      << " +- "    << std::setw(11) << std::left << sqrt(m_rateExpressAccumulator2)/ratesDenominator << " Hz";
   ss << " : ";
-  if (m_seed != "") ss << m_seed << " [PS:" << m_seedPrescale << "] -> ";
   ss << m_name << " [PS:" << m_prescale << "] [EXPRESS PS:" << m_expressPrescale << "]";
+  if (m_seed != "") ss << " <- " << m_seed << " [PS:" << m_seedPrescale << "]";
+  ss << " (Extrap:"<< getExtrapolationFactorString(m_extrapolationStrategy) <<")";
   return ss.str();
 }
 
diff --git a/Trigger/TrigCost/RatesAnalysis/src/components/RatesAnalysis_entries.cxx b/Trigger/TrigCost/RatesAnalysis/src/components/RatesAnalysis_entries.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..567b7811d4aab781b5de9bd4c50b848bf2a66c42
--- /dev/null
+++ b/Trigger/TrigCost/RatesAnalysis/src/components/RatesAnalysis_entries.cxx
@@ -0,0 +1,7 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "../FullMenu.h"
+
+DECLARE_COMPONENT( FullMenu )
diff --git a/Trigger/TrigCost/RatesAnalysis/test/RatesAnalysis_test.cxx b/Trigger/TrigCost/RatesAnalysis/test/RatesAnalysis_test.cxx
index 5b65b75f91b40411bc73f9eda1b573959b943caf..6897e217e6439e81b139ba553c718a53bb180b3d 100644
--- a/Trigger/TrigCost/RatesAnalysis/test/RatesAnalysis_test.cxx
+++ b/Trigger/TrigCost/RatesAnalysis/test/RatesAnalysis_test.cxx
@@ -14,11 +14,11 @@ int main() {
 
   MsgStream log(nullptr, "RatesAnalysis_test");
 
-  RatesTrigger* triggerA1 = new RatesTrigger("TriggerA1", log, 1, 1, "SeedA", 2);
-  RatesTrigger* triggerA2 = new RatesTrigger("TriggerA2", log, 2, 1, "SeedA", 2);
+  RatesTrigger* triggerA1 = new RatesTrigger("TriggerA1", log, /*prescale*/1, /*XpressPrescale*/1, "SeedA", /*seedPrescale*/2);
+  RatesTrigger* triggerA2 = new RatesTrigger("TriggerA2", log, /*prescale*/2, /*XpressPrescale*/1, "SeedA", /*seedPrescale*/2);
 
-  RatesTrigger* triggerB1 = new RatesTrigger("TriggerB1", log, 3, 1, "SeedB", 4);
-  RatesTrigger* triggerB2 = new RatesTrigger("TriggerB2", log, 4, 1, "SeedB", 4);
+  RatesTrigger* triggerB1 = new RatesTrigger("TriggerB1", log, /*prescale*/3, /*XpressPrescale*/1, "SeedB", /*seedPrescale*/4);
+  RatesTrigger* triggerB2 = new RatesTrigger("TriggerB2", log, /*prescale*/4, /*XpressPrescale*/1, "SeedB", /*seedPrescale*/4);
 
   WeightingValuesSummary_t wvs;
   wvs.m_enhancedBiasWeight = 5000;
diff --git a/Trigger/TrigCost/RatesAnalysisExamplesXAOD/CMakeLists.txt b/Trigger/TrigCost/RatesAnalysisExamplesXAOD/CMakeLists.txt
deleted file mode 100644
index d625286be66a2d70e1cf116934b090cb66dcde83..0000000000000000000000000000000000000000
--- a/Trigger/TrigCost/RatesAnalysisExamplesXAOD/CMakeLists.txt
+++ /dev/null
@@ -1,28 +0,0 @@
-################################################################################
-# Package: RatesAnalysisExamplesXAOD
-################################################################################
-
-# Declare the package name:
-atlas_subdir( RatesAnalysisExamplesXAOD )
-
-# Declare the package's dependencies:
-atlas_depends_on_subdirs( PUBLIC
-                          GaudiKernel
-                          PRIVATE
-                          Event/xAOD/xAODJet
-                          Event/xAOD/xAODMuon
-                          Event/xAOD/xAODMissingET
-                          Event/xAOD/xAODTau
-                          Event/xAOD/xAODEgamma
-                          Trigger/TrigCost/RatesAnalysis )
-
-# Component(s) in the package:
-atlas_add_component( RatesAnalysisExamplesXAOD
-                     src/*.cxx
-                     src/components/*.cxx
-                     LINK_LIBRARIES GaudiKernel xAODMuon xAODTau xAODEgamma xAODMissingET xAODJet RatesAnalysis )
-
-# Install files from the package:
-atlas_install_python_modules( python/*.py )
-atlas_install_joboptions( share/*.py )
-
diff --git a/Trigger/TrigCost/RatesAnalysisExamplesXAOD/python/RatesGetCrossSectionMC.py b/Trigger/TrigCost/RatesAnalysisExamplesXAOD/python/RatesGetCrossSectionMC.py
deleted file mode 100644
index 74ca1a111878d371c8fdd41a5e374f036b4ea4b2..0000000000000000000000000000000000000000
--- a/Trigger/TrigCost/RatesAnalysisExamplesXAOD/python/RatesGetCrossSectionMC.py
+++ /dev/null
@@ -1,108 +0,0 @@
-# Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
-
-from AthenaCommon.Logging import logging
-
-##
-# Basic wrapper around pyAMI for getting MC dataset cross sections & maniuplating dataset name strings
-##
-class GetCrossSectionAMI:
-
-  def __init__(self):
-    self.log = logging.getLogger('GetCrossSectionAMI')
-
-  def getDatasetNameFromPath(self, path):
-    import re
-    datasetRegex = 'mc.._.*TeV\..*' # mc??_????TeV.???
-    parts = path.split('/')
-    for part in parts: # Do any of these parts look like a dataset name?
-      if re.match(datasetRegex, part):
-        return part
-    self.log.error("Unable to figure out the MC dataset name from the path, you must supply it yourself as MCDatasetName")
-
-  def queryAmi(self, dataset):
-    if not 'EVNT' in dataset:
-      dataset = self.convertDatasetNameToEVNT(dataset)
-
-    self.dataset = dataset
-    try:
-      import pyAMI.client
-      import pyAMI.atlas.api as api
-      import json
-    except ImportError:
-      log.fatal("Unable to import the pyAMI client. Did you run 'lsetup pyami; voms-proxy-init -voms atlas'?")
-      exit()
-
-    self.log.info("Looking up details of " + self.dataset + " on AMI")
-
-    if not "EVNT" in self.dataset:
-      self.log.error("Dataset must be of type EVNT")
-      exit()
-
-    api.init()
-
-    # INSTANTIATE THE PYAMI CLIENT FOR ATLAS
-    client = pyAMI.client.Client('atlas')
-
-    # search for EVNT file
-    fields = 'files.cross_section,files.gen_filt_eff,nfiles'
-    try:
-      resDict = api.list_datasets(client, patterns = self.dataset, fields = fields)
-    except Exception as reason:
-      self.log.fatal("Unable to query AMI. Do you have a grid certificate? 'voms-proxy-init -voms atlas'")
-      self.log.fatal("You can also supply the MCCrossSection and MCFilterEfficiency by hand for this sample")
-      self.log.fatal("The reason for failure was:" + str(reason))
-      exit();
-
-    # loop over files in dataset, calculate avg filter efficiency
-    numFiles = 0
-    avgFiltEff = 0.0
-    avgXSec = 0.0
-    for results in resDict:
-      numFiles = (float)(results['nfiles'])
-      if (results['files_gen_filt_eff'] != 'NULL'): avgFiltEff += (float) (results['files_gen_filt_eff'])
-      if (results['files_cross_section'] != 'NULL'): avgXSec += (float) (results['files_cross_section'])
-      pass # end loop over files
-
-    if(numFiles != 0):
-      self.filtereff = avgFiltEff/numFiles
-      self.xsec      = avgXSec/numFiles
-
-    if self.xsec and self.filtereff:
-      self.log.info("################## ################## AMI Query Complete ################## ################## ")
-      self.log.info("    Cross section:" + str(self.xsec) + " nb")
-      self.log.info("Filter efficiency:" + str(self.filtereff))
-      self.log.info("You may want to supply these numbers directly in future as MCCrossSection and MCFilterEfficiency for speed")
-      self.log.info("################## ################## ################## ################## ################### ")
-    else:
-      self.log.fatal("Unable to read in cross section and filter efficency from AMI.")
-      exit();
-
-  def getCrossSection(self):
-    return self.xsec
-
-  def getFilterEfficiency(self):
-  	return self.filtereff
-
-  def convertDatasetNameToEVNT(self, dataset):
-    datasetReplace = dataset
-    datasetReplace = datasetReplace.replace('merge.AOD', 'evgen.EVNT')
-    datasetReplace = datasetReplace.replace('recon.AOD', 'evgen.EVNT')
-    datasetReplace = datasetReplace.replace('merge.ESD', 'evgen.EVNT')
-    datasetReplace = datasetReplace.replace('recon.ESD', 'evgen.EVNT')
-    explodeDot = datasetReplace.split('.')
-    datasetEVNT = ''
-    isTags = False
-    for part in explodeDot:
-      if not 'EVNT' in part:
-        if isTags: # Last section, just take the exxxx tag
-          explodeScore = part.split('_')
-          datasetEVNT += explodeScore[0]
-        else:
-          datasetEVNT += part + '.'
-      else:
-        isTags = True
-        datasetEVNT += part + '.'
-    self.log.info("Tried to converted dataset name to EVNT.")
-    self.log.info("Went from: " + dataset)
-    self.log.info("       To: " + datasetEVNT)
-    return datasetEVNT
diff --git a/Trigger/TrigCost/RatesAnalysisExamplesXAOD/share/ExampleRatesEmulation_JobOptions.py b/Trigger/TrigCost/RatesAnalysisExamplesXAOD/share/ExampleRatesEmulation_JobOptions.py
deleted file mode 100644
index ca8809e6d9119f5347a5c02ffb52bca27a7f1cd3..0000000000000000000000000000000000000000
--- a/Trigger/TrigCost/RatesAnalysisExamplesXAOD/share/ExampleRatesEmulation_JobOptions.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
-
-# Edit this file too 
-include("RatesAnalysisExamplesXAOD/ExampleRates_Configuration.py")
-
-#---- Algorithm Setup -------
-
-algseq = CfgMgr.AthSequencer("AthAlgSeq") #gets the main AthSequencer
-algseq += CfgMgr.ExampleRatesEmulation() #adds an instance of the example rates algorithm
-
-algseq.ExampleRatesEmulation.PrescaleXML = PrescaleXML
-algseq.ExampleRatesEmulation.DoUniqueRates = DoUniqueRates
-algseq.ExampleRatesEmulation.DoHistograms = DoHistograms
-algseq.ExampleRatesEmulation.NormaliseHistograms = NormaliseHistograms
-algseq.ExampleRatesEmulation.UseBunchCrossingTool = DoRatesVsPositionInTrain
-algseq.ExampleRatesEmulation.TargetLuminosity = TargetLuminosity
-algseq.ExampleRatesEmulation.VetoStartOfTrain = VetoStartOfTrain
-algseq.ExampleRatesEmulation.EnableLumiExtrapolation = EnableLumiExtrapolation
-
-# The following are only needed for MC
-algseq.ExampleRatesEmulation.IsMC = isMC
-algseq.ExampleRatesEmulation.MCCrossSection = MCCrossSection
-algseq.ExampleRatesEmulation.MCFilterEfficiency = MCFilterEfficiency
-algseq.ExampleRatesEmulation.MCKFactor = MCKFactor
-
-print algseq.ExampleRatesEmulation
-
-include("RatesAnalysisExamplesXAOD/ExampleRates_postSetup.py")
diff --git a/Trigger/TrigCost/RatesAnalysisExamplesXAOD/share/ExampleRatesFullMenu_JobOptions.py b/Trigger/TrigCost/RatesAnalysisExamplesXAOD/share/ExampleRatesFullMenu_JobOptions.py
deleted file mode 100644
index b095976f875838def60ffabf11db4108edcaf963..0000000000000000000000000000000000000000
--- a/Trigger/TrigCost/RatesAnalysisExamplesXAOD/share/ExampleRatesFullMenu_JobOptions.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
-
-# Edit this file too 
-include("RatesAnalysisExamplesXAOD/ExampleRates_Configuration.py")
-
-#---- Algorithm Setup -------
-
-algseq = CfgMgr.AthSequencer("AthAlgSeq") #gets the main AthSequencer
-algseq += CfgMgr.ExampleRatesFullMenu() #adds an instance of the example rates algorithm
-
-algseq.ExampleRatesFullMenu.PrescaleXML = PrescaleXML
-algseq.ExampleRatesFullMenu.DoUniqueRates = DoUniqueRates
-algseq.ExampleRatesFullMenu.DoHistograms = DoHistograms
-algseq.ExampleRatesFullMenu.NormaliseHistograms = NormaliseHistograms
-algseq.ExampleRatesFullMenu.UseBunchCrossingTool = DoRatesVsPositionInTrain
-algseq.ExampleRatesFullMenu.TargetLuminosity = TargetLuminosity
-algseq.ExampleRatesFullMenu.VetoStartOfTrain = VetoStartOfTrain
-algseq.ExampleRatesFullMenu.EnableLumiExtrapolation = EnableLumiExtrapolation
-
-# The following are only needed for MC
-algseq.ExampleRatesFullMenu.IsMC = isMC
-algseq.ExampleRatesFullMenu.MCCrossSection = MCCrossSection
-algseq.ExampleRatesFullMenu.MCFilterEfficiency = MCFilterEfficiency
-algseq.ExampleRatesFullMenu.MCKFactor = MCKFactor
-
-print algseq.ExampleRatesFullMenu
-
-include("RatesAnalysisExamplesXAOD/ExampleRates_postSetup.py")
diff --git a/Trigger/TrigCost/RatesAnalysisExamplesXAOD/share/ExampleRates_Configuration.py b/Trigger/TrigCost/RatesAnalysisExamplesXAOD/share/ExampleRates_Configuration.py
deleted file mode 100644
index 50c4f658dbb195a13606f064c6acc0c5dc8a9b6a..0000000000000000000000000000000000000000
--- a/Trigger/TrigCost/RatesAnalysisExamplesXAOD/share/ExampleRates_Configuration.py
+++ /dev/null
@@ -1,95 +0,0 @@
-# Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
-
-# This file is not a full JO. It is utilised in all ExampleRates JobOptions.
-#
-#---- User Options -------
-
-if not "GridMode" in dir():
-  GridMode = False
-
-if not "EvtMax" in dir():
-  EvtMax = -1
-
-if not "DoHistograms" in dir():
-  DoHistograms = True
-
-if not "NormaliseHistograms" in dir(): # Switch this off when running on the grid. hadd and normalise once all the files are back.
-  NormaliseHistograms = True
-
-if not "OutputHistogramFile" in dir():
-  OutputHistogramFile = "RatesHistograms.root"
-
-if not "DoUniqueRates" in dir():
-  DoUniqueRates = True
-
-if not "DoRatesVsPositionInTrain" in dir():
-  DoRatesVsPositionInTrain = False
-
-if not "EnableLumiExtrapolation" in dir(): 
-  EnableLumiExtrapolation = True
-
-if not "TargetLuminosity" in dir(): # Only if EnableLumiExtrapolation=True. See RatesAnalysis/RatesAnalysisAlg.h for more sophisticated scaling
-  TargetLuminosity = 1e34
-
-if not "VetoStartOfTrain" in dir(): # Implies DoRatesVsPositionInTrain=True
-  VetoStartOfTrain = 0
-
-# To simulate the application of prescales, need a prescale XML from the RuleBook
-# Make sure this is somewhere PathResolver can find it e.g. your current directory
-if not "PrescaleXML" in dir():
-  PrescaleXML = ""
-
-# AOD files to process
-if not "FilesInput" in dir():
-  # Test file from AtlasHLT,21.0.15: data16_13TeV.00309640.physics_EnhancedBias.merge.AOD.r9083_r9084_p3008_tid10654269_00
-  FilesInput = ["root://eosatlas.cern.ch//eos/atlas/atlasdatadisk/rucio/data16_13TeV/8d/de/AOD.10654269._000566.pool.root.1"]
-
-# If running over MC - we need the process cross section & filter efficiency of the sample
-# You can either supply these directly OR the dataset name for auto-lookup
-# If the dataset name is in the input files path, then it will be fetched from there
-# Note to enable autolookup, first run "lsetup pyami; voms-proxy-init -voms atlas" and enter your grid pass phrase
-if not "MCDatasetName" in dir(): # Set manually or make sure it is contained in the file path
-  MCDatasetName = ""
-
-# Alternatively, supply the values by hand for MC
-if not "MCCrossSection" in dir(): # Cross section of process in nb
-  MCCrossSection = 0
-if not "MCFilterEfficiency" in dir(): # Filter efficiency of any MC filter (0.0 - 1.0)
-  MCFilterEfficiency = 0
-if not "MCKFactor" in dir(): # Additional fudge-factor to apply to account for higher order corrections.  
-  MCKFactor = 1
-
-if not "ForcePOOL" in dir(): # If hybrid mode is failing for some reason (true at the moment for 21.X)
-  ForcePOOL = False
-
-#---- Setup -------
-
-if GridMode:
-  NormaliseHistograms = False
-
-if VetoStartOfTrain > 0:
-  DoRatesVsPositionInTrain = True
-
-jps.AthenaCommonFlags.FilesInput = FilesInput
-from PyUtils import AthFile
-af = AthFile.fopen(FilesInput[0]) 
-isMC = ('IS_SIMULATION' in af.fileinfos['evt_type'])
-
-if isMC and (not MCCrossSection or not MCFilterEfficiency): # If the input file is MC then make sure we have the needed info
-  from RatesAnalysisExamplesXAOD.RatesGetCrossSectionMC import GetCrossSectionAMI
-  amiTool = GetCrossSectionAMI()
-  if not MCDatasetName: # Can we get the dataset name from the input file path?
-    MCDatasetName = amiTool.getDatasetNameFromPath(FilesInput[0])
-  amiTool.queryAmi(MCDatasetName)
-  MCCrossSection = amiTool.getCrossSection()
-  MCFilterEfficiency = amiTool.getFilterEfficiency()
-
-#---- ESD Flag -------
-
-isESD = ('ESD' in FilesInput[0])
-
-#---- Set read-mode -------
-
-# xAOD hybrid mode is faster - but is not compatible with reading BICD position in train from DB or ESD files
-if DoRatesVsPositionInTrain or isESD or ForcePOOL: import AthenaPoolCnvSvc.ReadAthenaPool #sets up reading of POOL files (e.g. xAODs)
-else:                                              import AthenaRootComps.ReadAthenaxAODHybrid #alternative for FAST xAOD reading!
diff --git a/Trigger/TrigCost/RatesAnalysisExamplesXAOD/share/ExampleRates_postSetup.py b/Trigger/TrigCost/RatesAnalysisExamplesXAOD/share/ExampleRates_postSetup.py
deleted file mode 100644
index f425803910d90acba545d41e4a327c5220f07c10..0000000000000000000000000000000000000000
--- a/Trigger/TrigCost/RatesAnalysisExamplesXAOD/share/ExampleRates_postSetup.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
-
-# This file is not a full JO. It is utilised in all ExampleRates JobOptions.
-#---- Additional Setup -------
-
-theApp.EvtMax=EvtMax #says how many events to run over. Set to -1 for all events
-
-# Create output ROOT file for histograms
-if DoHistograms:
-  if not hasattr(svcMgr, 'THistSvc'): svcMgr += CfgMgr.THistSvc()
-  svcMgr.THistSvc.Output += ["RATESTREAM DATAFILE='" + OutputHistogramFile + "' OPT='RECREATE'"] #add an output root file stream
-
-# Setup for accessing bunchgroup data from the DB
-if DoRatesVsPositionInTrain:
-  from AthenaCommon.GlobalFlags import globalflags
-  globalflags.DataSource = 'geant4' if isMC else 'data' 
-  globalflags.DatabaseInstance = 'CONDBR2' 
-  from TrigBunchCrossingTool.BunchCrossingTool import BunchCrossingTool
-  if isMC: ToolSvc += BunchCrossingTool( "MC" )
-  else: ToolSvc += BunchCrossingTool( "LHC" )
-
-# Reduce logging - but don't suppress messages
-include("AthAnalysisBaseComps/SuppressLogging.py")
-MessageSvc.defaultLimit = 9999999
-MessageSvc.useColors = True
-MessageSvc.Format = "% F%35W%S%7W%R%T %0W%M"
-
-# Execution statistics
-from AthenaCommon.AppMgr import theAuditorSvc
-from GaudiAud.GaudiAudConf import ChronoAuditor
-theAuditorSvc += ChronoAuditor()
-theApp.AuditAlgorithms = True  
diff --git a/Trigger/TrigCost/RatesAnalysisExamplesXAOD/src/ExampleRatesEmulation.cxx b/Trigger/TrigCost/RatesAnalysisExamplesXAOD/src/ExampleRatesEmulation.cxx
deleted file mode 100644
index c13ff47197091fee7302fb1535c391782c99eddc..0000000000000000000000000000000000000000
--- a/Trigger/TrigCost/RatesAnalysisExamplesXAOD/src/ExampleRatesEmulation.cxx
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
-  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
-*/
-
-#include "ExampleRatesEmulation.h"
-
-#include "xAODJet/Jet.h"
-// #include "xAODMissingET/MissingET.h"
-// #include "xAODMissingET/MissingETContainer.h"
-#include "xAODMuon/Muon.h"
-#include "xAODTau/TauJet.h"
-#include "xAODEgamma/Photon.h"
-#include "xAODTau/TauJetContainer.h"
-#include "xAODTau/TauJetAuxContainer.h"
-#include "xAODMuon/MuonContainer.h"
-#include "xAODMuon/MuonAuxContainer.h"
-#include "xAODEgamma/PhotonContainer.h"
-#include "xAODEgamma/PhotonAuxContainer.h"
-
-ExampleRatesEmulation::ExampleRatesEmulation( const std::string& name, ISvcLocator* pSvcLocator ) : RatesAnalysisAlg(name, pSvcLocator) {
-  declareProperty( "TargetLuminosity", m_lumi = 1e34);
-}
-
-ExampleRatesEmulation::~ExampleRatesEmulation() {
-}
-
-StatusCode  ExampleRatesEmulation::ratesInitialize() {
-  ATH_MSG_INFO("In ratesInitialize()");
-
-  // Here we assume a full-ring, other functions are available to change this assumption.
-  // @see setTargetLumiMu(const double lumi, const double mu);
-  // @see setTargetLumiBunches(const double lumi, const int32_t bunches);
-  // @see setTargetMuBunches(const double mu, const int32_t bunches);
-  setTargetLumi( m_lumi );
-
-  const std::vector<double> jetBinEdges = {0,2.5,5,7.5,10,12.5,15,17.5,20,22.5,25,30,35,40,45,50,60,70,80,90,100,110,120,130,140,150,200,250,300,350,400,500};
-  const std::vector<double> htBinEdges = {100,200,300,400,500,600,700,800,900,1000,1500,2000,2500};
-
-  ATH_CHECK(newScanTrigger("OFF_Jx",  jetBinEdges, RatesScanTrigger::TriggerBehaviour_t::kTriggerBelowThreshold));
-  ATH_CHECK(newScanTrigger("OFF_FATJx",  jetBinEdges, RatesScanTrigger::TriggerBehaviour_t::kTriggerBelowThreshold));
-  ATH_CHECK(newScanTrigger("OFF_Jx.32ETA49",  jetBinEdges, RatesScanTrigger::TriggerBehaviour_t::kTriggerBelowThreshold));
-  ATH_CHECK(newScanTrigger("OFF_4Jx", jetBinEdges, RatesScanTrigger::TriggerBehaviour_t::kTriggerBelowThreshold));
-
-  ATH_CHECK(newScanTrigger("OFF_HTx", htBinEdges, RatesScanTrigger::TriggerBehaviour_t::kTriggerBelowThreshold));
-  ATH_CHECK(newScanTrigger("OFF_XEx", jetBinEdges, RatesScanTrigger::TriggerBehaviour_t::kTriggerBelowThreshold));
-
-  ATH_CHECK(newScanTrigger("OFF_MUx",  jetBinEdges, RatesScanTrigger::TriggerBehaviour_t::kTriggerBelowThreshold));
-  ATH_CHECK(newScanTrigger("OFF_2MUx",  jetBinEdges, RatesScanTrigger::TriggerBehaviour_t::kTriggerBelowThreshold));
-
-  ATH_CHECK(newScanTrigger("OFF_Gx",  jetBinEdges, RatesScanTrigger::TriggerBehaviour_t::kTriggerBelowThreshold));
-  ATH_CHECK(newScanTrigger("OFF_2Gx",  jetBinEdges, RatesScanTrigger::TriggerBehaviour_t::kTriggerBelowThreshold));
-
-  ATH_CHECK(newScanTrigger("OFF_TAUx",  jetBinEdges, RatesScanTrigger::TriggerBehaviour_t::kTriggerBelowThreshold));
-  ATH_CHECK(newScanTrigger("OFF_2TAUx",  jetBinEdges, RatesScanTrigger::TriggerBehaviour_t::kTriggerBelowThreshold));
-
-  ATH_CHECK(newScanTrigger("OFF_TAUx",  jetBinEdges, RatesScanTrigger::TriggerBehaviour_t::kTriggerBelowThreshold));
-  ATH_CHECK(newScanTrigger("OFF_2TAUx",  jetBinEdges, RatesScanTrigger::TriggerBehaviour_t::kTriggerBelowThreshold));
-
-
-  ATH_CHECK(newTrigger("OFF_MU35", 1, -1, "", 1, "RATE_Muon"));
-  ATH_CHECK(newTrigger("OFF_2MU20", 1, -1, "", 1, "RATE_Muon"));
-
-  ATH_CHECK(newTrigger("OFF_G40", 1, -1, "", 1, "RATE_EM"));
-  ATH_CHECK(newTrigger("OFF_2G25", 1, -1, "", 1, "RATE_EM"));
-
-  ATH_CHECK(newTrigger("OFF_G15_MU15", 1, -1, "", 1, "RATE_Combined"));
-
-  ATH_CHECK(newTrigger("OFF_TAU130", 1, -1, "", 1, "RATE_Tau"));
-  ATH_CHECK(newTrigger("OFF_2TAU50", 1, -1, "", 1, "RATE_Tau"));
-
-  ATH_CHECK(newTrigger("OFF_J180", 1, -1, "", 1, "RATE_Jet"));
-  ATH_CHECK(newTrigger("OFF_FATJ375", 1, -1, "", 1, "RATE_Jet"));
-  ATH_CHECK(newTrigger("OFF_J80.32ETA49", 1, -1, "", 1, "RATE_Jet"));
-  ATH_CHECK(newTrigger("OFF_4J50", 1, -1, "", 1, "RATE_Jet"));
-
-  // ATH_CHECK(newTrigger("OFF_HT300", 1, 1, "", 1, "Rate_HTMET"));
-  // ATH_CHECK(newTrigger("OFF_XE125", 1, 1, "", 1, "Rate_HTMET"));
-
-  // ATH_CHECK(newTrigger("OFF_J100_XE100", 1, 1, "", 1, "RATE_Combined"));
-
-
-  // ATH_CHECK(newTrigger("OFF_All", 1, 1, "", 1, ""));
-
-  return StatusCode::SUCCESS;
-}
-
-StatusCode  ExampleRatesEmulation::ratesExecute() {
-
-  const xAOD::JetContainer* jets = nullptr;
-  ATH_CHECK( evtStore()->retrieve(jets, "AntiKt4LCTopoJets") );
-  std::set<double> jetpTs; // Forces weak ordering
-  std::set<double> forwardJetpTs;
-  for (const auto& jet : *jets) {
-    jetpTs.insert(jet->pt()/1000.);
-    if (fabs(jet->eta()) > 3.2) forwardJetpTs.insert(jet->pt()/1000.);
-  }
-
-  double ht = 0.;
-  const xAOD::JetContainer* fatjets = nullptr;
-  ATH_CHECK( evtStore()->retrieve(fatjets, "CamKt12LCTopoJets") );
-  std::set<double> fatJetpTs; // Forces weak ordering
-  for (const auto& jet : *fatjets) {
-    ht += jet->pt()/1000.;
-    fatJetpTs.insert(jet->pt()/1000.);
-  }
-
-  const xAOD::TauJetContainer* tauJets = nullptr;
-  ATH_CHECK( evtStore()->retrieve(tauJets, "TauJets") );
-  std::set<double> tauJetpTs; // Forces weak ordering
-  for (const auto& tau : *tauJets) {
-    tauJetpTs.insert(tau->pt()/1000.);
-  }
-
-  const xAOD::MuonContainer* muons = nullptr;
-  ATH_CHECK( evtStore()->retrieve(muons, "Muons") );
-  std::set<double> muonpTs; // Forces weak ordering
-  for (const auto& mu : *muons) muonpTs.insert(mu->pt()/1000.);
-
-  const xAOD::PhotonContainer* photons = nullptr;
-  ATH_CHECK( evtStore()->retrieve(photons, "Photons") );
-  std::set<double> photonpTs; // Forces weak ordering
-  for (const auto& g : *photons) photonpTs.insert(g->pt()/1000.);
-
-  // const xAOD::MissingET* metObj = nullptr; 
-  // ATH_CHECK( evtStore()->retrieve( metObj, "MET_LocHadTopo" ) );
-  // const double met = metObj->met() / 1000.;
-
-  // ATH_CHECK(setTriggerDesicison("OFF_All", true));
-
-  //////////////
-
-  if (jetpTs.size() >= 1) ATH_CHECK(setTriggerDesicison("OFF_Jx", *jetpTs.rbegin() ));
-  if (jetpTs.size() >= 4) ATH_CHECK(setTriggerDesicison("OFF_4Jx",  *std::next(jetpTs.rbegin(), 3) ));
-  if (fatJetpTs.size() >= 1) ATH_CHECK(setTriggerDesicison("OFF_FATJx",  *fatJetpTs.rbegin() ));
-  if (forwardJetpTs.size() >= 1) ATH_CHECK(setTriggerDesicison("OFF_Jx.32ETA49",  *forwardJetpTs.rbegin() ));
-
-  ATH_CHECK(setTriggerDesicison("OFF_HTx", ht ));
-  // ATH_CHECK(setTriggerDesicison("OFF_XEx", met ));
-
-  if (muonpTs.size() >= 1) ATH_CHECK(setTriggerDesicison("OFF_MUx", *muonpTs.rbegin() ));
-  if (muonpTs.size() >= 2) ATH_CHECK(setTriggerDesicison("OFF_2MUx", *std::next(muonpTs.rbegin(), 1) ));
-
-  if (tauJetpTs.size() >= 1) ATH_CHECK(setTriggerDesicison("OFF_TAUx", *tauJetpTs.rbegin() ));
-  if (tauJetpTs.size() >= 2) ATH_CHECK(setTriggerDesicison("OFF_2TAUx", *std::next(tauJetpTs.rbegin(), 1) ));
-
-  if (photonpTs.size() >= 1) ATH_CHECK(setTriggerDesicison("OFF_Gx", *photonpTs.rbegin() ));
-  if (photonpTs.size() >= 2) ATH_CHECK(setTriggerDesicison("OFF_2Gx", *std::next(photonpTs.rbegin(), 1) ));
-
-  //////////////
-
-  if (muonpTs.size() >= 1 && *muonpTs.rbegin() >= 35.) ATH_CHECK(setTriggerDesicison("OFF_MU35", true ));
-  if (muonpTs.size() >= 2 && *std::next(muonpTs.rbegin(), 1) >= 20.) ATH_CHECK(setTriggerDesicison("OFF_2MU20", true ));
-
-  if (tauJetpTs.size() >= 1 && *tauJetpTs.rbegin() >= 130.) ATH_CHECK(setTriggerDesicison("OFF_TAU130", true ));
-  if (tauJetpTs.size() >= 2 && *std::next(tauJetpTs.rbegin(), 1) >= 50.) ATH_CHECK(setTriggerDesicison("OFF_2TAU50", true ));
-
-  if (photonpTs.size() >= 1 && *photonpTs.rbegin() >= 40.) ATH_CHECK(setTriggerDesicison("OFF_G40", true ));
-  if (photonpTs.size() >= 2 && *std::next(photonpTs.rbegin(), 1) >= 25.) ATH_CHECK(setTriggerDesicison("OFF_2G25", true ));
-
-  if (photonpTs.size() >= 1 && *photonpTs.rbegin() >= 15. &&
-      muonpTs.size() >= 1 && *muonpTs.rbegin() >= 15.) ATH_CHECK(setTriggerDesicison("OFF_G15_MU15", true ));
-
-  // if (jetpTs.size() >= 1 && *jetpTs.rbegin() >= 100. && met >= 100.) ATH_CHECK(setTriggerDesicison("OFF_J100_XE100", true ));
-
-  if (jetpTs.size() >= 1 && *jetpTs.rbegin() >= 180.) ATH_CHECK(setTriggerDesicison("OFF_J180", true ));
-  if (jetpTs.size() >= 4 && *std::next(jetpTs.rbegin(), 3) >= 50.) ATH_CHECK(setTriggerDesicison("OFF_4J50", true ));
-  if (fatJetpTs.size() >= 1 && *fatJetpTs.rbegin() >= 375.) ATH_CHECK(setTriggerDesicison("OFF_FATJ375", true ));
-  if (forwardJetpTs.size() >= 1 && *forwardJetpTs.rbegin() >= 80.) ATH_CHECK(setTriggerDesicison("OFF_J80.32ETA49", true ));
-
-  // if (met >= 125.) ATH_CHECK(setTriggerDesicison("OFF_XE125", true ));
-  // if (ht >= 300.) ATH_CHECK(setTriggerDesicison("OFF_HT300", true ));
-
-  return StatusCode::SUCCESS;
-}
-
-StatusCode  ExampleRatesEmulation::ratesFinalize() {
-  ATH_MSG_INFO("In ratesFinalize()");
-  return StatusCode::SUCCESS;
-}
diff --git a/Trigger/TrigCost/RatesAnalysisExamplesXAOD/src/ExampleRatesEmulation.h b/Trigger/TrigCost/RatesAnalysisExamplesXAOD/src/ExampleRatesEmulation.h
deleted file mode 100644
index 76fbec9cf0aa1273b639d1570779726139d02188..0000000000000000000000000000000000000000
--- a/Trigger/TrigCost/RatesAnalysisExamplesXAOD/src/ExampleRatesEmulation.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
-  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
-*/
-
-#ifndef RATESANALYSIS_EXAMPLERATESEMULATION_H
-#define RATESANALYSIS_EXAMPLERATESEMULATION_H 1
-
-#include "RatesAnalysis/RatesAnalysisAlg.h"
-
-class ExampleRatesEmulation: public ::RatesAnalysisAlg { 
- public: 
-  ExampleRatesEmulation( const std::string& name, ISvcLocator* pSvcLocator );
-  virtual ~ExampleRatesEmulation(); 
-
-  virtual StatusCode  ratesInitialize() override;
-  virtual StatusCode  ratesExecute() override;
-  virtual StatusCode  ratesFinalize() override;
-
- private:
-
-  float m_lumi; //!< Targer inst. luminosity
-
-}; 
-
-#endif //> !RATESANALYSIS_EXAMPLERATESEMULATION_H
diff --git a/Trigger/TrigCost/RatesAnalysisExamplesXAOD/src/ExampleRatesFullMenu.h b/Trigger/TrigCost/RatesAnalysisExamplesXAOD/src/ExampleRatesFullMenu.h
deleted file mode 100644
index b257522b6640caadbffa60ad913c9787da112f4d..0000000000000000000000000000000000000000
--- a/Trigger/TrigCost/RatesAnalysisExamplesXAOD/src/ExampleRatesFullMenu.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
-  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
-*/
-
-#ifndef RATESANALYSIS_EXAMPLERATESFULLMENU_H
-#define RATESANALYSIS_EXAMPLERATESFULLMENU_H 1
-
-#include "RatesAnalysis/RatesAnalysisAlg.h"
-
-class ExampleRatesFullMenu: public ::RatesAnalysisAlg { 
- public: 
-  ExampleRatesFullMenu( const std::string& name, ISvcLocator* pSvcLocator );
-  virtual ~ExampleRatesFullMenu(); 
-
-  virtual StatusCode  ratesInitialize() override;
-  virtual StatusCode  ratesExecute() override;
-  virtual StatusCode  ratesFinalize() override;
-
- private:
-
-  float m_lumi; //!< Targer inst. luminosity
-
-}; 
-
-#endif //> !RATESANALYSIS_EXAMPLERATESFULLMENU_H
diff --git a/Trigger/TrigCost/RatesAnalysisExamplesXAOD/src/components/RatesAnalysis_entries.cxx b/Trigger/TrigCost/RatesAnalysisExamplesXAOD/src/components/RatesAnalysis_entries.cxx
deleted file mode 100644
index 3470edc0e8668ce08331111c142921d33f273983..0000000000000000000000000000000000000000
--- a/Trigger/TrigCost/RatesAnalysisExamplesXAOD/src/components/RatesAnalysis_entries.cxx
+++ /dev/null
@@ -1,9 +0,0 @@
-/*
-  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
-*/
-
-#include "../ExampleRatesEmulation.h"
-#include "../ExampleRatesFullMenu.h"
-
-DECLARE_COMPONENT( ExampleRatesEmulation )
-DECLARE_COMPONENT( ExampleRatesFullMenu )