Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-12 09:31:09

0001 // -*- C++ -*-
0002 //
0003 // This file is part of YODA -- Yet more Objects for Data Analysis
0004 // Copyright (C) 2008-2025 The YODA collaboration (see AUTHORS for details)
0005 //
0006 #ifndef YODA_FillableStorage_H
0007 #define YODA_FillableStorage_H
0008 
0009 #include "YODA/BinnedStorage.h"
0010 #include "YODA/Dbn.h"
0011 
0012 namespace YODA {
0013 
0014 
0015   /// @brief Type to adapter mapping used when user didn't provide type adapter.
0016   template <size_t FillDim, typename BinT>
0017   struct defaultAdapter; /// @note Error points here if there is no default adapter.
0018 
0019 
0020   /// @brief Default fill adapter for binned type double
0021   template <size_t FillDim, template<size_t, typename, typename> class BinT, typename BinningT, size_t N>
0022   struct defaultAdapter<FillDim, BinT<N, double, BinningT>> {
0023 
0024     using AdapterT = std::function<void(
0025       BinT<N, double, BinningT>&,
0026       typename BinningT::EdgeTypesTuple&&, double, double)>;
0027 
0028     AdapterT _adapter = [](auto& storedNumber, auto&& /* coords */,
0029                            double weight, double /* fraction */) {
0030       storedNumber = storedNumber + weight;
0031     };
0032   };
0033 
0034   namespace {
0035     /// @brief Aux method to create a tuple of doubles,
0036     /// used as additional Dbn coordinates in profiles
0037     template <size_t... Is>
0038     constexpr auto dblPadding(std::index_sequence<Is...>) {
0039       return std::tuple< std::decay_t<decltype((void)Is, std::declval<double>())>...>();
0040     }
0041   }
0042 
0043   /// @brief Default fill adapter for binned type Dbn<N>
0044   ///
0045   /// @note When non fundamental type is used, template parameter differ.
0046   template <size_t DbnN, template<size_t, typename, typename> class BinT, typename BinningT, size_t N>
0047   struct defaultAdapter<DbnN, BinT<N, Dbn<DbnN>, BinningT>> {
0048     static_assert(DbnN >= N, "Dimension of the Dbn needs to be at least as high as binning dimension!");
0049 
0050     /// The fill coordinates are a tuple of
0051     /// -) the bin edge types (in case of histograms)
0052     /// -) the bin edge types and additional doubles (in case of profiles)
0053     using FillCoords = decltype(std::tuple_cat(std::declval<typename BinningT::EdgeTypesTuple>(),
0054                      dblPadding(std::make_index_sequence<DbnN-BinningT::Dimension::value>{})));
0055 
0056     using AdapterT = std::function<void(BinT<N, Dbn<DbnN>, BinningT>&, FillCoords&&, double, double)>;
0057 
0058     /// @note This adapter nullifies discrete coordinates and transforms
0059     /// coordinate tuple to std::array<double, N> to pass it to the
0060     /// stored Dbn object. The coordinate is moved to avoid copying.
0061     /// In cases when continuous axis' edges are not of double type
0062     /// that may positively affect performance.
0063     AdapterT _adapter = [](auto& dbn, auto&& coords, double weight, double fraction) {
0064       using CoordsArrT = std::array<double, DbnN>;
0065       CoordsArrT dblCoords;
0066       size_t binIdx = dbn.index();
0067       auto nullifyDiscrete = [&dblCoords, &binIdx, &coords](auto I){
0068         using isArithmetic = typename BinningT::template is_Arithmetic<I>;
0069         dblCoords[I] = nullifyIfDiscCoord(std::move(std::get<I>(coords)),
0070                                           std::integral_constant<bool,
0071                                           isArithmetic::value>{}, binIdx);
0072       };
0073       MetaUtils::staticFor<BinningT::Dimension::value>(nullifyDiscrete);
0074       if constexpr (DbnN > BinningT::Dimension::value)
0075         dblCoords[N] = std::move(std::get<N>(coords));
0076       dbn.fill(std::move(dblCoords), weight, fraction);
0077     };
0078   };
0079 
0080 
0081   /// @brief FillableStorage, introduces FillAdapterT on top of BinnedStorage base class
0082   ///
0083   /// @note The additional abstraction layer is necessary to distinguish between binned objects
0084   /// that are "live" (i.e. fillable/incrementable, like a Histo1D) and those that are "dead"
0085   /// (e.g. a binned set of cross-section measurement points).
0086   template <size_t FillDim, typename BinContentT, typename... AxisT>
0087   class FillableStorage
0088         : public BinnedStorage<BinContentT, AxisT...> {
0089 
0090     static_assert((FillDim >= sizeof...(AxisT)),
0091                   "Fill dimension should be at least as large as the binning dimension.");
0092 
0093   protected:
0094     /// @brief Convenience alias to be used in constructor
0095     using BaseT = BinnedStorage<BinContentT, AxisT...>;
0096     using BinningT = typename BaseT::BinningT;
0097     using BinT = Bin<sizeof...(AxisT), BinContentT, BinningT>;
0098     using AdapterWrapperT = defaultAdapter<FillDim, BinT>;
0099     using FillableT = FillableStorage<FillDim, BinContentT, AxisT...>;
0100     using FillCoordsT = decltype(std::tuple_cat(std::declval<typename BinningT::EdgeTypesTuple>(),
0101                                 dblPadding(std::make_index_sequence<FillDim-sizeof...(AxisT)>{})));
0102 
0103   public:
0104 
0105     /// @brief Type of the fill coordinates
0106     using FillType = FillCoordsT;
0107 
0108     /// @brief Fill dimension
0109     using FillDimension = std::integral_constant<size_t, FillDim>;
0110 
0111     /// @brief Adapter type (type of lambda used to access stored object).
0112     ///
0113     /// BinT is a stored object type;
0114     /// FillCoordsT is a coordinates init list (or tuple) type;
0115     /// Doubles are for weight and fraction correspondingly.
0116     using FillAdapterT = std::function<void(BinT&, FillCoordsT&&, double, double)>;
0117 
0118     /// @name Constructors
0119     /// @{
0120 
0121     /// @brief Nullary constructor for unique pointers etc.
0122     FillableStorage(FillAdapterT adapter = AdapterWrapperT()._adapter)
0123         : BaseT(), _fillAdapter(adapter), _nancount(0), _nansumw(0.), _nansumw2(0.) { }
0124 
0125     /// @brief Constructs FillableStorage from Binning.
0126     FillableStorage(const BinningT& binning, FillAdapterT adapter = AdapterWrapperT()._adapter)
0127         : BaseT(binning), _fillAdapter(adapter), _nancount(0), _nansumw(0.), _nansumw2(0.) { }
0128 
0129     /// @brief Constructs FillableStorage from Binning. Rvalue.
0130     FillableStorage(BinningT&& binning, FillAdapterT adapter = AdapterWrapperT()._adapter)
0131         : BaseT(std::move(binning)), _fillAdapter(adapter), _nancount(0), _nansumw(0.), _nansumw2(0.) { }
0132 
0133     /// @brief Constructs binning from an adapter and vectors of axes' edges
0134     FillableStorage(const std::vector<AxisT>&... edges, FillAdapterT adapter = AdapterWrapperT()._adapter)
0135         : BaseT(edges...), _fillAdapter(adapter), _nancount(0), _nansumw(0.), _nansumw2(0.) { }
0136 
0137     /// @brief Constructs binning from an adapter and Rvalue vectors of axes' edges
0138     FillableStorage(std::vector<AxisT>&&... edges, FillAdapterT adapter = AdapterWrapperT()._adapter)
0139         : BaseT(std::move(edges)...), _fillAdapter(adapter), _nancount(0), _nansumw(0.), _nansumw2(0.) { }
0140 
0141     /// @brief Constructs binning from an adapter and a sequence of axes
0142     FillableStorage(const Axis<AxisT>&... axes, FillAdapterT adapter = AdapterWrapperT()._adapter)
0143         : BaseT(axes...), _fillAdapter(adapter), _nancount(0), _nansumw(0.), _nansumw2(0.) { }
0144 
0145     /// @brief Constructs binning from an adapter and a sequence of Rvalue axes
0146     FillableStorage(Axis<AxisT>&&... axes, FillAdapterT adapter = AdapterWrapperT()._adapter)
0147         : BaseT(std::move(axes)...), _fillAdapter(adapter), _nancount(0), _nansumw(0.), _nansumw2(0.) { }
0148 
0149     /// @brief Copy constructor.
0150     FillableStorage(const FillableStorage& other)
0151         : BaseT(other), _fillAdapter(other._fillAdapter),
0152           _nancount(other._nancount), _nansumw(other._nansumw), _nansumw2(other._nansumw2) { }
0153 
0154     /// @brief Move constructor.
0155     FillableStorage(FillableStorage&& other)
0156         : BaseT(std::move(other)), _fillAdapter(std::move(other._fillAdapter)),
0157           _nancount(std::move(other._nancount)), _nansumw(std::move(other._nansumw)),
0158           _nansumw2(std::move(other._nansumw2)) { }
0159 
0160     /// @}
0161 
0162     /// @name Methods
0163     /// @{
0164 
0165     /// @brief Triggers fill adapter on the bin corresponding to coords
0166     ///
0167     /// @note Accepts coordinates only as rvalue tuple. The tuple members
0168     /// are then moved (bringing tuple member to unspecified state) later in adapters.
0169     template <size_t... Is>
0170     int fill(FillCoordsT&& coords, std::index_sequence<Is...>,
0171              const double weight = 1.0, const double fraction = 1.0) noexcept {
0172 
0173       // make sure the user isn't trying to fill with NaN ...
0174       // include all fill coordinates here
0175       if (containsNan(coords)) {
0176         _nancount += 1;
0177         _nansumw += weight*fraction;
0178         _nansumw2 += sqr(weight*fraction);
0179         return -1;
0180       }
0181       // select binned coordinates (possibly a subset of fill coordinates)
0182       auto binCoords = std::tuple<AxisT...>(std::get<Is>(coords)...);
0183       const size_t binIdx = FillableT::_binning.globalIndexAt(binCoords);
0184       _fillAdapter(BaseT::bin(binIdx), std::move(coords), weight, fraction);
0185       return int(binIdx);
0186     }
0187 
0188     /// @brief Triggers fill adapter on the bin corresponding to coords
0189     int fill(FillCoordsT&& coords, const double weight = 1.0, const double fraction = 1.0) noexcept {
0190       return fill(std::move(coords), std::make_index_sequence<sizeof...(AxisT)>{}, weight, fraction);
0191     }
0192 
0193 
0194     /// @name Utilities
0195     /// @{
0196 
0197     /// @brief Returns the dimension of the filling tuple
0198     size_t fillDim() const { return FillDim; }
0199 
0200     size_t nanCount() const { return _nancount; }
0201 
0202     double nanSumW() const { return _nansumw; }
0203 
0204     double nanSumW2() const { return _nansumw2; }
0205 
0206     void setNanLog(size_t count, double sumw, double sumw2) {
0207       _nancount = count;
0208       _nansumw  = sumw;
0209       _nansumw2 = sumw2;
0210     }
0211 
0212     /// @brief Reset the Fillable.
0213     ///
0214     /// Keep the binning but set all bin contents and related quantities to zero
0215     void reset() noexcept {
0216       _nancount = 0;
0217       _nansumw = _nansumw2 = 0.;
0218       BaseT::clearBins();
0219     }
0220 
0221     /// @}
0222 
0223     /// @name Operators
0224     /// @{
0225 
0226     /// @brief Copy assignment
0227     FillableStorage& operator = (const FillableStorage& other) noexcept {
0228       if (this != &other) {
0229         _fillAdapter = other._fillAdapter;
0230         _nancount    = other._nancount;
0231         _nansumw     = other._nansumw;
0232         _nansumw2    = other._nansumw2;
0233         BaseT::operator=(other);
0234       }
0235       return *this;
0236     }
0237 
0238     /// @brief Move assignment
0239     FillableStorage& operator = (FillableStorage&& other) noexcept {
0240       if (this != &other) {
0241         _fillAdapter = std::move(other._fillAdapter);
0242         _nancount    = std::move(other._nancount);
0243         _nansumw     = std::move(other._nansumw);
0244         _nansumw2    = std::move(other._nansumw2);
0245         BaseT::operator=(std::move(other));
0246       }
0247       return *this;
0248     }
0249 
0250     /// @brief Add another BinnedStorage to this one.
0251     FillableStorage& operator += (const FillableStorage& other) {
0252       if (*this != other)
0253         throw std::logic_error("YODA::BinnedStorage<" + std::to_string(sizeof...(AxisT)) +\
0254                                ">: Cannot add BinnedStorages with different binnings.");
0255       size_t i = 0;
0256       for (auto& bin : FillableT::bins(true)) {
0257         bin += other.bin(i++);
0258       }
0259 
0260       return *this;
0261     }
0262 
0263     /// @brief Subtract another BinnedStorage from this one.
0264     FillableStorage& operator -= (const FillableStorage& other) {
0265       if (*this != other)
0266         throw std::logic_error("YODA::FillableStorage<" + std::to_string(sizeof...(AxisT)) +\
0267                                ">: Cannot substract FillableStorages with different binnings.");
0268 
0269       size_t i = 0;
0270       for (auto& bin : FillableT::bins(true)) {
0271         bin -= other.bin(i++);
0272       }
0273 
0274       return *this;
0275     }
0276 
0277     /// @}
0278 
0279     private:
0280 
0281       /// @brief Adapter used to access stored objects
0282       FillAdapterT _fillAdapter;
0283 
0284       size_t _nancount;
0285 
0286       double _nansumw, _nansumw2;
0287 
0288   };
0289 
0290 
0291 } // namespace YODA
0292 
0293 #endif