diff --git a/Trigger/TrigCost/TrigCostAnalysis/src/CostData.cxx b/Trigger/TrigCost/TrigCostAnalysis/src/CostData.cxx
index 65d96da865ea12493c6e6586438d14441f028dac..d40bf149f5a97c80fa51ba5156def855eeb90c03 100644
--- a/Trigger/TrigCost/TrigCostAnalysis/src/CostData.cxx
+++ b/Trigger/TrigCost/TrigCostAnalysis/src/CostData.cxx
@@ -46,8 +46,8 @@ StatusCode CostData::cache() {
   return StatusCode::SUCCESS;
 }
 
-void CostData::setRosToRobMap(const std::map<std::string, std::vector<uint32_t>>& rosToRobMap) {
-  m_rosToRob = &rosToRobMap;
+void CostData::setCostROSData(const CostROSData& costROSData) {
+  m_costROSData = &costROSData;
 }
 
 void CostData::setLb(uint32_t lb) {
@@ -84,8 +84,8 @@ const xAOD::TrigCompositeContainer& CostData::rosCollection() const {
   return *m_rosCollection;
 }
 
-const std::map<std::string, std::vector<uint32_t>>& CostData::rosToRobMap() const {
-  return *m_rosToRob;
+const CostROSData& CostData::costROSData() const {
+  return *m_costROSData;
 }
 
 const std::map<size_t, std::vector<size_t>>& CostData::algToRequestMap() const {
diff --git a/Trigger/TrigCost/TrigCostAnalysis/src/CostData.h b/Trigger/TrigCost/TrigCostAnalysis/src/CostData.h
index 6df7dd102ae77c3c30540fd5f168f06f641cb599..dd91192c123282f4814eda0da50d13272a65aa47 100644
--- a/Trigger/TrigCost/TrigCostAnalysis/src/CostData.h
+++ b/Trigger/TrigCost/TrigCostAnalysis/src/CostData.h
@@ -10,6 +10,8 @@
 #include "TrigConfData/HLTChain.h"
 #include "TrigCompositeUtils/AlgToChainTool.h"
 
+#include "CostROSData.h"
+
 #include <map>
 #include <vector>
 
@@ -61,12 +63,12 @@ class CostData {
     /**
      * @brief Getter of the ROS to ROB map.
      */
-    const std::map<std::string, std::vector<uint32_t>>& rosToRobMap() const;
+    const CostROSData& costROSData() const;
 
     /**
      * @brief Set ROS to ROB map
      */
-    void setRosToRobMap(const std::map<std::string, std::vector<uint32_t>>& rosToRobMap);
+    void setCostROSData(const CostROSData& costROSData);
 
     /**
      * @brief Getter of the alg name to chains map.
@@ -191,7 +193,7 @@ class CostData {
     bool m_liveTimeIsPerEvent; //!< If the livetime represents a single event or all of the current LB
     const std::unordered_map<uint32_t, std::string>* m_typeMapPtr; //!< Cached non-owning pointer mapping algorithm instance names to types
     std::map<size_t, std::vector<size_t>> m_algToRos; //!< Mapping of indexes from m_costCollection to corresponding ROS requests made by algorithm
-    const std::map<std::string, std::vector<uint32_t>>* m_rosToRob = nullptr; //!< Mapping of ROS corresponding to ROB requests
+    const CostROSData* m_costROSData = nullptr; //!< Helper class to store ROS to ROB mapping
     const std::map<std::string, std::set<size_t>>* m_chainToAlgIdx = nullptr; //!<Mapping of chain to algorithms idx
     const std::map<std::string, std::set<size_t>>* m_chainToUniqAlgIdx = nullptr; //!<Mapping of chain name to its unique algorithms
     const std::map<std::string, std::map<int16_t, std::set<size_t>>>* m_sequencers = nullptr; //!<Mapping of sequence to algorithms
diff --git a/Trigger/TrigCost/TrigCostAnalysis/src/CostROSData.cxx b/Trigger/TrigCost/TrigCostAnalysis/src/CostROSData.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..eb76d7df6f932899cf3456fa8ad89b1414afdcb2
--- /dev/null
+++ b/Trigger/TrigCost/TrigCostAnalysis/src/CostROSData.cxx
@@ -0,0 +1,70 @@
+/*
+  Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "CostROSData.h"
+
+
+void CostROSData::initialize(const std::map<std::string, std::vector<uint32_t>>& rosToRobMap) {
+
+    unsigned rosCounter = 0;
+
+    for (const auto& rosRequest : rosToRobMap) {
+      for (uint32_t robId : rosRequest.second) {
+        m_robToRos[robId] = rosRequest.first;
+      }
+      m_rosIdToBin[rosRequest.first] = rosCounter;
+      ++rosCounter;
+    }
+
+    m_rosToRob = rosToRobMap;
+
+    m_msgStream.reset(new MsgStream(nullptr, "CostROSData"));
+}
+
+
+int CostROSData::getBinForROS(const std::string& rosName) const {
+    try {
+        return m_rosIdToBin.at(rosName);
+    } catch (const std::out_of_range& e) {
+        ATH_MSG_DEBUG("Bin for ROS " << rosName << " not found");
+        return -1;
+    }
+}
+
+
+std::string CostROSData::getROSForROB(uint32_t robId) const {
+    try {
+        return m_robToRos.at(robId);
+    } catch (const std::out_of_range& e) {
+        ATH_MSG_DEBUG("ROS for ROB " << getROBName(robId) << " not found");
+        return "";
+    }    
+}
+
+std::vector<uint32_t> CostROSData::getROBForROS(const std::string& rosName) const {
+    try {
+        return m_rosToRob.at(rosName);
+    } catch (const std::out_of_range& e) {
+        ATH_MSG_DEBUG("ROBs for ROS " << rosName << " not found");
+        return std::vector<uint32_t>();
+    }  
+}
+
+std::string CostROSData::getROBName(uint32_t robId) const {
+    std::stringstream robName;
+    robName << "0x" << std::hex << robId;
+    return robName.str();
+}
+
+MsgStream& CostROSData::msg() const {
+  return *m_msgStream;
+}
+
+MsgStream& CostROSData::msg(const MSG::Level lvl) const {
+  return *m_msgStream << lvl;
+}
+
+bool CostROSData::msgLvl(const MSG::Level lvl) const {
+  return lvl >= m_msgStream->level();
+}
\ No newline at end of file
diff --git a/Trigger/TrigCost/TrigCostAnalysis/src/CostROSData.h b/Trigger/TrigCost/TrigCostAnalysis/src/CostROSData.h
new file mode 100644
index 0000000000000000000000000000000000000000..a49da140ce253429035588396149ba05b86b2659
--- /dev/null
+++ b/Trigger/TrigCost/TrigCostAnalysis/src/CostROSData.h
@@ -0,0 +1,106 @@
+/*
+  Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGCOSTANALYSIS_COSTROSDATA_H
+#define TRIGCOSTANALYSIS_COSTROSDATA_H 1
+
+#include "AsgMessaging/MsgStream.h"
+#include "AthenaBaseComps/AthMsgStreamMacros.h"
+
+#include <boost/thread/tss.hpp>
+
+#include <map>
+#include <vector>
+
+/**
+ * @class CostROSData
+ * @brief Caches and propagates event data to be used by monitoring algorithms.
+ *
+ * The cache is created on passed rosToRob map 
+ *
+*/
+
+class CostROSData{
+  public:
+
+    /**
+     * @brief Create object based on ROS to ROB mapping
+     */
+    void initialize(const std::map<std::string, std::vector<uint32_t>>& rosToRobMap);
+
+    /**
+     * @brief Return cached bin for given ROS name
+     * @param[in] rosName ROS name
+     * @return Bin number or -1 if not found
+     */
+    int getBinForROS(const std::string& rosName) const;
+
+    /**
+     * @brief Return ROS name for given ROB
+     * @param[in] robId ROB id 
+     * @return ROS name
+     */
+    std::string getROSForROB(uint32_t robId) const;
+
+    /**
+     * @brief Return list of ROBs name for given ROS
+     * @param[in] rosName ROS name
+     * @return List of ROBs
+     */
+    std::vector<uint32_t> getROBForROS(const std::string& rosName) const;
+
+    /**
+     * @brief Create ROB name in hex string format
+     * @param[in] robId ROB id
+     * @return ROB id saved in hexadecimal representation
+     */
+    std::string getROBName(uint32_t robId) const;
+
+    /**
+     * @brief Return number of saved unique ROSes
+     * @return Number of ROSes
+     */
+    unsigned getNROS() const {return m_rosIdToBin.size();}
+
+    /**
+     * @brief Return ROS name to ROB ids map
+     * @return ROS name to ROB ids map
+     */
+    const std::map<std::string, std::vector<uint32_t>>& getROStoROBMap() const {return m_rosToRob;}
+
+    /**
+     * @brief Return ROB id to ROS name map
+     * @return ROB id to ROS name map
+     */
+    const std::map<uint32_t, std::string>& getROBtoROSMap() const {return m_robToRos;}
+
+    /**
+     * @brief Logging
+     * @return Message stream reference.
+     */  
+    MsgStream& msg() const;
+
+    /**
+     * @brief Logging on a given level
+     * @param[in] lvl Verbosity level
+     * @return Message stream reference.
+     */ 
+    MsgStream& msg(const MSG::Level lvl) const;
+
+    /**
+     * @brief Returns if requested level is same or higher than logging level
+     * @param[in] lvl Verbosity level
+     * @return If requested level is same or higher than logging level
+     */ 
+    bool msgLvl(const MSG::Level lvl) const;
+
+  private:
+    std::map<std::string, std::vector<uint32_t>> m_rosToRob ; //!< Mapping of ROS corresponding to ROB requests
+    std::map<uint32_t, std::string> m_robToRos; //!< Mapping of ROB corresponding to ROS
+    std::map<std::string, int> m_rosIdToBin;  //!< Cached mapping of ros id to bin in ROS histograms
+    
+    mutable boost::thread_specific_ptr<MsgStream> m_msgStream;
+};
+
+#endif // TRIGCOSTANALYSIS_COSTROSDATA_H
\ No newline at end of file
diff --git a/Trigger/TrigCost/TrigCostAnalysis/src/CounterBase.cxx b/Trigger/TrigCost/TrigCostAnalysis/src/CounterBase.cxx
index 07ea9fcccfd74953bf1f523d1b45779a73213d6d..08dd97675bf42e1c44226dc198bfb8aed6e5d244 100644
--- a/Trigger/TrigCost/TrigCostAnalysis/src/CounterBase.cxx
+++ b/Trigger/TrigCost/TrigCostAnalysis/src/CounterBase.cxx
@@ -7,6 +7,7 @@
 #include "CounterBase.h"
 #include "TH1F.h"
 #include "TH2F.h"
+#include "TProfile.h"
 
 CounterBase::CounterBase(const std::string& name, const MonitorBase* parent) 
   : m_name(name), m_parent(parent) {
@@ -64,6 +65,47 @@ void CounterBase::regHistogram(const std::string& name,
   // Histogram is now owned by THistSvc. A cache of the ptr is kept in the Variable
 }
 
+void CounterBase::regTProfile(const std::string& name, 
+  const std::string& title,
+  const VariableType type,
+  const LogType xaxis, 
+  const float min, 
+  const float max,
+  const size_t bins) 
+{
+  std::string hisSvcName = getParent()->getParent()->getName() + "_" + getParent()->getName() + "_" + getName() + "_" + name;
+  std::unique_ptr<TProfile> hist;
+
+  if (max <= min || bins == 0) {
+    throw std::runtime_error("CounterBase::regTProfile: Cannot have max <= min or bins == 0");
+  }
+
+  if (xaxis == kLinear) {
+    hist = std::make_unique<TProfile>(hisSvcName.c_str(), title.c_str(), bins, min, max);
+  } else if (xaxis == kLog) {
+    if (min <= 0) {
+      throw std::runtime_error("CounterBase::regTProfile: Cannot have min <= 0 with log binning");
+    }
+    std::unique_ptr<double[]> xbins = std::make_unique<double[]>(bins+1);
+    const double xlogmin = log10(min);
+    const double xlogmax = log10(max);
+    const double dlogx   = (xlogmax-xlogmin)/((double)bins);
+    for (size_t i = 0; i <= bins; ++i) { 
+      const double xlog = xlogmin + i*dlogx;
+      xbins[i] = exp( log(10) * xlog ); 
+    }
+    hist = std::make_unique<TProfile>(hisSvcName.c_str(), title.c_str(), bins, xbins.get());
+  } else {
+    throw std::runtime_error("CounterBase::regTProfile: Unknown logarithm flag");
+  }
+
+  m_variables.emplace(std::piecewise_construct,
+    std::forward_as_tuple(name),
+    std::forward_as_tuple(name, bookGetPointer(hist.release()), type)
+  );
+  // Histogram is now owned by THistSvc. A cache of the ptr is kept in the Variable
+}
+
 void CounterBase::regHistogram(const std::string& name, 
   const std::string& title,
   const VariableType type,
@@ -133,6 +175,10 @@ void CounterBase::regHistogram(const std::string& name,
   // Histogram is now owned by THistSvc. A cache of the ptr is kept in the Variable
 }
 
+bool CounterBase::variableExists(const std::string& name) const {
+  return (m_variables.count(name) == 1);
+}
+
 Variable& CounterBase::getVariable(const std::string& name) {
   auto it = m_variables.find(name);
   if (it == m_variables.end()) {
diff --git a/Trigger/TrigCost/TrigCostAnalysis/src/CounterBase.h b/Trigger/TrigCost/TrigCostAnalysis/src/CounterBase.h
index c02c3936519a5e005d91ae78d1889832e4fec31a..d74cf21a042fac2fdd27b0a1b0b144991a789f74 100644
--- a/Trigger/TrigCost/TrigCostAnalysis/src/CounterBase.h
+++ b/Trigger/TrigCost/TrigCostAnalysis/src/CounterBase.h
@@ -63,6 +63,13 @@ class CounterBase {
      */
     const MonitorBase* getParent() const;
 
+    /**
+     * @brief Check if a variable of a given name exists.
+     * @param[in] name Name of Variable.
+     * @return True if variable already exists.  
+     */
+    bool variableExists(const std::string& name) const;
+
     /**
      * @brief Returns a mutable reference to a named Variable. Throws if no such Variable exists.
      * Used when more complicated logic is needed on a Variable than simply using the fill() method.
@@ -134,6 +141,22 @@ class CounterBase {
       const float max = 1000000.,
       const size_t bins = 70);
 
+    /**
+     * @brief Book a TProfile for this Counter, to be filled in per-event monitoring.
+     * @param[in] name The name of the histogram (and corresponding Variable)
+     * @param[in] title ROOT histogram title string in format "title;xaxis;yaxis"
+     * @param[in] xaxis Controls if the x-axis should use fixed-width or logarithmic bin boundaries.
+     * @param[in] min X-axis minimum bound.
+     * @param[in] max X-axis maximum bound.
+     * @param[in] bins Number of histogram bins.
+     */
+    void regTProfile(const std::string& name,
+      const std::string& title, 
+      const VariableType type = VariableType::kPerCall, 
+      const LogType xaxis = kLog,
+      const float min = 0.1, 
+      const float max = 1000000.,
+      const size_t bins = 70);
 
     /**
      * @brief Book a 2D histogram for this Counter, to be filled in per-event monitoring.
diff --git a/Trigger/TrigCost/TrigCostAnalysis/src/TrigCostAnalysis.cxx b/Trigger/TrigCost/TrigCostAnalysis/src/TrigCostAnalysis.cxx
index cd560a76f08ddd2803da36be080df973eaf80483..a8f9979f76fec488d51d4ed0fbc9f5d79dc54d36 100644
--- a/Trigger/TrigCost/TrigCostAnalysis/src/TrigCostAnalysis.cxx
+++ b/Trigger/TrigCost/TrigCostAnalysis/src/TrigCostAnalysis.cxx
@@ -62,6 +62,8 @@ StatusCode  TrigCostAnalysis::initialize() {
       TrigConf::HLTUtils::file2hashes(hashFile);
     }
   }
+
+  m_costROSData.initialize(m_rosToRob);
   
   ATH_CHECK( m_histSvc->regTree("/COSTSTREAM/metadata", std::make_unique<TTree>("metadata", "metadata")) );
   ATH_CHECK( m_histSvc->getTree("/COSTSTREAM/metadata", m_metadataTree) );
@@ -272,7 +274,7 @@ StatusCode TrigCostAnalysis::execute() {
   const uint32_t onlineSlot = getOnlineSlot( costDataHandle.get() );
   CostData costData;
   ATH_CHECK( costData.set(costDataHandle.get(), rosDataHandle.get(), onlineSlot) );
-  costData.setRosToRobMap(m_rosToRob);
+  costData.setCostROSData(m_costROSData);
   costData.setChainToAlgMap(chainToAlgIdx);
   costData.setChainToUniqAlgMap(chainToUniqAlgs);
   costData.setSequencersMap(seqToAlgIdx);
diff --git a/Trigger/TrigCost/TrigCostAnalysis/src/TrigCostAnalysis.h b/Trigger/TrigCost/TrigCostAnalysis/src/TrigCostAnalysis.h
index a63b4706310668834f8664e4d59f97cde85a6c83..8363e592f4cbae1e581a84ad1dc6f386ac3d0b7f 100644
--- a/Trigger/TrigCost/TrigCostAnalysis/src/TrigCostAnalysis.h
+++ b/Trigger/TrigCost/TrigCostAnalysis/src/TrigCostAnalysis.h
@@ -17,6 +17,7 @@
 #include "GaudiKernel/ITHistSvc.h"
 
 #include "MonitoredRange.h"
+#include "CostROSData.h"
 
 #include <unordered_map>
 #include <mutex>
@@ -218,6 +219,7 @@ class TrigCostAnalysis: public ::AthAlgorithm {
     std::mutex m_addHostnameMutex; //!< Mutex to update set below
     mutable std::set<std::string> m_hostnames ATLAS_THREAD_SAFE; //!< Save unique hostnames for the run
 
+    CostROSData m_costROSData; //!< Cached CostROSData class with details needed for ROS monitoring
 }; 
 
 #endif // TRIGCOSTANALYSIS_TRIGCOSTALYSIS_H
diff --git a/Trigger/TrigCost/TrigCostAnalysis/src/Variable.cxx b/Trigger/TrigCost/TrigCostAnalysis/src/Variable.cxx
index f4fa5ba45c1c1e259df1dcf314f65694d3b637a3..9a5fa301f9925291ca54b9ac36dab07f823af2c3 100644
--- a/Trigger/TrigCost/TrigCostAnalysis/src/Variable.cxx
+++ b/Trigger/TrigCost/TrigCostAnalysis/src/Variable.cxx
@@ -7,6 +7,7 @@
 #include "Variable.h"
 #include "TH1F.h"
 #include "TH2F.h"
+#include "TProfile.h"
 
 Variable::Variable(const std::string& name, TH1* cacheHistoPtr, VariableType type) :
   m_name(name),
@@ -67,9 +68,17 @@ StatusCode Variable::fill(float value, float weight) {
 StatusCode Variable::fill(float xvalue, float yvalue, float weight) {
   switch(m_variableType) {
     case kPerCall:
+    {
       ATH_CHECK(m_cacheHistoPtr != nullptr);
-      dynamic_cast<TH2F*>(m_cacheHistoPtr)->Fill(xvalue * m_oneOverDenominator, yvalue * m_oneOverDenominator, weight);
+      TH2F* th2f = dynamic_cast<TH2F*>(m_cacheHistoPtr);
+      if (th2f == nullptr){
+        // Cast failed - should be TProfile
+        dynamic_cast<TProfile*>(m_cacheHistoPtr)->Fill(xvalue * m_oneOverDenominator, yvalue * m_oneOverDenominator, weight);
+      } else {
+        th2f->Fill(xvalue * m_oneOverDenominator, yvalue * m_oneOverDenominator, weight);
+      }
       break;
+    }
     case kPerEvent:
       ++m_calls;
       m_xaccumulator += xvalue;
@@ -102,12 +111,24 @@ StatusCode Variable::setBinLabel(int bin, const std::string& label) {
 }
 
 
+StatusCode Variable::setYBinLabel(int bin, const std::string& label) {
+  m_cacheHistoPtr->GetYaxis()->SetBinLabel(bin, label.c_str());
+  return StatusCode::SUCCESS;
+}
+
+
 StatusCode Variable::endEvent() {
   if (m_variableType == kPerEvent && m_calls > 0) {
     ATH_CHECK(m_cacheHistoPtr != nullptr);
     // 2D histogram
     if (m_yaccumulator > 0){
-      dynamic_cast<TH2F*>(m_cacheHistoPtr)->Fill(m_xaccumulator * m_oneOverDenominator, m_yaccumulator, m_weight);
+      TH2F* th2f = dynamic_cast<TH2F*>(m_cacheHistoPtr);
+      if (th2f == nullptr){
+        // Cast failed - should be TProfile
+        dynamic_cast<TProfile*>(m_cacheHistoPtr)->Fill(m_xaccumulator * m_oneOverDenominator, m_yaccumulator, m_weight);
+      } else {
+        th2f->Fill(m_xaccumulator * m_oneOverDenominator, m_yaccumulator, m_weight);
+      }
     } else {
       m_cacheHistoPtr->Fill(m_xaccumulator * m_oneOverDenominator, m_weight);
     }
diff --git a/Trigger/TrigCost/TrigCostAnalysis/src/Variable.h b/Trigger/TrigCost/TrigCostAnalysis/src/Variable.h
index 5101aedafa650267bdd76883131a040cc6756d8c..db14619afe0b22d8d6da9c2912e0d09c5bd1122a 100644
--- a/Trigger/TrigCost/TrigCostAnalysis/src/Variable.h
+++ b/Trigger/TrigCost/TrigCostAnalysis/src/Variable.h
@@ -118,7 +118,14 @@ class Variable {
      * @param[in] label Label to set
      */
     StatusCode setBinLabel(int bin, const std::string& label);
-  
+
+    /**
+     * @brief Set label on given bin in cached histogram on y axis
+     * @param[in] bin Bin number
+     * @param[in] label Label to set
+     */
+    StatusCode setYBinLabel(int bin, const std::string& label);
+
     /**
      * @brief Sets, until the end of the event, a denominator which will be used to normalise every Fill.
      * @pram[in] value The denominator to normalise Fill operations.
diff --git a/Trigger/TrigCost/TrigCostAnalysis/src/counters/CounterChain.cxx b/Trigger/TrigCost/TrigCostAnalysis/src/counters/CounterChain.cxx
index 64a988cfb4788bdbf09b600931af95f30b4b547d..4b01b2140ee98bc47b18a79829a66320013fd04e 100644
--- a/Trigger/TrigCost/TrigCostAnalysis/src/counters/CounterChain.cxx
+++ b/Trigger/TrigCost/TrigCostAnalysis/src/counters/CounterChain.cxx
@@ -9,7 +9,7 @@
 
 
 CounterChain::CounterChain(const std::string& name, const MonitorBase* parent) 
-  : CounterBase(name, parent)
+  : CounterBase(name, parent), m_isInitialized(false)
 {
   regHistogram("Group_perCall", "Chain group/Call;Group;Calls", VariableType::kPerCall, kLinear, -0.5, 9.5, 10);
   regHistogram("Chain_perEvent", "Chain calls/Event;Chain call;Events", VariableType::kPerEvent, kLinear, -0.5, 49.5);
@@ -25,17 +25,37 @@ CounterChain::CounterChain(const std::string& name, const MonitorBase* parent)
   regHistogram("RequestTime_perEvent", "ROB Elapsed Time/Event;Elapsed Time [ms];Events", VariableType::kPerEvent);
 }
 
+CounterChain::CounterChain(const std::string& name, unsigned nRos, const MonitorBase* parent) 
+  : CounterChain(name, parent)
+{
+  regTProfile("ROSRequests_perEvent", "Number of ROS requests;ROS names;Numer of requests to ROS per event", VariableType::kPerCall, LogType::kLinear, 0, nRos, nRos);
+  regTProfile("ROBRequestsPerROS_perEvent", "Number of ROBs per ROS requests;ROS names;Numer of ROBs requested per request", VariableType::kPerCall, LogType::kLinear, 0, nRos, nRos);
+  regTProfile("ROBRequestsPerROSPerEvent_perEvent", "Number of ROBs per ROS requests per event;ROS names;Numer of ROBs requested by ROS per event", VariableType::kPerCall, LogType::kLinear, 0, nRos, nRos);
+}
+
 
 StatusCode CounterChain::newEvent(const CostData& data, size_t index, const float weight) {
 
   ATH_CHECK( increment("Chain_perEvent", weight) );
 
-  // Fill the bins with groups and add the labels
-  int bin = 1;
-  for (const std::string& group : data.seededChains()[index].groups){
-    ATH_CHECK( getVariable("Group_perCall").setBinLabel(bin, group) );
-    ATH_CHECK( getVariable("Group_perCall").fill(group, weight) );
-    ++bin;
+  if (!m_isInitialized && variableExists("ROSRequests_perEvent")) {
+    // Set histograms labels
+    for (const auto& rosToRobPair : data.costROSData().getROStoROBMap()) {
+      int binForROS = data.costROSData().getBinForROS(rosToRobPair.first) + 1;
+      ATH_CHECK( getVariable("ROSRequests_perEvent").setBinLabel(binForROS, rosToRobPair.first));
+      ATH_CHECK( getVariable("ROBRequestsPerROS_perEvent").setBinLabel(binForROS, rosToRobPair.first));
+      ATH_CHECK( getVariable("ROBRequestsPerROSPerEvent_perEvent").setBinLabel(binForROS, rosToRobPair.first));
+    }
+
+    // Fill the bins with groups and add the labels
+    int bin = 1;
+    for (const std::string& group : data.seededChains()[index].groups){
+      ATH_CHECK( getVariable("Group_perCall").setBinLabel(bin, group) );
+      ATH_CHECK( getVariable("Group_perCall").fill(group, weight) );
+      ++bin;
+    }
+
+    m_isInitialized = true;
   }
 
   if (data.seededChains()[index].isPassRaw){
@@ -45,6 +65,8 @@ StatusCode CounterChain::newEvent(const CostData& data, size_t index, const floa
   // Monitor algorithms associated with chain name
   if (!data.chainToAlgMap().count(getName())) return StatusCode::SUCCESS;
 
+  std::map<std::string, int> nRosPerEvent; // Accumulate how many times ROS was requested in a request per this event
+  std::map<std::string, int> nRobsPerRosPerEvent; // Accumulate how many ROBs ROS requested per this event
   for (const size_t algIndex : data.chainToAlgMap().at(getName())){
     const xAOD::TrigComposite* alg = data.costCollection().at(algIndex);
     const uint32_t slot = alg->getDetail<uint32_t>("slot");
@@ -65,10 +87,14 @@ StatusCode CounterChain::newEvent(const CostData& data, size_t index, const floa
 
     for (size_t requestIdx : data.algToRequestMap().at(algIndex)) {
       const xAOD::TrigComposite* request = data.rosCollection().at(requestIdx);
+      const std::vector<uint32_t> robIdsPerRequest = request->getDetail<std::vector<uint32_t>>("robs_id");
       const std::vector<unsigned> robs_history = request->getDetail<std::vector<unsigned>>("robs_history");
       const std::vector<uint32_t> robs_size = request->getDetail<std::vector<uint32_t>>("robs_size");
 
+      std::map<std::string, int> nRobsPerRosPerRequest; // Accumulate how many ROBs ROS requested per request
+
       bool networkRequestIncremented = false;
+      std::set<std::string> requestedROSes;
       for (size_t i = 0; i < robs_size.size(); ++i) {
         // ROB request was fetched over the network
         if (robs_history[i] == robmonitor::RETRIEVED) {
@@ -80,8 +106,28 @@ StatusCode CounterChain::newEvent(const CostData& data, size_t index, const floa
         else if (robs_history[i] == robmonitor::HLT_CACHED || robs_history[i] == robmonitor::DCM_CACHED) {
           ATH_CHECK( fill("CachedROBSize_perEvent", robs_size[i] / 500., weight) );
         }
+
+        uint32_t robId = robIdsPerRequest[i];
+        if (variableExists("ROBRequestsPerROS_perEvent")){
+          std::string rosForROB = data.costROSData().getROSForROB(robId);
+          if (!rosForROB.empty()){
+            requestedROSes.insert(rosForROB);
+            nRobsPerRosPerRequest[rosForROB] += 1;
+            nRobsPerRosPerEvent[rosForROB] += 1;
+          }
+        }
+      }
+
+      // Save number of ROBs per ROS per request
+      for (const auto& robPerRosPair : nRobsPerRosPerRequest) {
+        ATH_CHECK( fill("ROBRequestsPerROS_perEvent", data.costROSData().getBinForROS(robPerRosPair.first), robPerRosPair.second, weight) );
       }
 
+      // Save the requested ROSes per request
+      for (const std::string& rosName : requestedROSes){
+          nRosPerEvent[rosName] += 1;
+      }
+    
       ATH_CHECK( increment("Request_perEvent", weight) );
 
       if (networkRequestIncremented) {
@@ -93,6 +139,16 @@ StatusCode CounterChain::newEvent(const CostData& data, size_t index, const floa
     }
   }
 
+  // Save the requested ROSes per event
+  for (const auto& robPerRosPair : nRosPerEvent) {
+    ATH_CHECK( fill("ROSRequests_perEvent", data.costROSData().getBinForROS(robPerRosPair.first), robPerRosPair.second, weight) );
+  }
+
+  // Save the number of ROBs per ROS per event
+  for (const auto& robPerRosPair : nRobsPerRosPerEvent) {
+    ATH_CHECK( fill("ROBRequestsPerROSPerEvent_perEvent", data.costROSData().getBinForROS(robPerRosPair.first), robPerRosPair.second, weight) );
+  }
+
   // Monitor unique algorithms associated with chain name
   if (!data.chainToUniqAlgMap().count(getName())) return StatusCode::SUCCESS;
 
diff --git a/Trigger/TrigCost/TrigCostAnalysis/src/counters/CounterChain.h b/Trigger/TrigCost/TrigCostAnalysis/src/counters/CounterChain.h
index ebdeaddf2fb867490f4a927f36f73d7a1d14919d..32b604ac6d30290fd71f4c74f8ab518632fe6029 100644
--- a/Trigger/TrigCost/TrigCostAnalysis/src/counters/CounterChain.h
+++ b/Trigger/TrigCost/TrigCostAnalysis/src/counters/CounterChain.h
@@ -25,6 +25,14 @@ class CounterChain : public CounterBase {
      */
     CounterChain(const std::string& name, const MonitorBase* parent);
 
+    /**
+     * @brief Construct counter.
+     * @param[in] name Counter's name
+     * @param[in] nRos Number of possible ROSes
+     * @param[in] parent Counter's parent monitor, cached non-owning pointer.
+     */
+    CounterChain(const std::string& name, unsigned nRos, const MonitorBase* parent);
+
     /**
      * @brief Default destructor.
      */
@@ -47,6 +55,9 @@ class CounterChain : public CounterBase {
      * @param[in] weight Global event weight
      */
     virtual StatusCode newEvent(const CostData& data, size_t index, const float weight = 1.) override;
+
+  private:
+    bool m_isInitialized;
 };
 
 #endif // TRIGCOSTANALYSIS_COUNTERALGORITHM_H
diff --git a/Trigger/TrigCost/TrigCostAnalysis/src/counters/CounterROS.cxx b/Trigger/TrigCost/TrigCostAnalysis/src/counters/CounterROS.cxx
index 540ba26d8f830246e8fa27a8fc641d45f6bba4b1..14716748d649b9151eff5050b3bf459a4721d915 100644
--- a/Trigger/TrigCost/TrigCostAnalysis/src/counters/CounterROS.cxx
+++ b/Trigger/TrigCost/TrigCostAnalysis/src/counters/CounterROS.cxx
@@ -22,6 +22,12 @@ CounterROS::CounterROS(const std::string& name, const MonitorBase* parent)
   regHistogram("ROBStatus_perCall", "ROB status/Call;Status;Events", VariableType::kPerCall, LogType::kLinear, 0, robmonitor::NUM_ROBHIST_CODES+1, robmonitor::NUM_ROBHIST_CODES+1);
 }
 
+CounterROS::CounterROS(const std::string& name, unsigned nRobs, const MonitorBase* parent) 
+  : CounterROS(name, parent) {
+
+  regHistogram("ROBsPerRequest_perCall", "Number of ROB requests;ROBs names;Number of requests", VariableType::kPerCall, LogType::kLinear, 0, nRobs, nRobs);
+}
+
 StatusCode CounterROS::newEvent(const CostData& data, size_t index, const float weight) {
 
   // Monitor only ROB data for corresponding ROS
@@ -31,23 +37,34 @@ StatusCode CounterROS::newEvent(const CostData& data, size_t index, const float
   const std::vector<unsigned> robs_history = tc->getDetail<std::vector<unsigned>>("robs_history");
   const std::vector<unsigned short> robs_status = tc->getDetail<std::vector<unsigned short>>("robs_status");
 
-  if (m_robIdsPerROS.size() == 0) {
-    m_robIdsPerROS = data.rosToRobMap().at(getName());
-  }
 
-  // Set lables of status histogram
-  ATH_CHECK( getVariable("ROBStatus_perCall").setBinLabel(1, "Unclassified"));
-  ATH_CHECK( getVariable("ROBStatus_perCall").setBinLabel(2, "Retrieved"));
-  ATH_CHECK( getVariable("ROBStatus_perCall").setBinLabel(3, "HLT Cached"));
-  ATH_CHECK( getVariable("ROBStatus_perCall").setBinLabel(4, "DCM Cached"));
-  ATH_CHECK( getVariable("ROBStatus_perCall").setBinLabel(5, "Ignored"));
-  ATH_CHECK( getVariable("ROBStatus_perCall").setBinLabel(6, "Disabled"));
-  ATH_CHECK( getVariable("ROBStatus_perCall").setBinLabel(7, "IsNotOK"));
+  if (m_robIdToBin.empty()) {
+    // Set lables of status histogram
+    ATH_CHECK( getVariable("ROBStatus_perCall").setBinLabel(1, "Unclassified"));
+    ATH_CHECK( getVariable("ROBStatus_perCall").setBinLabel(2, "Retrieved"));
+    ATH_CHECK( getVariable("ROBStatus_perCall").setBinLabel(3, "HLT Cached"));
+    ATH_CHECK( getVariable("ROBStatus_perCall").setBinLabel(4, "DCM Cached"));
+    ATH_CHECK( getVariable("ROBStatus_perCall").setBinLabel(5, "Ignored"));
+    ATH_CHECK( getVariable("ROBStatus_perCall").setBinLabel(6, "Disabled"));
+    ATH_CHECK( getVariable("ROBStatus_perCall").setBinLabel(7, "IsNotOK"));
+
+    if (variableExists("ROBsPerRequest_perCall")) {
+      // This monitor has it's own binning for ROBs due to the fact that limited number of ROBs are associated with one ROS
+      unsigned robCounter = 0;
+      for (uint32_t robId : data.costROSData().getROBForROS(getName())) {
+        std::string robName = data.costROSData().getROBName(robId);
+        ATH_CHECK( getVariable("ROBsPerRequest_perCall").setBinLabel(robCounter+1, robName));
+
+        m_robIdToBin[robId] = robCounter;
+        ++robCounter;
+      }
+    }
+  }
 
   // Find all ROB requests that are both in request and correspond to this ROS
   bool networkRequestIncremented = false;
   for (size_t i = 0; i < robIdsPerRequest.size(); ++i) {
-    if (std::find(m_robIdsPerROS.begin(), m_robIdsPerROS.end(), robIdsPerRequest[i]) != m_robIdsPerROS.end()) {
+    if (m_robIdToBin.find(robIdsPerRequest[i]) != m_robIdToBin.end()) {
       ATH_CHECK( fill("ROBStatus_perCall", getROBHistoryBin(robs_history[i]), weight) );
       // Status is ok when no status words are set
       if (robs_status[i] != 0) {
@@ -64,6 +81,10 @@ StatusCode CounterROS::newEvent(const CostData& data, size_t index, const float
       else if (robs_history[i] == robmonitor::HLT_CACHED || robs_history[i] == robmonitor::DCM_CACHED) {
         ATH_CHECK( fill("CachedROBSize_perEvent", robs_size[i] / 500., weight) );
       }
+
+      if (variableExists("ROBsPerRequest_perCall")){
+        ATH_CHECK( fill("ROBsPerRequest_perCall", m_robIdToBin.at(robIdsPerRequest[i]), weight) );
+      }
     }
   }
 
diff --git a/Trigger/TrigCost/TrigCostAnalysis/src/counters/CounterROS.h b/Trigger/TrigCost/TrigCostAnalysis/src/counters/CounterROS.h
index 88115a3f27a54eda87d1d6e2685971b421a4d7d0..b9fffb09e2a155ad91860da2785dca47af1621f7 100644
--- a/Trigger/TrigCost/TrigCostAnalysis/src/counters/CounterROS.h
+++ b/Trigger/TrigCost/TrigCostAnalysis/src/counters/CounterROS.h
@@ -26,6 +26,14 @@ class CounterROS : public CounterBase {
      */
     CounterROS(const std::string& name, const MonitorBase* parent);
 
+    /**
+     * @brief Construct counter.
+     * @param[in] name Counter's name
+     * @param[in] nRobs Number of ROBs possible for this ROB
+     * @param[in] parent Counter's parent monitor, cached non-owning pointer.
+     */
+    CounterROS(const std::string& name, unsigned nRobs, const MonitorBase* parent);
+
     /**
      * @brief Default destructor.
      */
@@ -56,7 +64,7 @@ class CounterROS : public CounterBase {
      */
     int getROBHistoryBin(const unsigned history);
 
-    std::vector<uint32_t> m_robIdsPerROS; //!< Cached mapping of ROB ids corresponding to ROS
+    std::map<uint32_t, unsigned> m_robIdToBin; //!< Cached mapping of rob id to bin in ROBsPerRequest_perEvent histogram
 };
 
 #endif // TRIGCOSTANALYSIS_COUNTERROS_H
\ No newline at end of file
diff --git a/Trigger/TrigCost/TrigCostAnalysis/src/monitors/MonitorChain.cxx b/Trigger/TrigCost/TrigCostAnalysis/src/monitors/MonitorChain.cxx
index d05063ed8441a592a9a0fd2ad67c071d2f88f96a..8d6442bd5e6e2c770cd68a514ad5f3b2dedd7248 100644
--- a/Trigger/TrigCost/TrigCostAnalysis/src/monitors/MonitorChain.cxx
+++ b/Trigger/TrigCost/TrigCostAnalysis/src/monitors/MonitorChain.cxx
@@ -13,13 +13,21 @@ StatusCode MonitorChain::newEvent(const CostData& data, const float weight) {
 
   const std::vector<TrigCompositeUtils::AlgToChainTool::ChainInfo>& seededChains = data.seededChains();
   for (size_t i = 0; i < seededChains.size(); ++i){
-    ATH_CHECK( getCounter(seededChains[i].name)->newEvent(data, i, weight) );
+    std::string chainName = seededChains[i].name;
+    if (!counterExists(chainName)){
+      // Create a new counter using specialized constructor in order to pass number of bins for some of the histograms
+      m_counters.insert( std::make_pair(chainName, newCounter(chainName, data.costROSData().getNROS())) );
+    } 
+    ATH_CHECK( getCounter(chainName)->newEvent(data, i, weight) );
   }
 
   return StatusCode::SUCCESS;
 }
 
-
 std::unique_ptr<CounterBase> MonitorChain::newCounter(const std::string& name) {
   return std::make_unique<CounterChain>(name, this);
 } 
+
+std::unique_ptr<CounterBase> MonitorChain::newCounter(const std::string& name, unsigned nROS) {
+  return std::make_unique<CounterChain>(name, nROS, this);
+} 
\ No newline at end of file
diff --git a/Trigger/TrigCost/TrigCostAnalysis/src/monitors/MonitorChain.h b/Trigger/TrigCost/TrigCostAnalysis/src/monitors/MonitorChain.h
index 3fe9643a8718f63525fa2746ba9d765cd4a8bf9a..b014c012f1fa505dcd363023386b86663610ecdb 100644
--- a/Trigger/TrigCost/TrigCostAnalysis/src/monitors/MonitorChain.h
+++ b/Trigger/TrigCost/TrigCostAnalysis/src/monitors/MonitorChain.h
@@ -54,6 +54,14 @@ class MonitorChain : public MonitorBase {
      * @return Owning unique ptr object typed on the CounterBase base class which points to concrete Counter of specialised type.
      */
     virtual std::unique_ptr<CounterBase> newCounter(const std::string& name) override; 
+
+    /**
+     * @brief Concrete counter instantiation. Mints named counter of CounterAlgorith type.
+     * @param[in] name Name of Counter to mint.
+     * @param[in] nROS Number of possible ROB ids to initialize ROB monitoring histograms
+     * @return Owning unique ptr object typed on the CounterBase base class which points to concrete Counter of specialised type.
+     */
+    std::unique_ptr<CounterBase> newCounter(const std::string& name, unsigned nROS);
 };
 
 #endif // TRIGCOSTANALYSIS_MONITORCHAIN_H
diff --git a/Trigger/TrigCost/TrigCostAnalysis/src/monitors/MonitorROS.cxx b/Trigger/TrigCost/TrigCostAnalysis/src/monitors/MonitorROS.cxx
index 660500303245ba0fd806dd9124c3bb5beaeed036..66670f1c906dd1590fa038dbc0b6e03baa173664 100644
--- a/Trigger/TrigCost/TrigCostAnalysis/src/monitors/MonitorROS.cxx
+++ b/Trigger/TrigCost/TrigCostAnalysis/src/monitors/MonitorROS.cxx
@@ -14,16 +14,6 @@ MonitorROS::MonitorROS(const std::string& name, const MonitoredRange* parent)
 
 StatusCode MonitorROS::newEvent(const CostData& data, const float weight) {
 
-  // Prepare ROB id per corresponding ROS name map
-  if (m_robToRos.empty()) {
-    const std::map<std::string, std::vector<uint32_t>>& rosToRobMap = data.rosToRobMap();
-    for (const auto& rosRequest : rosToRobMap) {
-      for (uint32_t robId : rosRequest.second) {
-        m_robToRos[robId] = rosRequest.first;
-      }
-    }
-  }
-
   if (data.rosCollection().empty()){
     ATH_MSG_DEBUG("The ROS collection is empty!");
   }
@@ -33,14 +23,23 @@ StatusCode MonitorROS::newEvent(const CostData& data, const float weight) {
     // Create set of unique ROS for this request
     std::set<std::string> rosPerRequest;
     for (uint32_t robId : robIds) {
-      if (!m_robToRos.count(robId)){
+      if (data.costROSData().getROSForROB(robId).empty()){
         ATH_MSG_WARNING("ROS for ROB 0x" << std::hex << robId << " is missing");
         continue;
       }
-      rosPerRequest.insert(m_robToRos.at(robId));
+      std::string rosForROB = data.costROSData().getROSForROB(robId);
+      if (!rosForROB.empty()){
+        rosPerRequest.insert(rosForROB);
+      }
     }
 
     for (const std::string& rosName : rosPerRequest) {
+      if (!counterExists(rosName)){
+        // Create a new counter using specialized constructor in order to pass number of bins for some of the histograms
+        unsigned nRobs = data.costROSData().getROBForROS(rosName).size(); // Number of all possible ROBs for this ROS
+        m_counters.insert( std::make_pair(rosName, newCounter(rosName, nRobs)) );
+      } 
+
       ATH_CHECK( getCounter(rosName)->newEvent(data, tc->index(), weight) );
     }
   }
@@ -52,3 +51,7 @@ StatusCode MonitorROS::newEvent(const CostData& data, const float weight) {
 std::unique_ptr<CounterBase> MonitorROS::newCounter(const std::string& name) {
   return std::make_unique<CounterROS>(name, this);
 } 
+
+std::unique_ptr<CounterBase> MonitorROS::newCounter(const std::string& name, unsigned nRobs) {
+  return std::make_unique<CounterROS>(name, nRobs, this);
+} 
\ No newline at end of file
diff --git a/Trigger/TrigCost/TrigCostAnalysis/src/monitors/MonitorROS.h b/Trigger/TrigCost/TrigCostAnalysis/src/monitors/MonitorROS.h
index 47d59d3c50bc7cb5aef28525260ef21c62faa25c..b06965d2892a4fa5d1f23070b2bb27381eaeeb3d 100644
--- a/Trigger/TrigCost/TrigCostAnalysis/src/monitors/MonitorROS.h
+++ b/Trigger/TrigCost/TrigCostAnalysis/src/monitors/MonitorROS.h
@@ -54,8 +54,14 @@ class MonitorROS : public MonitorBase {
      */
     virtual std::unique_ptr<CounterBase> newCounter(const std::string& name) override; 
 
-  private:
-    std::map<uint32_t, std::string> m_robToRos; //!< Cache correspondis ROS per ROB id
+    /**
+     * @brief Creates named counter passing the nRobs argument.
+     * @param[in] name Name of Counter to mint.
+     * @param[in] nRobs Initialize ROB histograms with nRobs
+     * @return Owning unique ptr object typed on the CounterBase base class which points to concrete Counter of specialised type.
+     */
+    std::unique_ptr<CounterBase> newCounter(const std::string& name, unsigned nRobs); 
+
 };
 
 #endif // TRIGCOSTANALYSIS_MONITORROS_H
\ No newline at end of file