diff --git a/algo/CMakeLists.txt b/algo/CMakeLists.txt
index 4dd79e72e3ddc13d0b463252f11a3374ef8e3a72..4abc148e95b04a5ef0aab0ee5332601f23703194 100644
--- a/algo/CMakeLists.txt
+++ b/algo/CMakeLists.txt
@@ -162,6 +162,7 @@ target_include_directories(Algo
          ${CMAKE_CURRENT_SOURCE_DIR}/unpack
          ${CMAKE_CURRENT_SOURCE_DIR}/detectors
          ${CMAKE_CURRENT_SOURCE_DIR}/qa
+         ${CMAKE_CURRENT_SOURCE_DIR}/ca
          ${CMAKE_CURRENT_SOURCE_DIR}/ca/qa
          ${CMAKE_CURRENT_SOURCE_DIR}
          ${CMAKE_SOURCE_DIR}/core/data/global
diff --git a/algo/ca/TrackingChain.cxx b/algo/ca/TrackingChain.cxx
index ea1acc6fcf6f64d993f19b6662c76667890909f1..4136c65fcd0afad876b77d3136fefe7a90557fb2 100644
--- a/algo/ca/TrackingChain.cxx
+++ b/algo/ca/TrackingChain.cxx
@@ -66,10 +66,12 @@ void TrackingChain::Init()
   fCaFramework.ReceiveParameters(std::move(parameters));
 
   // ------ Initialize QA modules
-  if (fOutputQa.IsSenderDefined()) {
+  if (fInputQa.IsSenderDefined()) {
     fInputQa.RegisterParameters(&fCaFramework.GetParameters());
     fInputQa.Init();
+  }
 
+  if (fOutputQa.IsSenderDefined()) {
     fOutputQa.RegisterParameters(&fCaFramework.GetParameters());
     fOutputQa.Init();
   }
@@ -157,12 +159,14 @@ TrackingChain::Output_t TrackingChain::PrepareOutput()
   output.monitorData = fCaMonitorData;
 
   // QA
-  if (fOutputQa.IsSenderDefined()) {
+  if (fInputQa.IsSenderDefined()) {
     fInputQa.RegisterInputData(&fCaFramework.GetInputData());
     fInputQa.RegisterTracks(&output.tracks);
     fInputQa.RegisterRecoHitIndices(&fCaFramework.fRecoHits);
     fInputQa.Exec();
+  }
 
+  if (fOutputQa.IsSenderDefined()) {
     fOutputQa.RegisterInputData(&fCaFramework.GetInputData());
     fOutputQa.RegisterTracks(&output.tracks);
     fOutputQa.RegisterRecoHitIndices(&fCaFramework.fRecoHits);
diff --git a/algo/ca/qa/CaInputQa.cxx b/algo/ca/qa/CaInputQa.cxx
index b8e6352854f1b03e059f5bc4723480bc678ff998..c63cb0cea9a9c37c565a4223ee03fb8bcb6b51d1 100644
--- a/algo/ca/qa/CaInputQa.cxx
+++ b/algo/ca/qa/CaInputQa.cxx
@@ -9,10 +9,10 @@
 
 #include "CaInputQa.h"
 
-#include "../TrackingDefs.h"
 #include "CaConstants.h"
 #include "CaInputData.h"
 #include "CaParameters.h"
+#include "TrackingDefs.h"
 
 #include <algorithm>
 #include <limits>
@@ -50,132 +50,130 @@ void InputQa::Init()
   // ------ Init histograms
 
   // Occupancy distributions
-  {
-    fvphHitOccupXY.resize(nSt);
-    fvphHitOccupZX.resize(nSt);
-    fvphHitOccupZY.resize(nSt);
-    fvphHitUsageXY.resize(nSt);
-
-    // Station sizes in transverse plane
-    std::vector<double> vMinX(nSt);
-    std::vector<double> vMaxX(nSt);
-    std::vector<double> vMinY(nSt);
-    std::vector<double> vMaxY(nSt);
-
-    int nBinsXY = 200;
-    int nBinsZ  = 400;
-
-    for (int iSt = 0; iSt < nSt; ++iSt) {
-      const auto& station = fpParameters->GetStation(iSt);
-      vMaxX[iSt]          = station.GetXmax<double>();
-      vMinX[iSt]          = station.GetXmin<double>();
-      vMaxY[iSt]          = station.GetYmax<double>();
-      vMinY[iSt]          = station.GetYmin<double>();
-      double dy           = (vMaxY[iSt] - vMinY[iSt]) * kXYZMargin;
-      double dx           = (vMaxX[iSt] - vMinX[iSt]) * kXYZMargin;
-      vMaxX[iSt] += dx;
-      vMinX[iSt] -= dx;
-      vMaxY[iSt] += dy;
-      vMinY[iSt] -= dy;
-    }
-    // Station max
-    double xMinA = *std::min_element(vMinX.begin(), vMinX.end());
-    double xMaxA = *std::max_element(vMaxX.begin(), vMaxX.end());
-    double yMinA = *std::min_element(vMinY.begin(), vMinY.end());
-    double yMaxA = *std::max_element(vMaxY.begin(), vMaxY.end());
-    double zMinA = fpParameters->GetStation(0).GetZ<double>();
-    double zMaxA = fpParameters->GetStation(nSt - 1).GetZ<double>();
-    {
-      double dz = (zMaxA - zMinA) * kXYZMargin;
-      zMinA -= dz;
-      zMaxA += dz;
-    }
-    for (int iSt = 0; iSt < nSt; ++iSt) {
-      int iStGeo           = fpParameters->GetActiveToGeoMap()[iSt];
-      auto [detID, iStLoc] = fpParameters->GetGeoToLocalIdMap()[iStGeo];
-      for (auto hitSet : kHitSets) {
-        auto setNm = EHitSet::Input == hitSet ? "input" : "used";
-        auto setTl = EHitSet::Input == hitSet ? "Input" : "Used";
-        {
-          auto name = format("hit_{}_occup_xy_sta_{}", setNm, iSt);
-          auto titl = format("{} hit occupancy in XY plane for station {} ({}{});x [cm];y [cm]", setTl, iSt,
-                             kDetName[detID], iStLoc);
-          fvphHitOccupXY[iSt][hitSet] =
-            fQaData.MakeObj<H2D>(name, titl, nBinsXY, vMinX[iSt], vMaxX[iSt], nBinsXY, vMinY[iSt], vMaxY[iSt]);
-        }
-        {
-          auto name = format("hit_{}_occup_zx_sta_{}", setNm, iSt);
-          auto titl = format("{} hit occupancy in ZX plane for station {} ({}{});z [cm];x [cm]", setTl, iSt,
-                             kDetName[detID], iStLoc);
-          fvphHitOccupZX[iSt][hitSet] = fQaData.MakeObj<H2D>(name, titl, nBinsZ, zMinA, zMaxA, nBinsXY, xMinA, xMaxA);
-        }
-        {
-          auto name = format("hit_{}_occup_zy_sta_{}", setNm, iSt);
-          auto titl = format("{} hit occupancy in ZY plane for station {} ({}{});z [cm];y [cm]", setTl, iSt,
-                             kDetName[detID], iStLoc);
-          fvphHitOccupZY[iSt][hitSet] = fQaData.MakeObj<H2D>(name, titl, nBinsZ, zMinA, zMaxA, nBinsXY, yMinA, yMaxA);
-        }
-      }
-      {
-        auto name = format("hit_usage_xy_sta_{}", iSt);
-        auto titl = format("Hit usage in XY plane for station {} ({}{});x [cm];y [cm]", iSt, kDetName[detID], iStLoc);
-        fvphHitUsageXY[iSt] =
-          fQaData.MakeObj<Prof2D>(name, titl, nBinsXY, vMinX[iSt], vMaxX[iSt], nBinsXY, vMinY[iSt], vMaxY[iSt], 0., 1.);
-      }
-    }
+  fvphHitOccupXY.resize(nSt);
+  fvphHitOccupZX.resize(nSt);
+  fvphHitOccupZY.resize(nSt);
+  fvphHitUsageXY.resize(nSt);
+
+  // Station sizes in transverse plane
+  std::vector<double> vMinX(nSt);
+  std::vector<double> vMaxX(nSt);
+  std::vector<double> vMinY(nSt);
+  std::vector<double> vMaxY(nSt);
+
+  int nBinsXY = 200;
+  int nBinsZ  = 400;
+
+  for (int iSt = 0; iSt < nSt; ++iSt) {
+    const auto& station = fpParameters->GetStation(iSt);
+    vMaxX[iSt]          = station.GetXmax<double>();
+    vMinX[iSt]          = station.GetXmin<double>();
+    vMaxY[iSt]          = station.GetYmax<double>();
+    vMinY[iSt]          = station.GetYmin<double>();
+    double dy           = (vMaxY[iSt] - vMinY[iSt]) * kXYZMargin;
+    double dx           = (vMaxX[iSt] - vMinX[iSt]) * kXYZMargin;
+    vMaxX[iSt] += dx;
+    vMinX[iSt] -= dx;
+    vMaxY[iSt] += dy;
+    vMinY[iSt] -= dy;
   }
-
-  // ----- Init canvases
+  // Station max
+  double xMinA = *std::min_element(vMinX.begin(), vMinX.end());
+  double xMaxA = *std::max_element(vMaxX.begin(), vMaxX.end());
+  double yMinA = *std::min_element(vMinY.begin(), vMinY.end());
+  double yMaxA = *std::max_element(vMaxY.begin(), vMaxY.end());
+  double zMinA = fpParameters->GetStation(0).GetZ<double>();
+  double zMaxA = fpParameters->GetStation(nSt - 1).GetZ<double>();
   {
-    // Hit occupancies
+    double dz = (zMaxA - zMinA) * kXYZMargin;
+    zMinA -= dz;
+    zMaxA += dz;
+  }
+  for (int iSt = 0; iSt < nSt; ++iSt) {
+    int iStGeo           = fpParameters->GetActiveToGeoMap()[iSt];
+    auto [detID, iStLoc] = fpParameters->GetGeoToLocalIdMap()[iStGeo];
     for (auto hitSet : kHitSets) {
       auto setNm = EHitSet::Input == hitSet ? "input" : "used";
       auto setTl = EHitSet::Input == hitSet ? "Input" : "Used";
-      {  // XY
-        auto name = format("ca_hit_{}_occupancy_xy", setNm);
-        auto titl = format("{} hit occupancy in different stations in XY plane", setNm);
-        auto canv = CanvasConfig(name, titl);
-        for (int iSt = 0; iSt < nSt; ++iSt) {
-          auto pad = PadConfig();
-          pad.RegisterHistogram(fvphHitOccupXY[iSt][hitSet], "colz");
-          canv.AddPadConfig(pad);
-        }
-        fQaData.AddCanvasConfig(canv);
+      {
+        auto name = format("hit_{}_occup_xy_sta_{}", setNm, iSt);
+        auto titl = format("{} hit occupancy in XY plane for station {} ({}{});x [cm];y [cm]", setTl, iSt,
+                           kDetName[detID], iStLoc);
+        fvphHitOccupXY[iSt][hitSet] =
+          fQaData.MakeObj<H2D>(name, titl, nBinsXY, vMinX[iSt], vMaxX[iSt], nBinsXY, vMinY[iSt], vMaxY[iSt]);
       }
-      {  // ZX and ZY
-        auto name = format("ca_hit_{}_occupancy_zx_zy", setNm);
-        auto titl = format("{} hit occupancy in different stations in ZX and ZY planes", setTl);
-        auto canv = CanvasConfig(name, titl);
-        {  // ZX
-          auto pad = PadConfig();
-          for (int iSt = 0; iSt < nSt; ++iSt) {
-            pad.RegisterHistogram(fvphHitOccupZX[iSt][hitSet], (iSt == 0 ? "colz" : "cols same"));
-          }
-          canv.AddPadConfig(pad);
-        }
-        {  // ZY
-          auto pad = PadConfig();
-          for (int iSt = 0; iSt < nSt; ++iSt) {
-            pad.RegisterHistogram(fvphHitOccupZY[iSt][hitSet], (iSt == 0 ? "colz" : "cols same"));
-          }
-          canv.AddPadConfig(pad);
-        }
-        fQaData.AddCanvasConfig(canv);
+      {
+        auto name = format("hit_{}_occup_zx_sta_{}", setNm, iSt);
+        auto titl = format("{} hit occupancy in ZX plane for station {} ({}{});z [cm];x [cm]", setTl, iSt,
+                           kDetName[detID], iStLoc);
+        fvphHitOccupZX[iSt][hitSet] = fQaData.MakeObj<H2D>(name, titl, nBinsZ, zMinA, zMaxA, nBinsXY, xMinA, xMaxA);
+      }
+      {
+        auto name = format("hit_{}_occup_zy_sta_{}", setNm, iSt);
+        auto titl = format("{} hit occupancy in ZY plane for station {} ({}{});z [cm];y [cm]", setTl, iSt,
+                           kDetName[detID], iStLoc);
+        fvphHitOccupZY[iSt][hitSet] = fQaData.MakeObj<H2D>(name, titl, nBinsZ, zMinA, zMaxA, nBinsXY, yMinA, yMaxA);
       }
     }
-    // Hit usage profiles
     {
-      auto name = format("ca_hit_usage_xy");
-      auto titl = format("Hit usage in different stations in XY plane");
+      auto name = format("hit_usage_xy_sta_{}", iSt);
+      auto titl = format("Hit usage in XY plane for station {} ({}{});x [cm];y [cm]", iSt, kDetName[detID], iStLoc);
+      fvphHitUsageXY[iSt] =
+        fQaData.MakeObj<Prof2D>(name, titl, nBinsXY, vMinX[iSt], vMaxX[iSt], nBinsXY, vMinY[iSt], vMaxY[iSt], 0., 1.);
+    }
+  }
+
+
+  // ----- Init canvases
+  // Hit occupancies
+  for (auto hitSet : kHitSets) {
+    auto setNm = EHitSet::Input == hitSet ? "input" : "used";
+    auto setTl = EHitSet::Input == hitSet ? "Input" : "Used";
+    {  // XY
+      auto name = format("ca_hit_{}_occupancy_xy", setNm);
+      auto titl = format("{} hit occupancy in different stations in XY plane", setNm);
       auto canv = CanvasConfig(name, titl);
       for (int iSt = 0; iSt < nSt; ++iSt) {
         auto pad = PadConfig();
-        pad.RegisterHistogram(fvphHitUsageXY[iSt], "colz");
+        pad.RegisterHistogram(fvphHitOccupXY[iSt][hitSet], "colz");
         canv.AddPadConfig(pad);
       }
       fQaData.AddCanvasConfig(canv);
     }
+    {  // ZX and ZY
+      auto name = format("ca_hit_{}_occupancy_zx_zy", setNm);
+      auto titl = format("{} hit occupancy in different stations in ZX and ZY planes", setTl);
+      auto canv = CanvasConfig(name, titl);
+      {  // ZX
+        auto pad = PadConfig();
+        for (int iSt = 0; iSt < nSt; ++iSt) {
+          pad.RegisterHistogram(fvphHitOccupZX[iSt][hitSet], (iSt == 0 ? "colz" : "cols same"));
+        }
+        canv.AddPadConfig(pad);
+      }
+      {  // ZY
+        auto pad = PadConfig();
+        for (int iSt = 0; iSt < nSt; ++iSt) {
+          pad.RegisterHistogram(fvphHitOccupZY[iSt][hitSet], (iSt == 0 ? "colz" : "cols same"));
+        }
+        canv.AddPadConfig(pad);
+      }
+      fQaData.AddCanvasConfig(canv);
+    }
+  }
+  // Hit usage profiles
+  {
+    auto name = format("ca_hit_usage_xy");
+    auto titl = format("Hit usage in different stations in XY plane");
+    auto canv = CanvasConfig(name, titl);
+    for (int iSt = 0; iSt < nSt; ++iSt) {
+      auto pad = PadConfig();
+      pad.RegisterHistogram(fvphHitUsageXY[iSt], "colz");
+      canv.AddPadConfig(pad);
+    }
+    fQaData.AddCanvasConfig(canv);
   }
+
   fQaData.Init(fpSender);
 }
 
diff --git a/algo/qa/DigiEventQa.h b/algo/qa/DigiEventQa.h
index 91961d46493f5f0fe9c540b809f32d01f9d0fd77..897cda8ebcb5cbf770e71b55f59f61aaa3275dc6 100644
--- a/algo/qa/DigiEventQa.h
+++ b/algo/qa/DigiEventQa.h
@@ -11,7 +11,7 @@
 #include "evbuild/EventBuilderConfig.h"
 
 #include <gsl/span>
-#include <map>
+#include <unordered_map>
 #include <vector>
 
 
@@ -25,8 +25,8 @@ namespace cbm::algo::evbuild
    **/
   struct DigiEventQaData {
     qa::HistogramContainer fHistContainer;
-    std::map<ECbmModuleId, qa::H1D*> fDigiTimeHistos = {};
-    size_t fNumEvents                                = 0;
+    std::unordered_map<ECbmModuleId, qa::H1D*> fDigiTimeHistos = {};
+    size_t fNumEvents                                          = 0;
   };
 
 
diff --git a/algo/qa/Histogram.h b/algo/qa/Histogram.h
index f2a29770e87c8ef58d90d9ac58c75958f0005d10..d136f46b5fb521f5795d62c35c59900a6a613312 100644
--- a/algo/qa/Histogram.h
+++ b/algo/qa/Histogram.h
@@ -36,10 +36,9 @@ namespace cbm::algo::qa
   using Axes1D_t = std::tuple<RegularAxis_t>;
   using Axes2D_t = std::tuple<RegularAxis_t, RegularAxis_t>;
 
-  using HistStorage_t = bh::default_storage;
+  using HistStorage_t = bh::weight_storage;
   using ProfStorage_t = bh::RootStyleProfileStorage<double>;
 
-
   /// \enum  EHistAxis
   /// \brief Enumeration for histogram axes
   enum class EAxis : unsigned
@@ -49,12 +48,122 @@ namespace cbm::algo::qa
     Z
   };
 
+  /// \class  TotalSums1D
+  /// \brief  Storage for total sums of weights, squared weights, weights over x, weights over squared x
+  class TotalSums1D {
+   public:
+    /// \brief Gets total sum of weights
+    double GetTotSumW() const { return fTotSumW; }
+
+    /// \brief Gets total sum of squared weights
+    double GetTotSumW2() const { return fTotSumW2; }
+
+    /// \brief Gets total sum of weight over x products
+    double GetTotSumWX() const { return fTotSumWX; }
+
+    /// \brief Gets total sum of weight over squared x products
+    double GetTotSumWX2() const { return fTotSumWX2; }
+
+   private:
+    /// \brief Serialization rule
+    friend class boost::serialization::access;
+    template<class Archive>
+    void serialize(Archive& ar, const unsigned int /*version*/)
+    {
+      ar& fTotSumW;
+      ar& fTotSumW2;
+      ar& fTotSumWX;
+      ar& fTotSumWX2;
+    }
+
+   protected:
+    /// \brief Updates the sums
+    /// \param x  X value
+    /// \param w  weight
+    void UpdateTotalSums(double x, double w)
+    {
+      fTotSumW += w;
+      fTotSumW2 += w * w;
+      fTotSumWX += w * x;
+      fTotSumWX2 += w * x * x;
+    }
+
+    /// \brief Resets the sums
+    void Reset()
+    {
+      fTotSumW   = 0;
+      fTotSumW2  = 0;
+      fTotSumWX  = 0;
+      fTotSumWX2 = 0;
+    }
+
+    double fTotSumW   = 0.;  ///< Total sum (over all bins) of weights
+    double fTotSumW2  = 0.;  ///< Total sum (over all bins) of squared weights
+    double fTotSumWX  = 0.;  ///< Total sum (over all bins) of weight over x products
+    double fTotSumWX2 = 0.;  ///< Total sum (over all bins) of weight over square x products
+  };
+
+  /// \class  TotalSums2D
+  /// \brief  TotalSums1D including storage for total sums of w*x*y, w*y, w*y*y products
+  class TotalSums2D : public TotalSums1D {
+   public:
+    /// \brief Gets total sum of weight over squared y products
+    double GetTotSumWXY() const { return fTotSumWXY; }
+
+    /// \brief Gets total sum of weight over y products
+    double GetTotSumWY() const { return fTotSumWY; }
+
+    /// \brief Gets total sum of weight over squared y products
+    double GetTotSumWY2() const { return fTotSumWY2; }
+
+   private:
+    /// \brief Serialization rule
+    friend class boost::serialization::access;
+    template<class Archive>
+    void serialize(Archive& ar, const unsigned int /*version*/)
+    {
+      ar& boost::serialization::base_object<TotalSums1D>(*this);
+      ar& fTotSumWXY;
+      ar& fTotSumWY;
+      ar& fTotSumWY2;
+    }
+
+   protected:
+    /// \brief Resets the sums
+    void Reset()
+    {
+      TotalSums1D::Reset();
+      fTotSumWXY = 0;
+      fTotSumWY  = 0;
+      fTotSumWY2 = 0;
+    }
+
+    /// \brief Updates the sums
+    /// \param x  X value
+    /// \param y  Y value
+    /// \param w  weight
+    void UpdateTotalSums(double x, double y, double w)
+    {
+      TotalSums1D::UpdateTotalSums(x, w);
+      fTotSumWXY += w * x * y;
+      fTotSumWY += w * y;
+      fTotSumWY2 += w * y * y;
+    }
+
+    double fTotSumWY  = 0.;  ///< Total sum (over all bins) of weight over y products
+    double fTotSumWXY = 0.;  ///< Total sum (over all bins) of weight over square x products
+    double fTotSumWY2 = 0.;  ///< Total sum (over all bins) of weight over x over y products
+  };
+
+
   /// \class  Histogram
   /// \brief  Interface to a histogram/profile
-  /// \tparam Axes    The axes type
-  /// \tparam Storage Storage type for the histogram
-  template<class Axes, class Storage>
-  class Histogram {
+  /// \tparam Axes      The axes type
+  /// \tparam Storage   Storage type for the histogram
+  /// \tparam TotalSums Class for storing the total sums of weights for axes (essential to calculate mean and std.dev.)
+  template<class Axes, class Storage, class TotalSums>
+  class Histogram : public TotalSums {
+   protected:
     using Hist_t                   = bh::histogram<Axes, Storage>;  // TODO: Test in future
     static constexpr unsigned Rank = std::tuple_size_v<Axes>;
 
@@ -144,7 +253,11 @@ namespace cbm::algo::qa
     const std::string& GetTitle() const { return fTitle; }
 
     /// \brief Resets the histogram
-    void Reset() { fHistogram.reset(); }
+    void Reset()
+    {
+      TotalSums::Reset();
+      fHistogram.reset();
+    }
 
     /// \brief Sets name
     /// \param name  Histogram name
@@ -168,6 +281,7 @@ namespace cbm::algo::qa
     /// \brief Serialization rule
     void serialize(Archive& ar, const unsigned int /*version*/)
     {
+      ar& boost::serialization::base_object<TotalSums>(*this);
       ar& fHistogram;
       ar& fName;
       ar& fTitle;
@@ -178,13 +292,15 @@ namespace cbm::algo::qa
     /// \brief Default constructor
     Histogram() = default;
 
-    explicit Histogram(const Histogram<Axes, Storage>& h)
-      : fHistogram(h.fHistogram)
-      , fName(h.fName)
-      , fTitle(h.fTitle)
-      , fEntries(h.fEntries)
-    {
-    }
+    explicit Histogram(const Histogram<Axes, Storage, TotalSums>& h) = default;
+
+    //explicit Histogram(const Histogram<Axes, Storage>& h)
+    //  : fHistogram(h.fHistogram)
+    //  , fName(h.fName)
+    //  , fTitle(h.fTitle)
+    //  , fEntries(h.fEntries)
+    //{
+    //}
 
     /// \brief Constructor
     /// \param bhist  Boost histogram object
@@ -209,10 +325,10 @@ namespace cbm::algo::qa
     int fEntries       = 0;  ///< Number of entries
   };
 
-  using BaseH1D    = Histogram<Axes1D_t, HistStorage_t>;
-  using BaseH2D    = Histogram<Axes2D_t, HistStorage_t>;
-  using BaseProf1D = Histogram<Axes1D_t, ProfStorage_t>;
-  using BaseProf2D = Histogram<Axes2D_t, ProfStorage_t>;
+  using BaseH1D    = Histogram<Axes1D_t, HistStorage_t, TotalSums1D>;
+  using BaseH2D    = Histogram<Axes2D_t, HistStorage_t, TotalSums2D>;
+  using BaseProf1D = Histogram<Axes1D_t, ProfStorage_t, TotalSums1D>;
+  using BaseProf2D = Histogram<Axes2D_t, ProfStorage_t, TotalSums2D>;
 
   /// \class  cbm::algo::qa::H1D
   /// \brief  1D-histogram
@@ -225,8 +341,7 @@ namespace cbm::algo::qa
     /// \param xMin   Lower x bound
     /// \param xMax   Upper x bound
     H1D(const std::string& name, const std::string& title, uint32_t nBins, double xMin, double xMax)
-      : BaseH1D(bh::make_histogram(RegularAxis_t(nBins, xMin, xMax)), name,
-                title)  // TODO: test bh::make_weighted_histogram
+      : BaseH1D(bh::make_weighted_histogram(RegularAxis_t(nBins, xMin, xMax)), name, title)
     {
     }
 
@@ -251,22 +366,24 @@ namespace cbm::algo::qa
     /// \return Bin number  TODO: implement
     int Fill(double x, double w = 1.)
     {
-      fHistogram(x, bh::weight(w));
+      auto cellIt = fHistogram(bh::weight(w), x);
       ++fEntries;
-      return -1;
+      int iBin = cellIt - fHistogram.begin();
+      if (iBin == 0 || iBin > static_cast<int>(GetNbinsX())) {
+        return -1;  // ROOT TH1::Fill behaviour
+      }
+      // NOTE: In ROOT TH1 the total sums are updated only for non-overflow bins
+      BaseH1D::UpdateTotalSums(x, w);
+      return iBin;
     }
 
     /// \brief Gets bin content
     /// \param iBin  Bin index
-    double GetBinContent(uint32_t iBin) const { return fHistogram.at(Histogram::GetBinBH(iBin)); }
+    double GetBinContent(uint32_t iBin) const { return fHistogram.at(Histogram::GetBinBH(iBin)).value(); }
 
     /// \brief Gets bin error
     /// \param iBin  Bin index
-    double GetBinError(uint32_t iBin) const
-    {
-      // TODO: implement Sumw2!
-      return std::sqrt(std::fabs(fHistogram.at(Histogram::GetBinBH(iBin))));
-    }
+    double GetBinError(uint32_t iBin) const { return std::sqrt(fHistogram.at(Histogram::GetBinBH(iBin)).variance()); }
 
     /// \brief Gets underlying bin accumulator
     /// \param iBin  Bin index
@@ -297,7 +414,8 @@ namespace cbm::algo::qa
     /// \param yMax    Upper y bound
     H2D(const std::string& name, const std::string& title, uint32_t nBinsX, double xMin, double xMax, uint32_t nBinsY,
         double yMin, double yMax)
-      : BaseH2D(bh::make_histogram(RegularAxis_t(nBinsX, xMin, xMax), RegularAxis_t(nBinsY, yMin, yMax)), name, title)
+      : BaseH2D(bh::make_weighted_histogram(RegularAxis_t(nBinsX, xMin, xMax), RegularAxis_t(nBinsY, yMin, yMax)), name,
+                title)
     {
     }
 
@@ -323,9 +441,21 @@ namespace cbm::algo::qa
     /// \return Bin number  TODO: implement
     int Fill(double x, double y, double w = 1.)
     {
-      fHistogram(x, y, bh::weight(w));
+      auto cellIt = fHistogram(x, y, bh::weight(w));
       ++fEntries;
-      return -1;
+
+      int iBin       = cellIt - fHistogram.begin();
+      uint32_t iBinX = iBin % (GetNbinsX() + 2);
+      if (iBinX == 0 || iBinX > GetNbinsX()) {
+        return -1;
+      }
+      uint32_t iBinY = iBin / (GetNbinsX() + 2);
+      if (iBinY == 0 || iBinY > GetNbinsY()) {
+        return -1;
+      }
+      // NOTE: In ROOT TH2 the total sums are updated only for non-overflow bins
+      BaseH2D::UpdateTotalSums(x, y, w);
+      return iBin;
     }
 
     /// \brief Gets underlying bin accumulator
@@ -341,7 +471,7 @@ namespace cbm::algo::qa
     /// \param iBinY  Bin index in y-axis
     double GetBinContent(uint32_t iBinX, uint32_t iBinY) const
     {
-      return fHistogram.at(Histogram::GetBinBH(iBinX), Histogram::GetBinBH(iBinY));
+      return fHistogram.at(Histogram::GetBinBH(iBinX), Histogram::GetBinBH(iBinY)).value();
     }
 
     /// \brief Gets bin error
@@ -349,7 +479,7 @@ namespace cbm::algo::qa
     /// \param iBinY  Bin index in y-axis
     double GetBinError(uint32_t iBinX, uint32_t iBinY) const
     {
-      return std::sqrt(std::fabs(fHistogram.at(Histogram::GetBinBH(iBinX), Histogram::GetBinBH(iBinY))));
+      return std::sqrt(fHistogram.at(Histogram::GetBinBH(iBinX), Histogram::GetBinBH(iBinY)).variance());
     }
 
    private:
@@ -412,11 +542,15 @@ namespace cbm::algo::qa
         return -1;
       }
 
-      fHistogram(x, bh::sample(y), bh::weight(w));
-      fTotSumWX += w * x;
-      fTotSumWX2 += w * x * x;
+      auto cellIt = fHistogram(x, bh::sample(y), bh::weight(w));
       ++fEntries;
-      return -1;
+      int iBin = cellIt - fHistogram.begin();
+      if (iBin == 0 || iBin > static_cast<int>(GetNbinsX())) {
+        return -1;  // ROOT TH1::Fill behaviour
+      }
+      // NOTE: In ROOT TProfile the total sums are updated only for non-overflow bins
+      BaseProf1D::UpdateTotalSums(x, w);
+      return iBin;
     }
 
     /// \brief Gets underlying bin accumulator
@@ -441,20 +575,6 @@ namespace cbm::algo::qa
     /// \brief Gets y-axis lower bound
     double GetMaxY() const { return fYmax; }
 
-    /// \brief Gets total sum of weight over x products
-    double GetTotSumWX() const { return fTotSumWX; }
-
-    /// \brief Gets total sum of weight over squared x products
-    double GetTotSumWX2() const { return fTotSumWX2; }
-
-    /// \brief Resets the profile
-    void Reset()
-    {
-      BaseProf1D::Reset();
-      fTotSumWX  = 0;
-      fTotSumWX2 = 0;
-    }
-
    private:
     /// \brief Serialization rule
     friend class boost::serialization::access;
@@ -464,14 +584,10 @@ namespace cbm::algo::qa
       ar& boost::serialization::base_object<BaseProf1D>(*this);
       ar& fYmin;
       ar& fYmax;
-      ar& fTotSumWX;
-      ar& fTotSumWX2;
     }
 
-    double fYmin      = 0.;
-    double fYmax      = 0.;
-    double fTotSumWX  = 0.;  ///< Total sum (over all bins) of weight over x products
-    double fTotSumWX2 = 0.;  ///< Total sum (over all bins) of weight over square x products
+    double fYmin = 0.;
+    double fYmax = 0.;
   };
 
   class Prof2D : public BaseProf2D {
@@ -527,14 +643,21 @@ namespace cbm::algo::qa
         return -1;
       }
 
-      fHistogram(x, y, bh::sample(z), bh::weight(w));
-      fTotSumWX += w * x;
-      fTotSumWY += w * y;
-      fTotSumWX2 += w * x * x;
-      fTotSumWXY += w * x * y;
-      fTotSumWY2 += w * y * y;
+      auto cellIt = fHistogram(x, y, bh::sample(z), bh::weight(w));
       ++fEntries;
-      return -1;
+
+      int iBin       = cellIt - fHistogram.begin();
+      uint32_t iBinX = iBin % (GetNbinsX() + 2);
+      if (iBinX == 0 || iBinX > GetNbinsX()) {
+        return -1;
+      }
+      uint32_t iBinY = iBin / (GetNbinsX() + 2);
+      if (iBinY == 0 || iBinY > GetNbinsY()) {
+        return -1;
+      }
+      // NOTE: In ROOT TProfile2D the total sums are updated only for non-overflow bins
+      BaseProf2D::UpdateTotalSums(x, y, w);
+      return iBin;
     }
 
     /// \brief Gets underlying bin accumulator
@@ -575,32 +698,6 @@ namespace cbm::algo::qa
     /// \brief Gets z-axis lower bound
     double GetMaxZ() const { return fZmax; }
 
-    /// \brief Gets total sum of weight over x products
-    double GetTotSumWX() const { return fTotSumWX; }
-
-    /// \brief Gets total sum of weight over y products
-    double GetTotSumWY() const { return fTotSumWY; }
-
-    /// \brief Gets total sum of weight over squared x products
-    double GetTotSumWX2() const { return fTotSumWX2; }
-
-    /// \brief Gets total sum of weight over x over y products
-    double GetTotSumWXY() const { return fTotSumWXY; }
-
-    /// \brief Gets total sum of weight over squared y products
-    double GetTotSumWY2() const { return fTotSumWY2; }
-
-    /// \brief Resets the profile
-    void Reset()
-    {
-      BaseProf2D::Reset();
-      fTotSumWX  = 0;
-      fTotSumWY  = 0;
-      fTotSumWX2 = 0;
-      fTotSumWXY = 0;
-      fTotSumWY2 = 0;
-    }
-
    private:
     /// \brief Serialization rule
     friend class boost::serialization::access;
@@ -610,20 +707,10 @@ namespace cbm::algo::qa
       ar& boost::serialization::base_object<BaseProf2D>(*this);
       ar& fZmin;
       ar& fZmax;
-      ar& fTotSumWX;
-      ar& fTotSumWY;
-      ar& fTotSumWX2;
-      ar& fTotSumWXY;
-      ar& fTotSumWY2;
     }
 
-    double fZmin      = 0.;
-    double fZmax      = 0.;
-    double fTotSumWX  = 0.;  ///< Total sum (over all bins) of weight over x products
-    double fTotSumWY  = 0.;  ///< Total sum (over all bins) of weight over y products
-    double fTotSumWX2 = 0.;  ///< Total sum (over all bins) of weight over square x products
-    double fTotSumWXY = 0.;  ///< Total sum (over all bins) of weight over square x products
-    double fTotSumWY2 = 0.;  ///< Total sum (over all bins) of weight over x over y products
+    double fZmin = 0.;
+    double fZmax = 0.;
   };
 
 }  // namespace cbm::algo::qa
diff --git a/core/qa/CbmQaOnlineInterface.cxx b/core/qa/CbmQaOnlineInterface.cxx
index 0c6cebba0c086dc3307534da55c6a055a0a7cb39..0b5823b10a0326365cefa128ecd0d7cef69b5d0e 100644
--- a/core/qa/CbmQaOnlineInterface.cxx
+++ b/core/qa/CbmQaOnlineInterface.cxx
@@ -20,7 +20,7 @@ using cbm::algo::qa::H2D;
 using cbm::algo::qa::Prof1D;
 using cbm::algo::qa::Prof2D;
 using cbm::qa::OnlineInterface;
-using cbm::qa::TProfileAccessor;
+using cbm::qa::RootHistogramAccessor;
 
 
 // ---------------------------------------------------------------------------------------------------------------------
@@ -32,13 +32,9 @@ TH1D* OnlineInterface::ROOTHistogram(const cbm::algo::qa::H1D& hist)
   double xMax = hist.GetMaxX();
   bool add    = TH1::AddDirectoryStatus();
   TH1::AddDirectory(false);
-  auto* pRes = new TH1D(hist.GetName().c_str(), hist.GetTitle().c_str(), nBins, xMin, xMax);
+  auto* pRes = new RootHistogramAccessor<TH1D, H1D>(hist.GetName().c_str(), hist.GetTitle().c_str(), nBins, xMin, xMax);
   TH1::AddDirectory(add);
-  for (int iBinX = 0; iBinX <= pRes->GetNbinsX() + 1; ++iBinX) {
-    pRes->SetBinContent(iBinX, hist.GetBinContent(iBinX));
-    //pRes->SetBinError(iBinX, hist.GetBinError(iBinX));
-  }
-  pRes->SetEntries(hist.GetEntries());
+  pRes->SetFromQaHistogram(hist);
   return pRes;
 }
 
@@ -54,15 +50,10 @@ TH2D* OnlineInterface::ROOTHistogram(const cbm::algo::qa::H2D& hist)
   double yMax = hist.GetMaxY();
   bool add    = TH1::AddDirectoryStatus();
   TH1::AddDirectory(false);
-  auto* pRes = new TH2D(hist.GetName().c_str(), hist.GetTitle().c_str(), nBinsX, xMin, xMax, nBinsY, yMin, yMax);
+  auto* pRes = new RootHistogramAccessor<TH2D, H2D>(hist.GetName().c_str(), hist.GetTitle().c_str(), nBinsX, xMin, xMax,
+                                                    nBinsY, yMin, yMax);
   TH1::AddDirectory(add);
-  for (int iBinX = 0; iBinX <= pRes->GetNbinsX() + 1; ++iBinX) {
-    for (int iBinY = 0; iBinY <= pRes->GetNbinsY() + 1; ++iBinY) {
-      pRes->SetBinContent(iBinX, iBinY, hist.GetBinContent(iBinX, iBinY));
-      //pRes->SetBinError(iBinX, iBinY, hist.GetBinError(iBinX, iBinY));
-    }
-  }
-  pRes->SetEntries(hist.GetEntries());
+  pRes->SetFromQaHistogram(hist);
   return pRes;
 }
 
@@ -79,9 +70,9 @@ TProfile* OnlineInterface::ROOTHistogram(const Prof1D& prof)
   double yMax      = prof.GetMaxY();
   bool add         = TH1::AddDirectoryStatus();
   TH1::AddDirectory(false);
-  auto* pRes = new TProfileAccessor<TProfile, Prof1D>(name, titl, nBinsX, xMin, xMax, yMin, yMax);
+  auto* pRes = new RootHistogramAccessor<TProfile, Prof1D>(name, titl, nBinsX, xMin, xMax, yMin, yMax);
   TH1::AddDirectory(add);
-  pRes->SetFromQaProf(prof);
+  pRes->SetFromQaHistogram(prof);
   return pRes;
 }
 
@@ -101,8 +92,9 @@ TProfile2D* OnlineInterface::ROOTHistogram(const Prof2D& prof)
   double zMax      = prof.GetMaxZ();
   bool add         = TH1::AddDirectoryStatus();
   TH1::AddDirectory(false);
-  auto* pRes = new TProfileAccessor<TProfile2D, Prof2D>(name, titl, nBinsX, xMin, xMax, nBinsY, yMin, yMax, zMin, zMax);
+  auto* pRes =
+    new RootHistogramAccessor<TProfile2D, Prof2D>(name, titl, nBinsX, xMin, xMax, nBinsY, yMin, yMax, zMin, zMax);
   TH1::AddDirectory(add);
-  pRes->SetFromQaProf(prof);
+  pRes->SetFromQaHistogram(prof);
   return pRes;
 }
diff --git a/core/qa/CbmQaOnlineInterface.h b/core/qa/CbmQaOnlineInterface.h
index db19c16aa117febc815e7f4c72c5f00bf240ea22..462c90fb20d6e997394014e3424f14e625947ae1 100644
--- a/core/qa/CbmQaOnlineInterface.h
+++ b/core/qa/CbmQaOnlineInterface.h
@@ -13,40 +13,34 @@
 
 #include "Histogram.h"
 #include "Logger.h"
+#include "TH1D.h"
+#include "TH2D.h"
 #include "TProfile.h"
 #include "TProfile2D.h"
 
+#include <log.hpp>
 #include <type_traits>
 
-class TH1D;
-class TH2D;
-class TProfile2D;
-namespace cbm::algo::qa
-{
-  class H1D;
-  class H2D;
-  class Prof1D;
-  class Prof2D;
-}  // namespace cbm::algo::qa
 
 namespace cbm::qa
 {
-  /// \class TProfileAccessor
-  /// \brief Helper class to access protected fields of TProfile
+  /// \class RootHistogramAccessor
+  /// \brief Helper class to access protected fields of TH1, TH2, TProfile and TProfile2D
   ///
   /// The ROOT TProfile classes do not alow to set a profile from outside, so we need to set all the
-  /// fields directly.
-  template<class RootProfile, class QaProfile>
-  class TProfileAccessor : public RootProfile {
+  /// fields directly. Also the access to TH1/TH2 protected fields is needed in order to set the
+  /// total sums of w*x, w*x*x, w*y, w*x*y, and w*y*y.
+  template<class RootHistogram, class QaHistogram>
+  class RootHistogramAccessor : public RootHistogram {
 
    public:
     template<class... Args>
-    TProfileAccessor(Args... args) : RootProfile(args...)
+    RootHistogramAccessor(Args... args) : RootHistogram(args...)
     {
     }
 
-    /// \brief Sets fields from cbm::algo::qa::Prof1D
-    void SetFromQaProf(const QaProfile& prof);
+    /// \brief Sets fields from qa histogram
+    void SetFromQaHistogram(const QaHistogram& histo);
   };
 
   /// \class  OnlineInterface
@@ -80,44 +74,75 @@ namespace cbm::qa
 {
   // -------------------------------------------------------------------------------------------------------------------
   //
-  template<class RootProfile, class QaProfile>
-  void TProfileAccessor<RootProfile, QaProfile>::SetFromQaProf(const QaProfile& prof)
+  template<class RootHistogram, class QaHistogram>
+  void RootHistogramAccessor<RootHistogram, QaHistogram>::SetFromQaHistogram(const QaHistogram& hist)
   {
-    this->Sumw2();
-    auto SetBin = [&](int iBinRoot, const auto& binQa) {
-      this->fArray[iBinRoot]             = binQa.GetSumWV();
-      this->fBinEntries.fArray[iBinRoot] = binQa.GetSumW();
-      this->fSumw2.fArray[iBinRoot]      = binQa.GetSumWV2();
-      this->fBinSumw2.fArray[iBinRoot]   = binQa.GetSumW2();
-
-      this->fTsumw += binQa.GetSumW();
-      this->fTsumw2 += binQa.GetSumW2();
-      this->fTsumwy += binQa.GetSumWV();
-      this->fTsumwy2 += binQa.GetSumWV2();
-    };
-
-    if constexpr (std::is_same_v<RootProfile, TProfile>) {
-      for (int iBinX = 0; iBinX <= this->GetNbinsX() + 1; ++iBinX) {
-        int iBin = this->GetBin(iBinX);
-        auto bin = prof.GetBinAccumulator(iBinX);
-        SetBin(iBin, bin);
+    // Set individual bin properties
+    if constexpr (std::is_same_v<RootHistogram, TProfile> || std::is_same_v<RootHistogram, TProfile2D>) {
+      this->Sumw2();
+      auto SetBin = [&](int iBinRoot, const auto& binQa) {
+        this->fArray[iBinRoot]             = binQa.GetSumWV();
+        this->fBinEntries.fArray[iBinRoot] = binQa.GetSumW();
+        this->fSumw2.fArray[iBinRoot]      = binQa.GetSumWV2();
+        this->fBinSumw2.fArray[iBinRoot]   = binQa.GetSumW2();
+      };
+      if constexpr (std::is_same_v<RootHistogram, TProfile>) {
+        for (int iBinX = 0; iBinX <= this->GetNbinsX() + 1; ++iBinX) {
+          int iBin = this->GetBin(iBinX);
+          auto bin = hist.GetBinAccumulator(iBinX);
+          SetBin(iBin, bin);
+          this->fTsumwy += bin.GetSumWV();
+          this->fTsumwy2 += bin.GetSumWV2();
+        }
+      }
+      else if constexpr (std::is_same_v<RootHistogram, TProfile2D>) {
+        for (int iBinX = 0; iBinX <= this->GetNbinsX() + 1; ++iBinX) {
+          for (int iBinY = 0; iBinY <= this->GetNbinsY() + 1; ++iBinY) {
+            int iBin = this->GetBin(iBinX, iBinY);
+            auto bin = hist.GetBinAccumulator(iBinX, iBinY);
+            SetBin(iBin, bin);
+            this->fTsumwz += bin.GetSumWV();
+            this->fTsumwz2 += bin.GetSumWV2();
+          }
+        }
       }
     }
-    else if constexpr (std::is_same_v<RootProfile, TProfile2D>) {
-      for (int iBinX = 0; iBinX <= this->GetNbinsX() + 1; ++iBinX) {
-        for (int iBinY = 0; iBinY <= this->GetNbinsY() + 1; ++iBinY) {
-          int iBin = this->GetBin(iBinX, iBinY);
-          auto bin = prof.GetBinAccumulator(iBinX, iBinY);
-          SetBin(iBin, bin);
+    else if constexpr (std::is_same_v<RootHistogram, TH1D> || std::is_same_v<RootHistogram, TH2D>) {
+      if (hist.GetTotSumW() != hist.GetTotSumW2()) {  // some of weights were not equal to 1.
+        if (!this->fSumw2.fN) {
+          this->Sumw2();
+        }
+      }
+      auto SetBin = [&](int iBinRoot, const auto& binQa) {
+        this->fArray[iBinRoot] = binQa.value();
+        if (this->fSumw2.fN) {
+          this->fSumw2.fArray[iBinRoot] = binQa.variance();
+        }
+      };
+      if constexpr (std::is_same_v<RootHistogram, TH1D>) {
+        for (int iBinX = 0; iBinX <= this->GetNbinsX() + 1; ++iBinX) {
+          SetBin(this->GetBin(iBinX), hist.GetBinAccumulator(iBinX));
+        }
+      }
+      else if constexpr (std::is_same_v<RootHistogram, TH2D>) {
+        for (int iBinX = 0; iBinX <= this->GetNbinsX() + 1; ++iBinX) {
+          for (int iBinY = 0; iBinY <= this->GetNbinsY() + 1; ++iBinY) {
+            SetBin(this->GetBin(iBinX, iBinY), hist.GetBinAccumulator(iBinX, iBinY));
+          }
         }
       }
-      this->fTsumwy  = prof.GetTotSumWY();
-      this->fTsumwy2 = prof.GetTotSumWY2();
-      this->fTsumwxy = prof.GetTotSumWXY();
     }
-    this->fTsumwx  = prof.GetTotSumWX();
-    this->fTsumwx2 = prof.GetTotSumWX2();
-    this->SetEntries(prof.GetEntries());
-  }
 
+    // Set total sums
+    if constexpr (std::is_base_of_v<TH2D, RootHistogram>) {
+      this->fTsumwy  = hist.GetTotSumWY();
+      this->fTsumwy2 = hist.GetTotSumWY2();
+      this->fTsumwxy = hist.GetTotSumWXY();
+    }
+    this->fTsumw   = hist.GetTotSumW();
+    this->fTsumw2  = hist.GetTotSumW2();
+    this->fTsumwx  = hist.GetTotSumWX();
+    this->fTsumwx2 = hist.GetTotSumWX2();
+    this->SetEntries(hist.GetEntries());
+  }
 }  // namespace cbm::qa