Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:57:28

0001 /***********************************************************************************\
0002 * (c) Copyright 1998-2022 CERN for the benefit of the LHCb and ATLAS collaborations *
0003 *                                                                                   *
0004 * This software is distributed under the terms of the Apache version 2 licence,     *
0005 * copied verbatim in the file "LICENSE".                                            *
0006 *                                                                                   *
0007 * In applying this licence, CERN does not waive the privileges and immunities       *
0008 * granted to it by virtue of its status as an Intergovernmental Organization        *
0009 * or submit itself to any jurisdiction.                                             *
0010 \***********************************************************************************/
0011 #pragma once
0012 
0013 #include <Gaudi/Accumulators.h>
0014 #include <Gaudi/MonitoringHub.h>
0015 #include <GaudiKernel/HistoDef.h>
0016 
0017 #include <array>
0018 #include <cmath>
0019 #include <fmt/format.h>
0020 #include <nlohmann/json.hpp>
0021 #include <string>
0022 #include <type_traits>
0023 #include <utility>
0024 #include <vector>
0025 
0026 namespace Gaudi::Accumulators {
0027 
0028   namespace details {
0029     template <std::size_t, typename T>
0030     using alwaysT = T;
0031     // get a tuple of n types the given Type, or directly the type for n = 1
0032     template <typename Type, unsigned int ND>
0033     struct GetTuple;
0034     template <typename Type, unsigned int ND>
0035     using GetTuple_t = typename GetTuple<Type, ND>::type;
0036     template <typename Type, unsigned int ND>
0037     struct GetTuple {
0038       using type =
0039           decltype( std::tuple_cat( std::declval<std::tuple<Type>>(), std::declval<GetTuple_t<Type, ND - 1>>() ) );
0040     };
0041     template <typename Type>
0042     struct GetTuple<Type, 1> {
0043       using type = std::tuple<Type>;
0044     };
0045 
0046     inline void requireValidTitle( std::string_view sv ) {
0047       if ( !sv.empty() && ( std::isspace( sv.back() ) || std::isspace( sv.front() ) ) ) {
0048         throw GaudiException(
0049             fmt::format( "Histogram title \'{}\' has whitespace at front or back -- please remove", sv ),
0050             "Gaudi::Accumulators", StatusCode::FAILURE );
0051       }
0052     }
0053   } // namespace details
0054 
0055   /**
0056    * A functor to extract weight, take a pair (valueTuple, weight) as input
0057    */
0058   struct ExtractWeight {
0059     template <typename Arithmetic>
0060     constexpr decltype( auto ) operator()( const std::pair<Arithmetic, Arithmetic>& v ) const noexcept {
0061       return v.second;
0062     }
0063   };
0064 
0065   /**
0066    * A Product functor, take a pair (value, weight) as input
0067    */
0068   struct WeightedProduct {
0069     template <typename Arithmetic>
0070     constexpr decltype( auto ) operator()( const std::pair<Arithmetic, Arithmetic>& v ) const noexcept {
0071       return v.first * v.second;
0072     }
0073   };
0074 
0075   /**
0076    * A WeightedSquare functor, take a pair (value, weight) as input
0077    */
0078   struct WeightedSquare {
0079     template <typename Arithmetic>
0080     constexpr decltype( auto ) operator()( const std::pair<Arithmetic, Arithmetic>& v ) const noexcept {
0081       return v.first * v.first * v.second;
0082     }
0083   };
0084 
0085   /**
0086    * WeightedCountAccumulator. A WeightedCountAccumulator is an Accumulator storing the number of provided values,
0087    * weighted. It basically sums the weights and thus is similar to the SumAccumulator except that it takes
0088    * a pair (valueTuple, weight) as input
0089    * @see Gaudi::Accumulators for detailed documentation
0090    */
0091   template <atomicity Atomicity, typename Arithmetic>
0092   struct WeightedCountAccumulator
0093       : GenericAccumulator<std::pair<Arithmetic, Arithmetic>, Arithmetic, Atomicity, ExtractWeight> {
0094     using Base = GenericAccumulator<std::pair<Arithmetic, Arithmetic>, Arithmetic, Atomicity, ExtractWeight>;
0095     using Base::Base;
0096     using Base::operator+=;
0097     //// overload of operator+= to be able to only give weight and no value
0098     WeightedCountAccumulator operator+=( const Arithmetic weight ) {
0099       *this += { Arithmetic{}, weight };
0100       return *this;
0101     }
0102     Arithmetic nEntries() const { return this->value(); }
0103   };
0104 
0105   /**
0106    * WeightedSumAccumulator. A WeightedSumAccumulator is an Accumulator storing a weighted sum of values.
0107    * It takes a pair (valueTuple, weight) and basically sums the product of the last item othe 2 part of its in put pair
0108    * : weight and value
0109    * @see Gaudi::Accumulators for detailed documentation
0110    */
0111   template <atomicity Atomicity, typename Arithmetic>
0112   struct WeightedSumAccumulator
0113       : GenericAccumulator<std::pair<Arithmetic, Arithmetic>, Arithmetic, Atomicity, WeightedProduct> {
0114     using GenericAccumulator<std::pair<Arithmetic, Arithmetic>, Arithmetic, Atomicity,
0115                              WeightedProduct>::GenericAccumulator;
0116     Arithmetic sum() const { return this->value(); }
0117   };
0118 
0119   /**
0120    * WeightedSquareAccumulator. A WeightedSquareAccumulator is an Accumulator storing a weighted sum of squared values.
0121    * It basically takes a pair (value, weight) as input and sums weight*value*value
0122    * @see Gaudi::Accumulators for detailed documentation
0123    */
0124   template <atomicity Atomicity, typename Arithmetic = double>
0125   struct WeightedSquareAccumulator
0126       : GenericAccumulator<std::pair<Arithmetic, Arithmetic>, Arithmetic, Atomicity, WeightedSquare> {
0127     using GenericAccumulator<std::pair<Arithmetic, Arithmetic>, Arithmetic, Atomicity,
0128                              WeightedSquare>::GenericAccumulator;
0129     Arithmetic sum2() const { return this->value(); };
0130   };
0131 
0132   /**
0133    * WeightedAveragingAccumulator. An AveragingAccumulator is an Accumulator able to compute an average
0134    * This implementation takes a pair (value, weight) as input
0135    * @see Gaudi::Accumulators for detailed documentation
0136    */
0137   template <atomicity Atomicity, typename Arithmetic>
0138   using WeightedAveragingAccumulator =
0139       AveragingAccumulatorBase<Atomicity, Arithmetic, WeightedCountAccumulator, WeightedSumAccumulator>;
0140 
0141   /**
0142    * WeightedSigmaAccumulator. A SigmaAccumulator is an Accumulator able to compute an average and variance/rms
0143    * This implementation takes a pair (value, weight) as input
0144    * @see Gaudi::Accumulators for detailed documentation
0145    */
0146   template <atomicity Atomicity, typename Arithmetic>
0147   using WeightedSigmaAccumulator =
0148       SigmaAccumulatorBase<Atomicity, Arithmetic, WeightedAveragingAccumulator, WeightedSquareAccumulator>;
0149 
0150   /**
0151    * Definition of an Histogram Axis
0152    */
0153   template <typename Arithmetic>
0154   struct Axis {
0155     Axis( unsigned int _nBins, Arithmetic _minValue, Arithmetic _maxValue, std::string _title = {},
0156           std::vector<std::string> _labels = {} )
0157         : nBins( _nBins )
0158         , minValue( _minValue )
0159         , maxValue( _maxValue )
0160         , title( std::move( _title ) )
0161         , labels( std::move( _labels ) )
0162         , ratio( _nBins / ( _maxValue - _minValue ) ) {
0163       details::requireValidTitle( title );
0164       for ( const auto& s : labels ) details::requireValidTitle( s );
0165     };
0166     explicit Axis( Gaudi::Histo1DDef const& def )
0167         : Axis( (unsigned int)def.bins(), def.lowEdge(), def.highEdge(), def.title() ){};
0168     /// number of bins for this Axis
0169     unsigned int nBins;
0170     /// min and max values on this axis
0171     Arithmetic minValue, maxValue;
0172     /// title of this axis
0173     std::string title;
0174     /// labels for the bins
0175     std::vector<std::string> labels;
0176     /**
0177      * precomputed ratio to convert a value into bin number
0178      * equal to nBins/(maxValue-minValue). Only used for floating Arithmetic
0179      */
0180     Arithmetic ratio;
0181 
0182     /// returns the bin number for a given value, ranging from 0 (underflow) to nBins+1 (overflow)
0183     unsigned int index( Arithmetic value ) const {
0184       // In case we use integer as Arithmetic type, we cannot use ratio for computing indices,
0185       // as ratios < 1.0 will simply be 0, so we have to pay the division in such a case
0186       int idx;
0187       if constexpr ( std::is_integral_v<Arithmetic> ) {
0188         idx = ( ( value - minValue ) * nBins / ( maxValue - minValue ) ) + 1;
0189       } else {
0190         idx = std::floor( ( value - minValue ) * ratio ) + 1;
0191       }
0192       return idx < 0 ? 0 : ( (unsigned int)idx > nBins ? nBins + 1 : (unsigned int)idx );
0193     }
0194     /// says whether the given value is within the range of the axis
0195     bool inAcceptance( Arithmetic value ) const { return value >= minValue && value <= maxValue; }
0196   };
0197 
0198   /// automatic conversion of the Axis type to json
0199   template <typename Arithmetic>
0200   void to_json( nlohmann::json& j, const Axis<Arithmetic>& axis ) {
0201     j = nlohmann::json{ { "nBins", axis.nBins },
0202                         { "minValue", axis.minValue },
0203                         { "maxValue", axis.maxValue },
0204                         { "title", axis.title } };
0205     if ( !axis.labels.empty() ) { j["labels"] = axis.labels; }
0206   }
0207 
0208   /// small class used as InputType for regular Histograms
0209   template <typename Arithmetic, unsigned int ND, unsigned int NIndex = ND>
0210   struct HistoInputType : std::array<Arithmetic, ND> {
0211     // allow construction from set of values
0212     template <class... ARGS, typename = typename std::enable_if_t<( sizeof...( ARGS ) == NIndex )>>
0213     HistoInputType( ARGS... args ) : std::array<Arithmetic, ND>{ static_cast<Arithmetic>( args )... } {}
0214     // The change on NIndex == 1 allow to have simpler syntax in that case, that is no tuple of one item
0215     using ValueType          = HistoInputType<Arithmetic, NIndex == 1 ? 1 : ND, NIndex>;
0216     using AxisArithmeticType = Arithmetic;
0217     using InternalType       = std::array<Arithmetic, ND>;
0218     unsigned int computeIndex( const std::array<Axis<Arithmetic>, NIndex>& axis ) const {
0219       unsigned int index = 0;
0220       for ( unsigned int j = 0; j < NIndex; j++ ) {
0221         unsigned int dim = NIndex - j - 1;
0222         // compute local index for a given dimension
0223         int localIndex = axis[dim].index( ( *this )[dim] );
0224         // compute global index. Bins are stored in a column first manner
0225         index *= ( axis[dim].nBins + 2 );
0226         index += localIndex;
0227       }
0228       return index;
0229     }
0230     bool inAcceptance( const std::array<Axis<Arithmetic>, NIndex>& axis ) const {
0231       for ( unsigned int dim = 0; dim < NIndex; dim++ ) {
0232         if ( !axis[dim].inAcceptance( ( *this )[dim] ) ) return false;
0233       }
0234       return true;
0235     }
0236     auto forInternalCounter() { return 1ul; }
0237     template <typename AxisType, long unsigned NAxis>
0238     static unsigned int computeTotNBins( std::array<AxisType, NAxis> axis ) {
0239       unsigned int nTotBins = 1;
0240       for ( unsigned int i = 0; i < NAxis; i++ ) { nTotBins *= ( axis[i].nBins + 2 ); }
0241       return nTotBins;
0242     }
0243   };
0244 
0245   /// specialization of HistoInputType for ND == 1 in order to have simpler syntax
0246   /// that is, no tuple of one item
0247   template <typename Arithmetic>
0248   class HistoInputType<Arithmetic, 1> {
0249   public:
0250     using ValueType          = HistoInputType;
0251     using AxisArithmeticType = Arithmetic;
0252     using InternalType       = Arithmetic;
0253     HistoInputType( Arithmetic a ) : value( a ) {}
0254     unsigned int computeIndex( const std::array<Axis<Arithmetic>, 1>& axis ) const { return axis[0].index( value ); }
0255     bool inAcceptance( const std::array<Axis<Arithmetic>, 1>& axis ) const { return axis[0].inAcceptance( value ); }
0256     Arithmetic& operator[]( int ) { return value; }
0257     operator Arithmetic() const { return value; }
0258     auto forInternalCounter() { return 1ul; }
0259     template <typename AxisType>
0260     static unsigned int computeTotNBins( std::array<AxisType, 1> axis ) {
0261       return axis[0].nBins + 2;
0262     }
0263 
0264   private:
0265     Arithmetic value;
0266   };
0267 
0268   /// small class used as InputType for weighted Histograms
0269   template <typename Arithmetic, unsigned int ND, unsigned int NIndex = ND>
0270   struct WeightedHistoInputType : std::pair<HistoInputType<Arithmetic, ND, NIndex>, Arithmetic> {
0271     // The change on NIndex == 1 allow to have simpler syntax in that case, that is no tuple of one item
0272     using ValueType          = HistoInputType<Arithmetic, NIndex == 1 ? 1 : ND, NIndex>;
0273     using AxisArithmeticType = Arithmetic;
0274     using std::pair<HistoInputType<Arithmetic, ND, NIndex>, Arithmetic>::pair;
0275     unsigned int computeIndex( const std::array<Axis<Arithmetic>, NIndex>& axis ) const {
0276       return this->first.computeIndex( axis );
0277     }
0278     unsigned int inAcceptance( const std::array<Axis<Arithmetic>, NIndex>& axis ) const {
0279       return this->first.inAcceptance( axis );
0280     }
0281     auto forInternalCounter() { return std::pair( this->first.forInternalCounter(), this->second ); }
0282     template <typename AxisType, long unsigned NAxis>
0283     static unsigned int computeTotNBins( std::array<AxisType, NAxis> axis ) {
0284       return HistoInputType<Arithmetic, ND, NIndex>::computeTotNBins( axis );
0285     }
0286   };
0287 
0288   /**
0289    * Internal Accumulator class dealing with Histograming. Templates parameters are :
0290    *  - Atomicity : none or full
0291    *  - Arithmetic : the arithmetic type used for values filled into the histogram
0292    *  - ND : the number of dimensions of the histogram.
0293    *    Note that ND is given as an integral_constant as it needs to be a type for the internal template
0294    *    business of the Counter (more precisely the Buffer class)
0295    *  - InputType : the type given as input of the Histogram. Typically (Weighted)HistoInputType
0296    *  - BaseAccumulator : the underlying accumulator used in each bin
0297    *
0298    * This accumulator is simply an array of BaseAccumulator, one per bin. The number of bins is
0299    * the product of the number of bins for each dimension, each of them increased by 2 for storing
0300    * the values under min and above max
0301    */
0302   template <atomicity Atomicity, typename InputType, typename Arithmetic, typename ND,
0303             template <atomicity Ato, typename Arith> typename BaseAccumulatorT>
0304   class HistogramingAccumulatorInternal {
0305     template <atomicity, typename, typename, typename, template <atomicity, typename> typename>
0306     friend class HistogramingAccumulatorInternal;
0307 
0308   public:
0309     using BaseAccumulator    = BaseAccumulatorT<Atomicity, Arithmetic>;
0310     using AxisArithmeticType = typename InputType::AxisArithmeticType;
0311     using AxisType           = Axis<AxisArithmeticType>;
0312     template <std::size_t... Is>
0313     HistogramingAccumulatorInternal( details::GetTuple_t<AxisType, ND::value> axis, std::index_sequence<Is...> )
0314         : m_axis{ std::get<Is>( axis )... }
0315         , m_totNBins{ InputType::computeTotNBins( m_axis ) }
0316         , m_value( new BaseAccumulator[m_totNBins] ) {
0317       reset();
0318     }
0319     template <atomicity ato>
0320     HistogramingAccumulatorInternal(
0321         construct_empty_t,
0322         const HistogramingAccumulatorInternal<ato, InputType, Arithmetic, ND, BaseAccumulatorT>& other )
0323         : m_axis( other.m_axis )
0324         , m_totNBins{ InputType::computeTotNBins( m_axis ) }
0325         , m_value( new BaseAccumulator[m_totNBins] ) {
0326       reset();
0327     }
0328     [[deprecated( "Use `++h1[x]`, `++h2[{x,y}]`, etc. instead." )]] HistogramingAccumulatorInternal&
0329     operator+=( InputType v ) {
0330       accumulator( v.computeIndex( m_axis ) ) += v.forInternalCounter();
0331       return *this;
0332     }
0333     void reset() {
0334       for ( unsigned int index = 0; index < m_totNBins; index++ ) accumulator( index ).reset();
0335     }
0336     template <atomicity ato>
0337     void mergeAndReset( HistogramingAccumulatorInternal<ato, InputType, Arithmetic, ND, BaseAccumulatorT>& other ) {
0338       assert( m_totNBins == other.m_totNBins );
0339       for ( unsigned int index = 0; index < m_totNBins; index++ ) {
0340         accumulator( index ).mergeAndReset( other.accumulator( index ) );
0341       }
0342     }
0343     [[nodiscard]] auto operator[]( typename InputType::ValueType v ) {
0344       return Buffer<BaseAccumulatorT, Atomicity, Arithmetic>{ accumulator( v.computeIndex( m_axis ) ) };
0345     }
0346 
0347     auto& axis() const { return m_axis; }
0348     auto  nBins( unsigned int i ) const { return m_axis[i].nBins; }
0349     auto  minValue( unsigned int i ) const { return m_axis[i].minValue; }
0350     auto  maxValue( unsigned int i ) const { return m_axis[i].maxValue; }
0351     auto  binValue( unsigned int i ) const { return accumulator( i ).value(); }
0352     auto  nEntries( unsigned int i ) const { return accumulator( i ).nEntries(); }
0353     auto  totNBins() const { return m_totNBins; }
0354 
0355   private:
0356     BaseAccumulator& accumulator( unsigned int index ) const {
0357       assert( index < m_totNBins );
0358       return m_value[index];
0359     }
0360     /// set of Axis of this Histogram
0361     std::array<AxisType, ND::value> m_axis;
0362     /// total number of bins in this histogram, under and overflow included
0363     unsigned int m_totNBins;
0364     /// Histogram content
0365     std::unique_ptr<BaseAccumulator[]> m_value;
0366   };
0367 
0368   /**
0369    * Class implementing a regular histogram accumulator
0370    *
0371    * Actually only an alias to HistogramingAccumulatorInternal with proper template parameters
0372    */
0373   template <atomicity Atomicity, typename Arithmetic, typename ND>
0374   using HistogramingAccumulator = HistogramingAccumulatorInternal<Atomicity, HistoInputType<Arithmetic, ND::value>,
0375                                                                   unsigned long, ND, IntegralAccumulator>;
0376 
0377   /**
0378    * Class implementing a weighted histogram accumulator
0379    *
0380    * Actually only an alias to HistogramingAccumulatorInternal with proper template parameters
0381    */
0382   template <atomicity Atomicity, typename Arithmetic, typename ND>
0383   using WeightedHistogramingAccumulator =
0384       HistogramingAccumulatorInternal<Atomicity, WeightedHistoInputType<Arithmetic, ND::value>, Arithmetic, ND,
0385                                       WeightedCountAccumulator>;
0386 
0387   /**
0388    * Class implementing a profile histogram accumulator
0389    *
0390    * Actually only an alias to HistogramingAccumulatorInternal with proper template parameters
0391    */
0392   template <atomicity Atomicity, typename Arithmetic, typename ND>
0393   using ProfileHistogramingAccumulator =
0394       HistogramingAccumulatorInternal<Atomicity, HistoInputType<Arithmetic, ND::value + 1, ND::value>, Arithmetic, ND,
0395                                       SigmaAccumulator>;
0396 
0397   /**
0398    * Class implementing a weighted profile histogram accumulator
0399    *
0400    * Actually only an alias to HistogramingAccumulatorInternal with proper template parameters
0401    */
0402   template <atomicity Atomicity, typename Arithmetic, typename ND>
0403   using WeightedProfileHistogramingAccumulator =
0404       HistogramingAccumulatorInternal<Atomicity, WeightedHistoInputType<Arithmetic, ND::value + 1, ND::value>,
0405                                       Arithmetic, ND, WeightedSigmaAccumulator>;
0406 
0407   /**
0408    * A base counter dealing with Histograms
0409    *
0410    * Main features of that Counter :
0411    *  - can be any number of dimensions. The dimension is its first template parameter
0412    *  - for each dimension, a triplet of values have to be given at
0413    *    construction : nbins, minValue, maxValue. These triplets have to be
0414    *    embedded into braces, as the constructor takes an array of them
0415    *  - the operator+= takes either an array of values (one per dimension)
0416    *    or a tuple<array of values, weight>. The value inside the bin
0417    *    corresponding to the given values is then increased by 1 or weight
0418    *  - the prefered syntax is to avoid operator+= and use operator[] to get a
0419    *    buffer on the bin you're updating. Syntax becomes :
0420    *        ++counter[{x,y}]   or   wcounter[{x,y]] += w
0421    *  - the Counter is templated by the types of the values given to
0422    *    operator+ and also by the type stored into the bins
0423    *  - the counter can be atomic or not and supports buffering. Note that
0424    *    the atomicity is classical eventual consistency. So each bin is
0425    *    atomically updated but bins are not garanted to be coherent when
0426    *    reading all of them back
0427    *  - profile histograms are also supported, operator+= takes one more
0428    *    value in the array of values in that case
0429    *
0430    * This base class is then aliases for the 4 standard cases of Histogram,
0431    * WeightedHistogram, ProfileHistogram and WeightedProfileHistogram
0432    *
0433    * Typical usage :
0434    * \code
0435    * Histogram<2, double, atomicity::full>
0436    *   counter{owner, "CounterName", "HistoTitle", {{nBins1, minVal1, maxVal1}, {nBins2, minVal2, maxVal2}}};
0437    * ++counter[{val1, val2}];    // prefered syntax
0438    * counter += {val1, val2};    // original syntax inherited from counters
0439    *
0440    * WeightedHistogram<2, double, atomicity::full>
0441    *   wcounter{owner, "CounterName", "HistoTitle", {{nBins1, minVal1, maxVal1}, {nBins2, minVal2, maxVal2}}};
0442    * wcounter[{val1, val2}] += w;    // prefered syntax
0443    * wcounter += {{val1, val2}, w};  // original syntax inherited from counters
0444    * \endcode
0445    *
0446    * When serialized to json, this counter uses new types histogram:Histogram:<prec>, histogram:ProfileHistogram:<prec>,
0447    * histogram:WeightedHistogram:<prec> and histrogram:WeightedProfileHistogram:<prec>
0448    * <prec> id described in Accumulators.h and decribes here the precision of the bin data.
0449    * All these types have the same fields, namely :
0450    *   dimension(integer), title(string), empty(bool), nEntries(integer), axis(array), bins(array)
0451    * where :
0452    *     + axis is an array of tuples, one per dimension, with content (nBins(integer), minValue(number),
0453    * maxValue(number), title(string))
0454    *     + bins is an array of values
0455    *       - The length of the array is the product of (nBins+2) for all axis
0456    *       - the +2 is because the bin 0 is the one for values below minValue and bin nBins+1 is the one for values
0457    * above maxValue bins are stored row first so we iterate first on highest dimension
0458    *       - the value is a number for non profile histograms
0459    *       - the value is of the form ( (nEntries(integer), sum(number) ), sum2(number) ) for profile histograms
0460    *         Note the pair with a pair as first entry
0461    */
0462   template <unsigned int ND, atomicity Atomicity, typename Arithmetic, const char* Type,
0463             template <atomicity, typename, typename> typename Accumulator, typename Seq>
0464   class HistogramingCounterBaseInternal;
0465   template <unsigned int ND, atomicity Atomicity, typename Arithmetic, const char* Type,
0466             template <atomicity, typename, typename> typename Accumulator, std::size_t... NDs>
0467   class HistogramingCounterBaseInternal<ND, Atomicity, Arithmetic, Type, Accumulator, std::index_sequence<NDs...>>
0468       : public BufferableCounter<Atomicity, Accumulator, Arithmetic, std::integral_constant<int, ND>> {
0469   public:
0470     using Parent           = BufferableCounter<Atomicity, Accumulator, Arithmetic, std::integral_constant<int, ND>>;
0471     using AccumulatorType  = Accumulator<Atomicity, Arithmetic, std::integral_constant<int, ND>>;
0472     using NumberDimensions = std::integral_constant<unsigned int, ND>;
0473     inline static const std::string typeString{ std::string{ Type } + ':' + typeid( Arithmetic ).name() };
0474     template <typename OWNER>
0475     HistogramingCounterBaseInternal( OWNER* owner, std::string const& name, std::string const& title,
0476                                      details::GetTuple_t<typename AccumulatorType::AxisType, ND> axis )
0477         : Parent( owner, name, *this, axis, std::make_index_sequence<ND>{} ), m_title( title ) {
0478       details::requireValidTitle( m_title );
0479     }
0480     template <typename OWNER>
0481     HistogramingCounterBaseInternal( OWNER* owner, std::string const& name, std::string const& title,
0482                                      details::alwaysT<NDs, typename AccumulatorType::AxisType>... allAxis )
0483         : HistogramingCounterBaseInternal( owner, name, title, std::make_tuple( allAxis... ) ) {}
0484     using Parent::print;
0485     template <typename stream>
0486     stream& printImpl( stream& o, bool /*tableFormat*/ ) const {
0487       o << ND << "D Histogram with config ";
0488       for ( unsigned int i = 0; i < ND; i++ ) {
0489         o << this->nBins( i ) << " " << this->minValue( i ) << " " << this->maxValue( i ) << " ";
0490       }
0491       return o;
0492     }
0493     std::ostream& print( std::ostream& o, bool tableFormat = false ) const override {
0494       return printImpl( o, tableFormat );
0495     }
0496     MsgStream&  print( MsgStream& o, bool tableFormat = false ) const override { return printImpl( o, tableFormat ); }
0497     friend void reset( HistogramingCounterBaseInternal& c ) { c.reset(); }
0498     friend void mergeAndReset( HistogramingCounterBaseInternal& h, HistogramingCounterBaseInternal& o ) {
0499       h.mergeAndReset( o );
0500     }
0501     friend void to_json( nlohmann::json& j, HistogramingCounterBaseInternal const& h ) {
0502       // get all bin values and compute total nbEntries
0503       std::vector<typename AccumulatorType::BaseAccumulator::OutputType> bins;
0504       bins.reserve( h.totNBins() );
0505       unsigned long totNEntries{ 0 };
0506       for ( unsigned int i = 0; i < h.totNBins(); i++ ) {
0507         bins.push_back( h.binValue( i ) );
0508         totNEntries += h.nEntries( i );
0509       }
0510       // build json
0511       j = { { "type", std::string( Type ) + ":" + typeid( Arithmetic ).name() },
0512             { "title", h.m_title },
0513             { "dimension", ND },
0514             { "empty", totNEntries == 0 },
0515             { "nEntries", totNEntries },
0516             { "axis", h.axis() },
0517             { "bins", bins } };
0518     }
0519 
0520   private:
0521     std::string const m_title;
0522   };
0523   template <unsigned int ND, atomicity Atomicity, typename Arithmetic, const char* Type,
0524             template <atomicity, typename, typename> typename Accumulator>
0525   using HistogramingCounterBase =
0526       HistogramingCounterBaseInternal<ND, Atomicity, Arithmetic, Type, Accumulator, std::make_index_sequence<ND>>;
0527 
0528   namespace naming {
0529     inline constexpr char histogramString[]                = "histogram:Histogram";
0530     inline constexpr char weightedHistogramString[]        = "histogram:WeightedHistogram";
0531     inline constexpr char profilehistogramString[]         = "histogram:ProfileHistogram";
0532     inline constexpr char weightedProfilehistogramString[] = "histogram:WeightedProfileHistogram";
0533   } // namespace naming
0534   /// standard histograming counter. See HistogramingCounterBase for details
0535   template <unsigned int ND, atomicity Atomicity = atomicity::full, typename Arithmetic = double>
0536   using Histogram =
0537       HistogramingCounterBase<ND, Atomicity, Arithmetic, naming::histogramString, HistogramingAccumulator>;
0538 
0539   /// standard histograming counter with weight. See HistogramingCounterBase for details
0540   template <unsigned int ND, atomicity Atomicity = atomicity::full, typename Arithmetic = double>
0541   using WeightedHistogram = HistogramingCounterBase<ND, Atomicity, Arithmetic, naming::weightedHistogramString,
0542                                                     WeightedHistogramingAccumulator>;
0543 
0544   /// profile histograming counter. See HistogramingCounterBase for details
0545   template <unsigned int ND, atomicity Atomicity = atomicity::full, typename Arithmetic = double>
0546   using ProfileHistogram = HistogramingCounterBase<ND, Atomicity, Arithmetic, naming::profilehistogramString,
0547                                                    ProfileHistogramingAccumulator>;
0548 
0549   /// weighted profile histograming counter. See HistogramingCounterBase for details
0550   template <unsigned int ND, atomicity Atomicity = atomicity::full, typename Arithmetic = double>
0551   using WeightedProfileHistogram =
0552       HistogramingCounterBase<ND, Atomicity, Arithmetic, naming::weightedProfilehistogramString,
0553                               WeightedProfileHistogramingAccumulator>;
0554 
0555 } // namespace Gaudi::Accumulators