diff --git a/algo/qa/Accumulators.h b/algo/qa/Accumulators.h new file mode 100644 index 0000000000000000000000000000000000000000..67867da4ca2bff9f1d182482f10b4b7a64743711 --- /dev/null +++ b/algo/qa/Accumulators.h @@ -0,0 +1,172 @@ +/* Copyright (C) 2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Sergei Zharko [committer] */ + +/// \file Accumulators.h +/// \date 03.03.2024 +/// \brief Custom accumulators for boost::histogram (header) +/// \author Sergei Zharko <s.zharko@gsi.de> + +#pragma once + +#include <boost/histogram/fwd.hpp> // for weighted_mean<> +#include <boost/histogram/make_histogram.hpp> +#include <boost/serialization/access.hpp> + +#include <cmath> +#include <type_traits> + +namespace boost::histogram +{ + /// \class RootStyleProfileAccumulator + /// \brief A ROOT-style accumulator for the Boost-histogram profiles + /// \tparam ValueType Type of the values + template<class ValueType> + class RootStyleProfileAccumulator { + public: + using Value_t = ValueType; + using ConstRef_t = const Value_t&; + using Weight_t = weight_type<Value_t>; + + /// \brief Default constructor + RootStyleProfileAccumulator() = default; + + /// \brief Copy constructor from the instances with other types + template<class OtherValueType> + RootStyleProfileAccumulator(const RootStyleProfileAccumulator<OtherValueType>& other) + : fSumWV(other.fSumWV) + , fSumWV2(other.fSumWV2) + , fSumW(other.fSumW) + , fSumW2(other.fSumW2) + { + } + + /// \brief Constructor from the external counters + /// \param sumWV Sum of weight * value + /// \param sumWV Sum of weight * value * value + /// \param sumW Sum of weights + /// \param sumW2 Sum of weights + RootStyleProfileAccumulator(ConstRef_t sumWV, ConstRef_t sumWV2, ConstRef_t sumW, ConstRef_t sumW2) + : fSumWV(sumWV) + , fSumWV2(sumWV2) + , fSumW(sumW) + , fSumW2(sumW2) + { + } + + /// \brief Inserts sample with 1. weight + /// \param v Sample + void operator()(ConstRef_t v) { operator()(weight(1.), v); } + + /// \brief Inserts sample with weight + /// \param v Sample + /// \param w Sample weight + void operator()(const Weight_t& w, ConstRef_t v) + { + fSumWV += w.value * v; + fSumWV2 += w.value * v * v; + fSumW += w.value; + fSumW2 += w.value * w.value; + } + + /// \brief Adds another accumulator + RootStyleProfileAccumulator& operator+=(const RootStyleProfileAccumulator& rhs) + { + if (rhs.fSumW == 0) { + return *this; + } + fSumWV += rhs.fSumWV; + fSumWV2 += rhs.fSumWV2; + fSumW += rhs.fSumW; + fSumW2 += rhs.fSumW2; + return *this; + } + + /// \brief Scales all entries by value + RootStyleProfileAccumulator& operator*=(ConstRef_t s) noexcept + { + fSumWV *= s; + fSumWV2 *= s * s; + return *this; + } + + /// \brief Equality operator + bool operator==(const RootStyleProfileAccumulator& rhs) const noexcept + { + return fSumWV == rhs.fSumWV && fSumWV2 == rhs.fSumWV2 && fSumW == rhs.fSumW && fSumW2 == rhs.fSumW2; + } + + /// \brief Non-equality operator + bool operator!=(const RootStyleProfileAccumulator& rhs) const noexcept { return !operator==(rhs); } + + /// \brief Returns sum of weights + ConstRef_t GetSumW() const noexcept { return fSumW; } + + /// \brief Returns sum of weight squares + ConstRef_t GetSumW2() const noexcept { return fSumW2; } + + /// \brief Returns sum of products of weight multiplied by value + ConstRef_t GetSumWV() const noexcept { return fSumWV; } + + /// \brief Returns sum of products of weight multiplied by squared value + ConstRef_t GetSumWV2() const noexcept { return fSumWV2; } + + /// \brief Returns the mean value + Value_t GetMean() const noexcept { return fSumW ? (fSumWV / fSumW) : 0; } + + /// \brief Returns effective sum of values + Value_t GetEffCount() const noexcept { return fSumW2 ? (fSumW * fSumW / fSumW2) : 0; } + + /// \brief Returns variance + Value_t GetVariance() const noexcept { return fSumW ? (fSumWV2 / fSumW - GetMean() * GetMean()) : 0; } + + /// \brief Returns standard deviation of the sample + Value_t GetStdDev() const noexcept { return std::sqrt(GetVariance()); } + + /// \brief Returns standard error of the mean + Value_t GetSEM() const noexcept { return fSumW ? GetStdDev() / std::sqrt(GetEffCount()) : 0; } + + private: + friend class boost::serialization::access; + /// \brief Serialization rule + template<class Archive> + void serialize(Archive& ar, const unsigned int /*version*/) + { + ar& make_nvp("fSumWV", fSumWV); + ar& make_nvp("fSumWV2", fSumWV2); + ar& make_nvp("fSumW", fSumW); + ar& make_nvp("fSumW2", fSumW2); + } + + Value_t fSumWV{}; ///< Sum of weight * value + Value_t fSumWV2{}; ///< Sum of weight * value * value + Value_t fSumW{}; ///< Sum of weights + Value_t fSumW2{}; ///< Sum of squared weights + }; +} // namespace boost::histogram + +#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED +namespace std +{ + template<class T, class U> + /// Specialization for boost::histogram::accumulators::weighted_mean. + struct common_type<boost::histogram::RootStyleProfileAccumulator<T>, + boost::histogram::RootStyleProfileAccumulator<U>> { + using type = boost::histogram::RootStyleProfileAccumulator<common_type_t<T, U>>; + }; +} // namespace std +#endif + +namespace boost::histogram +{ + template<typename T> + using RootStyleProfileStorage = dense_storage<RootStyleProfileAccumulator<T>>; + + /// \brief Maker for RootStyleProfileAccumulator + template<class Axis, class... Axes, class = detail::requires_axis<Axis>> + auto MakeRootStyleProfile(Axis&& axis, Axes&&... axes) + { + return make_histogram_with(RootStyleProfileStorage<double>(), std::forward<Axis>(axis), + std::forward<Axes>(axes)...); + } +} // namespace boost::histogram diff --git a/algo/qa/Histogram.h b/algo/qa/Histogram.h index adb2f5b80af74585359972a0691031bf3c1f2ab3..f2a29770e87c8ef58d90d9ac58c75958f0005d10 100644 --- a/algo/qa/Histogram.h +++ b/algo/qa/Histogram.h @@ -9,6 +9,8 @@ #pragma once +#include "Accumulators.h" + #include <boost/histogram.hpp> //#include <boost/histogram/algorithm/sum.hpp> #include <boost/histogram/serialization.hpp> @@ -17,6 +19,7 @@ #include <boost/serialization/string.hpp> #include <boost/serialization/vector.hpp> +#include <algorithm> #include <cstdint> #include <memory> #include <numeric> @@ -30,6 +33,13 @@ namespace cbm::algo::qa using RegularAxis_t = bh::axis::regular<>; + using Axes1D_t = std::tuple<RegularAxis_t>; + using Axes2D_t = std::tuple<RegularAxis_t, RegularAxis_t>; + + using HistStorage_t = bh::default_storage; + using ProfStorage_t = bh::RootStyleProfileStorage<double>; + + /// \enum EHistAxis /// \brief Enumeration for histogram axes enum class EAxis : unsigned @@ -40,13 +50,13 @@ namespace cbm::algo::qa }; /// \class Histogram - /// \brief Interface to one-dimentional histogram - /// \tparam Rank Rank of the histogram (1 - 1D, 2 - 2D, ...) - /// \tparam Axes The axes type - template<unsigned Rank, class Axes, std::enable_if_t<(Rank > 0) && (Rank < 3), bool> = true> + /// \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 { - //using Hist_t = bh::histogram<Axes, bh::weight_storage>; // TODO: Test in future - using Hist_t = bh::histogram<Axes, bh::default_storage>; + using Hist_t = bh::histogram<Axes, Storage>; // TODO: Test in future + static constexpr unsigned Rank = std::tuple_size_v<Axes>; public: /// \brief Destructor @@ -118,6 +128,18 @@ namespace cbm::algo::qa return GetAxisMax<EAxis::Y>(); } + /// \brief Gets maximum value + double GetMaximum() const + { + return *std::max_element(bh::indexed(fHistogram).begin(), bh::indexed(fHistogram).end()); + } + + /// \brief Gets minimum value + double GetMinimum() const + { + return *std::min_element(bh::indexed(fHistogram).begin(), bh::indexed(fHistogram).end()); + } + /// \brief Gets title const std::string& GetTitle() const { return fTitle; } @@ -133,9 +155,17 @@ namespace cbm::algo::qa /// \note ROOT axes convention is applicable: "<main title>;<x-axis title>;<y-axis title>" void SetTitle(const std::string& title) { fTitle = title; } + /// \brief String representation of the histogram/profile + //std::string ToString() const + //{ + // std::stringstream msg; + // msg << + //} + private: friend class boost::serialization::access; template<class Archive> + /// \brief Serialization rule void serialize(Archive& ar, const unsigned int /*version*/) { ar& fHistogram; @@ -148,7 +178,7 @@ namespace cbm::algo::qa /// \brief Default constructor Histogram() = default; - explicit Histogram(const Histogram<Rank, Axes>& h) + explicit Histogram(const Histogram<Axes, Storage>& h) : fHistogram(h.fHistogram) , fName(h.fName) , fTitle(h.fTitle) @@ -179,8 +209,10 @@ namespace cbm::algo::qa int fEntries = 0; ///< Number of entries }; - using BaseH1D = Histogram<1, std::tuple<RegularAxis_t>>; - using BaseH2D = Histogram<2, std::tuple<RegularAxis_t, RegularAxis_t>>; + 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>; /// \class cbm::algo::qa::H1D /// \brief 1D-histogram @@ -214,12 +246,12 @@ namespace cbm::algo::qa H1D& operator=(H1D&&) = default; /// \brief Fills histogram - /// \param value Value - /// \param weight Weight + /// \param x Value + /// \param w Weight /// \return Bin number TODO: implement - int Fill(double value, double weight = 1.) + int Fill(double x, double w = 1.) { - fHistogram(value, bh::weight(weight)); + fHistogram(x, bh::weight(w)); ++fEntries; return -1; } @@ -236,7 +268,12 @@ namespace cbm::algo::qa return std::sqrt(std::fabs(fHistogram.at(Histogram::GetBinBH(iBin)))); } + /// \brief Gets underlying bin accumulator + /// \param iBin Bin index + auto GetBinAccumulator(uint32_t iBin) const { return fHistogram.at(Histogram::GetBinBH(iBin)); } + private: + /// \brief Serialization function friend class boost::serialization::access; template<class Archive> void serialize(Archive& ar, const unsigned int /*version*/) @@ -280,17 +317,25 @@ namespace cbm::algo::qa H2D& operator=(H2D&&) = default; /// \brief Fills histogram - /// \param valueX Value along x-axis - /// \param valueY Value along y-axis - /// \param weight Weight + /// \param x Value along x-axis + /// \param y Value along y-axis + /// \param w Weight /// \return Bin number TODO: implement - int Fill(double valueX, double valueY, double weight = 1.) + int Fill(double x, double y, double w = 1.) { - fHistogram(valueX, valueY, bh::weight(weight)); + fHistogram(x, y, bh::weight(w)); ++fEntries; return -1; } + /// \brief Gets underlying bin accumulator + /// \param iBinX Bin index along x-axis + /// \param iBinY Bin index along y-axis + auto GetBinAccumulator(uint32_t iBinX, uint32_t iBinY) const + { + return fHistogram.at(Histogram::GetBinBH(iBinX), Histogram::GetBinBH(iBinY)); + } + /// \brief Gets bin content /// \param iBinX Bin index in x-axis /// \param iBinY Bin index in y-axis @@ -308,6 +353,7 @@ namespace cbm::algo::qa } private: + /// \brief Serialization rule friend class boost::serialization::access; template<class Archive> void serialize(Archive& ar, const unsigned int /*version*/) @@ -316,12 +362,268 @@ namespace cbm::algo::qa } }; - //class Prof1D: public Histogram<1, std::tuple<RegularAxis_t>> { + // NOTE: Boost::histogram by default uses a different error calculation approach as ROOT TProfile. TODO: investigate + + class Prof1D : public BaseProf1D { + public: + /// \brief Constructor for 2D-histogram + /// \param name Name of histogram + /// \param title Title of histogram + /// \param nBinsX Number of x bins + /// \param xMin Lower x bound + /// \param xMax Upper x bound + /// \param yMin Lower y bound (optional) + /// \param yMax Upper y bound (optional) + /// + /// \note If the yMin and yMax are defined (yMin != yMax), all the passed values outside the [yMin, yMax] range + /// will be ignored. + Prof1D(const std::string& name, const std::string& title, uint32_t nBinsX, double xMin, double xMax, + double yMin = 0., double yMax = 0.) + : BaseProf1D(bh::MakeRootStyleProfile(RegularAxis_t(nBinsX, xMin, xMax)), name, title) + , fYmin(yMin) + , fYmax(yMax) + { + } + + /// \brief Default constructor + Prof1D() = default; + + /// \brief Copy constructor + Prof1D(const Prof1D&) = default; + + /// \brief Move constructor + Prof1D(Prof1D&&) = default; + + /// \brief Copy assignment operator + Prof1D& operator=(const Prof1D&) = default; + + /// \brief Move assignment operator + Prof1D& operator=(Prof1D&&) = default; + + /// \brief Fills histogram + /// \param x Value along x-axis + /// \param y Value along y-axis + /// \param w Weight + /// \return Bin number TODO: implement + int Fill(double x, double y, double w = 1.) + { + /// Skip measurement, if it goes beyond the defined [fYmin, fYmax] range + if ((fYmin != fYmax) && (y < fYmin || y > fYmax || std::isnan(y))) { + return -1; + } + + fHistogram(x, bh::sample(y), bh::weight(w)); + fTotSumWX += w * x; + fTotSumWX2 += w * x * x; + ++fEntries; + return -1; + } + + /// \brief Gets underlying bin accumulator + /// \param iBin Bin index along x-axis + auto GetBinAccumulator(uint32_t iBin) const { return fHistogram.at(Histogram::GetBinBH(iBin)); } + + /// \brief Gets bin content + /// \param iBin Bin index + double GetBinContent(uint32_t iBin) const { return fHistogram.at(Histogram::GetBinBH(iBin)).GetMean(); } + + /// \brief Gets bin entries + /// \param iBin Bin index + double GetBinCount(uint32_t iBin) const { return fHistogram.at(Histogram::GetBinBH(iBin)).GetEffCount(); } + + /// \brief Gets bin error + /// \param iBin Bin index + double GetBinError(uint32_t iBin) const { return fHistogram.at(Histogram::GetBinBH(iBin)).GetSEM(); } + + /// \brief Gets y-axis lower bound + double GetMinY() const { return fYmin; } + + /// \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; + template<class Archive> + void serialize(Archive& ar, const unsigned int /*version*/) + { + 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 + }; + + class Prof2D : public BaseProf2D { + public: + /// \brief Constructor for 2D-histogram + /// \param name Name of histogram + /// \param title Title of histogram + /// \param nBinsX Number of x bins + /// \param xMin Lower x bound + /// \param xMax Upper x bound + /// \param nBinsY Number of y bins + /// \param yMin Lower y bound + /// \param yMax Upper y bound + /// \param zMin Lower z bound (optional) + /// \param zMax Upper z bound (optional) + /// + /// \note If the zMin and zMax are defined (zMin != zMax), all the passed values outside the [zMin, zMax] range + /// will be ignored. + Prof2D(const std::string& name, const std::string& title, uint32_t nBinsX, double xMin, double xMax, + uint32_t nBinsY, double yMin, double yMax, double zMin = 0., double zMax = 0.) + : BaseProf2D(bh::MakeRootStyleProfile(RegularAxis_t(nBinsX, xMin, xMax), RegularAxis_t(nBinsY, yMin, yMax)), name, + title) + , fZmin(zMin) + , fZmax(zMax) + { + } + + /// \brief Default constructor + Prof2D() = default; - //}; + /// \brief Copy constructor + Prof2D(const Prof2D&) = default; - //class Prof2D: public Histogram<2, std::tuple<RegularAxis_t, RegularAxis_t>> { + /// \brief Move constructor + Prof2D(Prof2D&&) = default; - //}; + /// \brief Copy assignment operator + Prof2D& operator=(const Prof2D&) = default; + + /// \brief Move assignment operator + Prof2D& operator=(Prof2D&&) = default; + + /// \brief Fills histogram + /// \param x Value along x-axis + /// \param y Value along y-axis + /// \param z Value along z-axis + /// \param w Weight + /// \return Bin number TODO: implement + int Fill(double x, double y, double z, double w = 1.) + { + /// Skip measurement, if it goes beyond the defined [fYmin, fYmax] range + if ((fZmin != fZmax) && (z < fZmin || z > fZmax || std::isnan(z))) { + 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; + ++fEntries; + return -1; + } + + /// \brief Gets underlying bin accumulator + /// \param iBinX Bin index along x-axis + /// \param iBinY Bin index along y-axis + auto GetBinAccumulator(uint32_t iBinX, uint32_t iBinY) const + { + return fHistogram.at(Histogram::GetBinBH(iBinX), Histogram::GetBinBH(iBinY)); + } + + /// \brief Gets bin content + /// \param iBinX Bin index along x-axis + /// \param iBinY Bin index along y-axis + double GetBinContent(uint32_t iBinX, uint32_t iBinY) const + { + return fHistogram.at(Histogram::GetBinBH(iBinX), Histogram::GetBinBH(iBinY)).GetMean(); + } + + /// \brief Gets bin entries + /// \param iBinX Bin index along x-axis + /// \param iBinY Bin index along y-axis + double GetBinCount(uint32_t iBinX, uint32_t iBinY) const + { + return fHistogram.at(Histogram::GetBinBH(iBinX), Histogram::GetBinBH(iBinY)).GetEffCount(); + } + + /// \brief Gets bin error + /// \param iBinX Bin index along x-axis + /// \param iBinY Bin index along y-axis + double GetBinError(uint32_t iBinX, uint32_t iBinY) const + { + return fHistogram.at(Histogram::GetBinBH(iBinX), Histogram::GetBinBH(iBinY)).GetSEM(); + } + + /// \brief Gets z-axis lower bound + double GetMinZ() const { return fZmin; } + + /// \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; + template<class Archive> + void serialize(Archive& ar, const unsigned int /*version*/) + { + 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 + }; } // namespace cbm::algo::qa diff --git a/algo/qa/HistogramContainer.cxx b/algo/qa/HistogramContainer.cxx index a0fe588738daa6f7afd21b3648cf98509c0930b0..08ef1af72af188515c797512ca27f8d9d2a58835 100644 --- a/algo/qa/HistogramContainer.cxx +++ b/algo/qa/HistogramContainer.cxx @@ -21,4 +21,10 @@ void HistogramContainer::Reset() for (auto& h : fvH2) { h.Reset(); } + //for (auto& h : fvP1) { + // h.Reset(); + //} + //for (auto& h : fvP2) { + // h.Reset(); + //} } diff --git a/algo/qa/HistogramContainer.h b/algo/qa/HistogramContainer.h index 11dc4316cd80341741a29df198737ec92151c810..0420d87e8dff49f08f207be8065515c4b70062f5 100644 --- a/algo/qa/HistogramContainer.h +++ b/algo/qa/HistogramContainer.h @@ -22,6 +22,8 @@ namespace cbm::algo::qa struct HistogramContainer { std::forward_list<qa::H1D> fvH1 = {}; ///< List of 1D-histograms std::forward_list<qa::H2D> fvH2 = {}; ///< List of 2D-histograms + //std::forward_list<qa::Prof1D> fvP1 = {}; ///< List of 1D-profiles + //std::forward_list<qa::Prof2D> fvP2 = {}; ///< List of 2D-profiles /// \brief Resets the histograms void Reset(); @@ -33,6 +35,8 @@ namespace cbm::algo::qa { ar& fvH1; ar& fvH2; + //ar& fvP1; + //ar& fvP2; } }; } // namespace cbm::algo::qa diff --git a/algo/qa/PadConfig.h b/algo/qa/PadConfig.h index 9c2e7bcf1a5b52bf5de08524b5cb087782281e9b..1c4d3cb9926e28581897d1ad96783b18304152c2 100644 --- a/algo/qa/PadConfig.h +++ b/algo/qa/PadConfig.h @@ -63,11 +63,15 @@ namespace cbm::algo::qa } /// \brief Registers a histogram in the pad + /// \tparam Hist Histogram class /// \param hist Histogram object /// \param opt Draw options for the histogram // TODO: implement a single function - void RegisterHistogram(const H1D* hist, std::string_view opt) { RegisterObject(hist->GetName(), opt); } - void RegisterHistogram(const H2D* hist, std::string_view opt) { RegisterObject(hist->GetName(), opt); } + template<class Hist> + void RegisterHistogram(const Hist* hist, std::string_view opt) + { + RegisterObject(hist->GetName(), opt); + } /// \brief Returns message config std::string ToString() const; diff --git a/core/qa/CbmQaBaseLinkDef.h b/core/qa/CbmQaBaseLinkDef.h index 9e903ee0005238f3c6c45944dd04952c300b14d4..2d33214ac1ed04ddfd13d9b57853a683aacdbfd3 100644 --- a/core/qa/CbmQaBaseLinkDef.h +++ b/core/qa/CbmQaBaseLinkDef.h @@ -18,6 +18,8 @@ // create streamers automatically #pragma link C++ class cbm::algo::qa::H1D - ; #pragma link C++ class cbm::algo::qa::H2D - ; +#pragma link C++ class cbm::algo::qa::Prof1D - ; +#pragma link C++ class cbm::algo::qa::Prof2D - ; #pragma link C++ class cbm::qa::OnlineInterface + ; #pragma link C++ class CbmQaEff + ; #pragma link C++ class CbmQaPieSlice + ; diff --git a/core/qa/CbmQaOnlineInterface.cxx b/core/qa/CbmQaOnlineInterface.cxx index e479bb282c56f20c7c7f745f48af7703452d1f08..0c6cebba0c086dc3307534da55c6a055a0a7cb39 100644 --- a/core/qa/CbmQaOnlineInterface.cxx +++ b/core/qa/CbmQaOnlineInterface.cxx @@ -12,8 +12,16 @@ #include "TH1D.h" #include "TH2D.h" +#include "TProfile.h" +#include "TProfile2D.h" +using cbm::algo::qa::H1D; +using cbm::algo::qa::H2D; +using cbm::algo::qa::Prof1D; +using cbm::algo::qa::Prof2D; using cbm::qa::OnlineInterface; +using cbm::qa::TProfileAccessor; + // --------------------------------------------------------------------------------------------------------------------- // @@ -57,3 +65,44 @@ TH2D* OnlineInterface::ROOTHistogram(const cbm::algo::qa::H2D& hist) pRes->SetEntries(hist.GetEntries()); return pRes; } + +// --------------------------------------------------------------------------------------------------------------------- +// +TProfile* OnlineInterface::ROOTHistogram(const Prof1D& prof) +{ + const char* name = prof.GetName().c_str(); + const char* titl = prof.GetTitle().c_str(); + int nBinsX = prof.GetNbinsX(); + double xMin = prof.GetMinX(); + double xMax = prof.GetMaxX(); + double yMin = prof.GetMinY(); + double yMax = prof.GetMaxY(); + bool add = TH1::AddDirectoryStatus(); + TH1::AddDirectory(false); + auto* pRes = new TProfileAccessor<TProfile, Prof1D>(name, titl, nBinsX, xMin, xMax, yMin, yMax); + TH1::AddDirectory(add); + pRes->SetFromQaProf(prof); + return pRes; +} + +// --------------------------------------------------------------------------------------------------------------------- +// +TProfile2D* OnlineInterface::ROOTHistogram(const Prof2D& prof) +{ + const char* name = prof.GetName().c_str(); + const char* titl = prof.GetTitle().c_str(); + int nBinsX = prof.GetNbinsX(); + double xMin = prof.GetMinX(); + double xMax = prof.GetMaxX(); + int nBinsY = prof.GetNbinsY(); + double yMin = prof.GetMinY(); + double yMax = prof.GetMaxY(); + double zMin = prof.GetMinZ(); + 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); + TH1::AddDirectory(add); + pRes->SetFromQaProf(prof); + return pRes; +} diff --git a/core/qa/CbmQaOnlineInterface.h b/core/qa/CbmQaOnlineInterface.h index 2b995df8594061278fed69e3eef7cbab7efe8db3..db19c16aa117febc815e7f4c72c5f00bf240ea22 100644 --- a/core/qa/CbmQaOnlineInterface.h +++ b/core/qa/CbmQaOnlineInterface.h @@ -12,29 +12,112 @@ #pragma once #include "Histogram.h" +#include "Logger.h" +#include "TProfile.h" +#include "TProfile2D.h" + +#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 + /// + /// 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 { + + public: + template<class... Args> + TProfileAccessor(Args... args) : RootProfile(args...) + { + } + + /// \brief Sets fields from cbm::algo::qa::Prof1D + void SetFromQaProf(const QaProfile& prof); + }; + /// \class OnlineInterface /// \brief A collection of tools for online QA object conversions class OnlineInterface { public: - /// \brief Converts histogram H1 to ROOT histogram TH1D + /// \brief Converts histogram H1D to ROOT histogram TH1D /// \param hist 1D-histogram /// \note Allocates memory on heap for the ROOT histogram static TH1D* ROOTHistogram(const cbm::algo::qa::H1D& hist); - /// \brief Converts histogram H2 to ROOT histogram TH2D + /// \brief Converts histogram H2D to ROOT histogram TH2D /// \param hist 1D-histogram /// \note Allocates memory on heap for the ROOT histogram static TH2D* ROOTHistogram(const cbm::algo::qa::H2D& hist); + + /// \brief Converts profile Prof1D to ROOT TProfile + /// \param prof 1D-profile + /// \note Allocates memory on heap for the ROOT histogram + static TProfile* ROOTHistogram(const cbm::algo::qa::Prof1D& prof); + + /// \brief Converts profile Prof2D to ROOT TProfile2D + /// \param prof 2D-profile + /// \note Allocates memory on heap for the ROOT histogram + static TProfile2D* ROOTHistogram(const cbm::algo::qa::Prof2D& prof); }; } // namespace cbm::qa + + +namespace cbm::qa +{ + // ------------------------------------------------------------------------------------------------------------------- + // + template<class RootProfile, class QaProfile> + void TProfileAccessor<RootProfile, QaProfile>::SetFromQaProf(const QaProfile& prof) + { + 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); + } + } + 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); + } + } + this->fTsumwy = prof.GetTotSumWY(); + this->fTsumwy2 = prof.GetTotSumWY2(); + this->fTsumwxy = prof.GetTotSumWXY(); + } + this->fTsumwx = prof.GetTotSumWX(); + this->fTsumwx2 = prof.GetTotSumWX2(); + this->SetEntries(prof.GetEntries()); + } + +} // namespace cbm::qa