Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-04-03 08:51:51

0001 /***********************************************************************************\
0002 * (c) Copyright 1998-2024 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/StaticHistogram.h>
0014 #include <Gaudi/MonitoringHub.h>
0015 #include <GaudiKernel/GaudiException.h>
0016 #include <TDirectory.h>
0017 #include <TFile.h>
0018 #include <TH1.h>
0019 #include <TH1D.h>
0020 #include <TH2D.h>
0021 #include <TH3D.h>
0022 #include <TProfile.h>
0023 #include <TProfile2D.h>
0024 #include <TProfile3D.h>
0025 #include <algorithm>
0026 #include <functional>
0027 #include <gsl/span>
0028 #include <nlohmann/json.hpp>
0029 #include <range/v3/numeric/accumulate.hpp>
0030 #include <range/v3/range/conversion.hpp>
0031 #include <range/v3/view/split_when.hpp>
0032 #include <range/v3/view/transform.hpp>
0033 #include <string>
0034 #include <vector>
0035 
0036 // upstream has renamed namespace ranges::view -> ranges::views
0037 #if RANGE_V3_VERSION < 900
0038 namespace ranges::views {
0039   using namespace ranges::view;
0040 }
0041 #endif
0042 
0043 namespace Gaudi::Histograming::Sink {
0044 
0045   /**
0046    * templated Traits dealing with Root Histogram filling for standard histograms
0047    *
0048    * Specializatoins of the Traits type must provide the following static functions :
0049    *   - Histo create( std::string& name, std::string& title, Axis& axis... )
0050    *       it should intanciate a new ROOT histogram instance and return it
0051    *   - void fillMetaData( Histo& histo, nlohmann::json const& jsonAxis, unsigned int nentries)
0052    *       it should fill metadata in the ROOT histo histogram provides from the list of axis number of entries
0053    *   - void fill( Histo& histo, unsigned int nbins, const WeightType& weight )
0054    *       it should fill the given number of bin with the given values
0055    * Last point, it should have a static constexpr unsigned int Dimension
0056    */
0057   template <bool isProfile, typename RootHisto, unsigned int N>
0058   struct Traits;
0059 
0060   /**
0061    * generic function to convert json to a ROOT Histogram
0062    *
0063    * returns the Root histogram and the dir where to save it in the Root file
0064    * This may be different from input dir in case name has slashes
0065    */
0066   template <typename Traits>
0067   std::tuple<typename Traits::Histo, std::string> jsonToRootHistogram( std::string& dir, std::string& name,
0068                                                                        nlohmann::json const& j );
0069 
0070   /**
0071    * generic function to convert a ROOT Histogram to json
0072    *
0073    * essentially used for backward compatibility of old HistogramService with MonitoringHub
0074    */
0075   template <typename Histo>
0076   nlohmann::json rootHistogramTojson( Histo const& );
0077 
0078   /**
0079    * generic method to save histograms to files, based on Traits
0080    */
0081   template <typename Traits>
0082   void saveRootHisto( TFile& file, std::string dir, std::string name, nlohmann::json const& j );
0083 
0084   /**
0085    * generic method to save regular histograms to files
0086    *
0087    * Can be used in most cases as the handler function to register into Sink::Base
0088    * contains all the boiler plate code and redirects specific code to the adapted Traits template
0089    */
0090   template <unsigned int N, bool isProfile, typename ROOTHisto>
0091   void saveRootHisto( TFile& file, std::string dir, std::string name, nlohmann::json const& j );
0092 
0093   namespace details {
0094 
0095     /// Small helper struct representing the Axis of an Histogram
0096     struct Axis {
0097       unsigned int nBins;
0098       double       minValue;
0099       double       maxValue;
0100       std::string  title;
0101     };
0102 
0103     /// extract an Axis from json data
0104     inline Axis jsonToAxis( nlohmann::json& jAxis ) {
0105       return { jAxis.at( "nBins" ).get<unsigned int>(), jAxis.at( "minValue" ).get<double>(),
0106                jAxis.at( "maxValue" ).get<double>(),
0107                ";" + jAxis.at( "title" ).get<std::string>() }; // ";" to prepare concatenations of titles
0108     }
0109 
0110     /**
0111      * handles cases where name includes '/' character(s) and move needed part of it
0112      * to dir. Also handle case of absolute names
0113      */
0114     inline void fixNameAndDir( std::string& name, std::string& dir ) {
0115       if ( name[0] == '/' ) {
0116         dir  = "";
0117         name = name.substr( 1 );
0118       }
0119       // take into account the case where name contains '/'s (e.g. "Group/Name") by
0120       // moving the prefix into dir
0121       if ( auto pos = name.rfind( '/' ); pos != std::string::npos ) {
0122         dir += '/' + name.substr( 0, pos );
0123         name = name.substr( pos + 1 );
0124       }
0125     }
0126 
0127     /**
0128      * generic function to convert json to a ROOT Histogram - internal implementation
0129      */
0130     template <typename Traits, std::size_t... index>
0131     std::tuple<typename Traits::Histo, std::string> jsonToRootHistogramInternal( std::string& dir, std::string& name,
0132                                                                                  nlohmann::json const& j,
0133                                                                                  std::index_sequence<index...> ) {
0134       // extract data from json
0135       auto jsonAxis = j.at( "axis" );
0136       auto axis     = std::array{ jsonToAxis( jsonAxis[index] )... };
0137       auto weights  = j.at( "bins" ).get<typename Traits::WeightType>();
0138       auto title    = j.at( "title" ).get<std::string>();
0139       auto nentries = j.at( "nEntries" ).get<unsigned int>();
0140       // weird way ROOT has to give titles to axis
0141       title += ( axis[index].title + ... );
0142       // compute total number of bins, multiplying bins per axis
0143       auto totNBins = ( ( axis[index].nBins + 2 ) * ... );
0144       // fix name and dir if needed
0145       fixNameAndDir( name, dir );
0146       // Create Root histogram calling constructors with the args tuple
0147       auto histo = Traits::create( name, title, axis[index]... );
0148       // fill Histo
0149       Traits::fill( histo, totNBins, weights );
0150       // in case we have sums, overwrite them in the histogram with our more precise values
0151       // FIXME This is only supporting regular histograms. It won't work in case of weighted histograms
0152       if constexpr ( sizeof...( index ) == 1 ) {
0153         if ( j.find( "sum" ) != j.end() ) {
0154           double s[13];
0155           s[0] = j.at( "nTotEntries" ).get<double>();
0156           s[1] = j.at( "nTotEntries" ).get<double>();
0157           s[2] = j.at( "sum" ).get<double>();
0158           s[3] = j.at( "sum2" ).get<double>();
0159           histo.PutStats( s );
0160         }
0161       } else {
0162         if ( j.find( "sumx" ) != j.end() ) {
0163           double s[13];
0164           s[0] = j.at( "nTotEntries" ).get<double>();
0165           s[1] = j.at( "nTotEntries" ).get<double>();
0166           s[2] = j.at( "sumx" ).get<double>();
0167           s[3] = j.at( "sumx2" ).get<double>();
0168           s[4] = j.at( "sumy" ).get<double>();
0169           s[5] = j.at( "sumy2" ).get<double>();
0170           s[6] = j.at( "sumxy" ).get<double>();
0171           if constexpr ( sizeof...( index ) > 2 ) {
0172             s[7]  = j.at( "sumz" ).get<double>();
0173             s[8]  = j.at( "sumz2" ).get<double>();
0174             s[9]  = j.at( "sumxz" ).get<double>();
0175             s[10] = j.at( "sumyz" ).get<double>();
0176           }
0177           histo.PutStats( s );
0178         }
0179       }
0180       // fill histo metadata, e.g. bins and number of entries
0181       Traits::fillMetaData( histo, jsonAxis, nentries );
0182       return { histo, dir };
0183     }
0184 
0185     /**
0186      * Common base for Traits dealing with Histogram conversions to Root
0187      * Provides generic implementation for creating the histogram and filling meta data
0188      * The filling (method fill) is not implemented
0189      */
0190     template <typename RootHisto, unsigned int N>
0191     struct TraitsBase {
0192       static constexpr unsigned int Dimension{ N };
0193       // hleper method for the creation of the Root histogram, using an index_sequence for handling the axis
0194       template <typename AxisArray, std::size_t... index>
0195       static RootHisto create( std::string& name, std::string& title, AxisArray axis, std::index_sequence<index...> ) {
0196         return std::make_from_tuple<RootHisto>(
0197             std::tuple_cat( std::tuple{ name.c_str(), title.c_str() },
0198                             std::tuple{ std::get<index>( axis ).nBins, std::get<index>( axis ).minValue,
0199                                         std::get<index>( axis ).maxValue }... ) );
0200       }
0201       // Generic creation of the Root histogram from name, title and a set of axis
0202       template <typename... Axis>
0203       static RootHisto create( std::string& name, std::string& title, Axis&... axis ) {
0204         return create( name, title, std::make_tuple( axis... ), std::make_index_sequence<sizeof...( Axis )>() );
0205       }
0206       // Generic fill of the metadata of the Root histogram from json data
0207       static void fillMetaData( RootHisto& histo, nlohmann::json const& jsonAxis, unsigned int nentries ) {
0208         auto try_set_bin_labels = [&histo, &jsonAxis]( auto idx ) {
0209           TAxis* axis = nullptr;
0210           switch ( idx ) {
0211           case 0:
0212             axis = histo.GetXaxis();
0213             break;
0214           case 1:
0215             axis = histo.GetYaxis();
0216             break;
0217           case 2:
0218             axis = histo.GetZaxis();
0219             break;
0220           default:
0221             break;
0222           }
0223           if ( axis && jsonAxis[idx].contains( "labels" ) ) {
0224             const auto labels = jsonAxis[idx].at( "labels" );
0225             for ( unsigned int i = 0; i < labels.size(); i++ ) {
0226               axis->SetBinLabel( i + 1, labels[i].template get<std::string>().c_str() );
0227             }
0228           }
0229           if ( axis && jsonAxis[idx].contains( "xbins" ) ) {
0230             const auto xbins = jsonAxis[idx].at( "xbins" );
0231             axis->Set( xbins.size() - 1, xbins.template get<std::vector<double>>().data() );
0232           }
0233         };
0234         for ( unsigned int i = 0; i < jsonAxis.size(); i++ ) try_set_bin_labels( i );
0235         // Fix the number of entries, wrongly computed by ROOT from the numer of calls to fill
0236         histo.SetEntries( nentries );
0237       }
0238     };
0239 
0240     /// helper Wrapper around TProfileX to be able to fill it
0241     template <typename TP>
0242     struct ProfileWrapper : TP {
0243       template <typename... Args>
0244       ProfileWrapper( Args&&... args ) : TP( std::forward<Args>( args )... ) {
0245         // When the static function TH1::SetDefaultSumw2 had been called (passing true), `Sumw2(true)` is automatically
0246         // called by the constructor of TProfile. This is a problem because in the profiles in Gaudi::Accumulators we do
0247         // not keep track of the sum of squares of weights and consequently don't set fBinSumw2 of the TProfile, which
0248         // in turn leads to a self-inconsistent TProfile object. The "fix" is to disable sum of squares explicitly.
0249         this->Sumw2( false );
0250       }
0251       ProfileWrapper( ProfileWrapper const& )             = delete;
0252       ProfileWrapper& operator=( ProfileWrapper const& )  = delete;
0253       ProfileWrapper( ProfileWrapper const&& )            = delete;
0254       ProfileWrapper& operator=( ProfileWrapper const&& ) = delete;
0255       void            setBinNEntries( Int_t i, Int_t n ) { this->fBinEntries.fArray[i] = n; }
0256       void            setBinW2( Int_t i, Double_t v ) { this->fSumw2.fArray[i] = v; }
0257       Double_t        getBinW2( Int_t i ) { return this->fSumw2.fArray[i]; }
0258     };
0259 
0260     /**
0261      * changes to the ROOT directory given in the current ROOT file and returns
0262      * the current directory before the change
0263      */
0264     inline TDirectory* changeDir( TFile& file, std::string dir ) {
0265       // remember the current directory
0266       auto previousDir = gDirectory;
0267       // find or create the directory for the histogram
0268       using namespace ranges;
0269       auto is_delimiter        = []( auto c ) { return c == '/' || c == '.'; };
0270       auto transform_to_string = views::transform( []( auto&& rng ) { return rng | to<std::string>; } );
0271       auto currentDir          = accumulate( dir | views::split_when( is_delimiter ) | transform_to_string,
0272                                              file.GetDirectory( "" ), []( auto current, auto&& dir_level ) {
0273                                       if ( current ) {
0274                                         // try to get next level
0275                                         auto nextDir = current->GetDirectory( dir_level.c_str() );
0276                                         // if it does not exist, create it
0277                                         if ( !nextDir ) nextDir = current->mkdir( dir_level.c_str() );
0278                                         // move to next level
0279                                         current = nextDir;
0280                                       }
0281                                       return current;
0282                                     } );
0283       if ( !currentDir )
0284         throw GaudiException( "Could not create directory " + dir, "Histogram::Sink::Root", StatusCode::FAILURE );
0285       // switch to the directory
0286       currentDir->cd();
0287       return previousDir;
0288     }
0289 
0290     template <typename Histo>
0291     nlohmann::json allAxisTojson( Histo const& h ) {
0292       if constexpr ( std::is_base_of_v<TH3D, Histo> ) {
0293         return { *h.GetXaxis(), *h.GetYaxis(), *h.GetZaxis() };
0294       } else if constexpr ( std::is_base_of_v<TProfile2D, Histo> || std::is_base_of_v<TH2D, Histo> ) {
0295         return { *h.GetXaxis(), *h.GetYaxis() };
0296       } else {
0297         return { *h.GetXaxis() };
0298       }
0299     }
0300 
0301     template <typename Histo>
0302     nlohmann::json binsTojson( Histo const& h ) {
0303       if constexpr ( std::is_base_of_v<TProfile, Histo> || std::is_base_of_v<TProfile2D, Histo> ) {
0304         nlohmann::json j;
0305         // ROOT TProfile interface being completely inconsistent, we have to play
0306         // with different ways to access values of the histogram... one per value !
0307         auto* sums  = h.GetArray();
0308         auto* sums2 = h.GetSumw2();
0309         for ( unsigned long n = 0; n < (unsigned long)h.GetSize(); n++ ) {
0310           j.push_back( nlohmann::json{ { h.GetBinEntries( n ), sums[n] }, sums2->At( n ) } );
0311         }
0312         return j;
0313       } else {
0314         return gsl::span{ h.GetArray(), (unsigned long)h.GetSize() };
0315       }
0316     }
0317 
0318     /// automatic translation of Root Histograms to json
0319     template <typename Histo>
0320     nlohmann::json rootHistogramToJson( Histo const& h ) {
0321       bool        isProfile = std::is_base_of_v<TProfile, Histo> || std::is_base_of_v<TProfile2D, Histo>;
0322       std::string type      = isProfile ? "histogram:ProfileHistogram:double" : "histogram:Histogram:double";
0323       auto        j         = nlohmann::json{ { "type", type },
0324                                               { "title", h.GetTitle() },
0325                                               { "dimension", h.GetDimension() },
0326                                               { "empty", (int)h.GetEntries() == 0 },
0327                                               { "nEntries", (int)h.GetEntries() },
0328                                               { "axis", allAxisTojson( h ) },
0329                                               { "bins", binsTojson( h ) } };
0330       if ( !isProfile ) {
0331         double s[13];
0332         h.GetStats( s );
0333         if ( h.GetDimension() == 1 ) {
0334           j["nTotEntries"] = s[0];
0335           j["sum"]         = s[2];
0336           j["sum2"]        = s[3];
0337           j["mean"]        = s[2] / s[0];
0338         } else {
0339           j["nTotEntries"] = s[0];
0340           j["sumx"]        = s[2];
0341           j["sumx2"]       = s[3];
0342           j["meanx"]       = s[2] / s[0];
0343           j["sumy"]        = s[4];
0344           j["sumy2"]       = s[5];
0345           j["sumxy"]       = s[6];
0346           j["meany"]       = s[4] / s[0];
0347           if ( h.GetDimension() >= 3 ) {
0348             j["sumz"]  = s[7];
0349             j["sumz2"] = s[8];
0350             j["sumxz"] = s[9];
0351             j["sumyz"] = s[10];
0352             j["meanz"] = s[7] / s[0];
0353           }
0354         }
0355       }
0356       return j;
0357     }
0358 
0359   } // namespace details
0360 
0361   /**
0362    * Specialization of Traits dealing with non profile Root Histograms
0363    */
0364   template <typename RootHisto, unsigned int N>
0365   struct Traits<false, RootHisto, N> : details::TraitsBase<RootHisto, N> {
0366     using Histo      = RootHisto;
0367     using WeightType = std::vector<double>;
0368     static void fill( Histo& histo, unsigned int nbins, const WeightType& weight ) {
0369       for ( unsigned int i = 0; i < nbins; i++ ) { histo.SetBinContent( i, weight[i] ); }
0370     }
0371   };
0372 
0373   /**
0374    * Specialization of Traits dealing with profile Root Histograms
0375    */
0376   template <typename RootHisto, unsigned int N>
0377   struct Traits<true, RootHisto, N> : details::TraitsBase<details::ProfileWrapper<RootHisto>, N> {
0378     using Histo      = details::ProfileWrapper<RootHisto>;
0379     using WeightType = std::vector<std::tuple<std::tuple<unsigned int, double>, double>>;
0380     static constexpr void fill( Histo& histo, unsigned int nbins, const WeightType& weight ) {
0381       for ( unsigned int i = 0; i < nbins; i++ ) {
0382         auto [c, sumWeight2]       = weight[i];
0383         auto [nEntries, sumWeight] = c;
0384         histo.setBinNEntries( i, nEntries );
0385         histo.SetBinContent( i, sumWeight );
0386         histo.setBinW2( i, sumWeight2 );
0387       }
0388     };
0389   };
0390 
0391   template <typename Traits>
0392   std::tuple<typename Traits::Histo, std::string> jsonToRootHistogram( std::string& dir, std::string& name,
0393                                                                        nlohmann::json const& j ) {
0394     return details::jsonToRootHistogramInternal<Traits>( dir, name, j, std::make_index_sequence<Traits::Dimension>() );
0395   }
0396 
0397   template <typename Traits>
0398   void saveRootHisto( TFile& file, std::string dir, std::string name, nlohmann::json const& j ) {
0399     // get the Root histogram
0400     auto [histo, newDir] = jsonToRootHistogram<Traits>( dir, name, j );
0401     // Change to the proper directory in the ROOT file
0402     auto previousDir = details::changeDir( file, newDir );
0403     // write to file
0404     histo.Write();
0405     // switch back to the previous directory
0406     previousDir->cd();
0407   }
0408 
0409   template <unsigned int N, bool isProfile, typename ROOTHisto>
0410   void saveRootHisto( TFile& file, std::string dir, std::string name, nlohmann::json const& j ) {
0411     saveRootHisto<Traits<isProfile, ROOTHisto, N>>( file, std::move( dir ), std::move( name ), j );
0412   }
0413 
0414   /// Direct conversion of 1D histograms from Gaudi to ROOT format
0415   template <Accumulators::atomicity Atomicity = Accumulators::atomicity::full, typename Arithmetic = double>
0416   details::ProfileWrapper<TProfile> profileHisto1DToRoot( std::string name, Monitoring::Hub::Entity const& ent ) {
0417     // get original histogram from the Entity
0418     auto const& gaudiHisto =
0419         *reinterpret_cast<Gaudi::Accumulators::StaticProfileHistogram<1, Atomicity, Arithmetic>*>( ent.id() );
0420     // convert to Root
0421     auto const&                       gaudiAxis = gaudiHisto.template axis<0>();
0422     details::ProfileWrapper<TProfile> histo{ name.c_str(), gaudiHisto.title().c_str(), gaudiAxis.numBins(),
0423                                              gaudiAxis.minValue(), gaudiAxis.maxValue() };
0424     histo.Sumw2();
0425     unsigned long totNEntries{ 0 };
0426     for ( unsigned int i = 0; i < gaudiHisto.totNBins(); i++ ) { totNEntries += gaudiHisto.nEntries( i ); }
0427     if ( !gaudiAxis.labels().empty() ) {
0428       auto& axis = *histo.GetXaxis();
0429       for ( unsigned int i = 0; i < gaudiAxis.labels().size(); i++ ) {
0430         axis.SetBinLabel( i + 1, gaudiAxis.labels()[i].c_str() );
0431       }
0432     }
0433     for ( unsigned int i = 0; i < gaudiHisto.totNBins(); i++ ) {
0434       auto const& [tmp, sumw2] = gaudiHisto.binValue( i );
0435       auto const& [nent, sumw] = tmp;
0436       histo.SetBinContent( i, sumw );
0437       histo.setBinNEntries( i, nent );
0438       histo.setBinW2( i, sumw2 );
0439     }
0440     histo.SetEntries( totNEntries );
0441     return histo;
0442   }
0443 
0444   /// Direct conversion of 2D histograms from Gaudi to ROOT format
0445   template <Accumulators::atomicity Atomicity = Accumulators::atomicity::full, typename Arithmetic = double>
0446   details::ProfileWrapper<TProfile2D> profileHisto2DToRoot( std::string name, Monitoring::Hub::Entity const& ent ) {
0447     // get original histogram from the Entity
0448     auto const& gaudiHisto =
0449         *reinterpret_cast<Gaudi::Accumulators::StaticProfileHistogram<2, Atomicity, Arithmetic>*>( ent.id() );
0450     // convert to Root
0451     auto const&                         gaudiXAxis = gaudiHisto.template axis<0>();
0452     auto const&                         gaudiYAxis = gaudiHisto.template axis<1>();
0453     details::ProfileWrapper<TProfile2D> histo{ name.c_str(),          gaudiHisto.title().c_str(), gaudiXAxis.numBins(),
0454                                                gaudiXAxis.minValue(), gaudiXAxis.maxValue(),      gaudiYAxis.numBins(),
0455                                                gaudiYAxis.minValue(), gaudiYAxis.maxValue() };
0456     histo.Sumw2();
0457     if ( !gaudiXAxis.labels().empty() ) {
0458       auto& axis = *histo.GetXaxis();
0459       for ( unsigned int i = 0; i < gaudiXAxis.labels().size(); i++ ) {
0460         axis.SetBinLabel( i + 1, gaudiXAxis.labels()[i].c_str() );
0461       }
0462     }
0463     if ( !gaudiYAxis.labels().empty() ) {
0464       auto& axis = *histo.GetYaxis();
0465       for ( unsigned int i = 0; i < gaudiYAxis.labels().size(); i++ ) {
0466         axis.SetBinLabel( i + 1, gaudiYAxis.labels()[i].c_str() );
0467       }
0468     }
0469     for ( unsigned int i = 0; i < gaudiHisto.totNBins(); i++ ) {
0470       auto const& [tmp, sumw2] = gaudiHisto.binValue( i );
0471       auto const& [nent, sumw] = tmp;
0472       histo.SetBinContent( i, sumw );
0473       histo.setBinNEntries( i, nent );
0474       histo.setBinW2( i, sumw2 );
0475     }
0476     unsigned long totNEntries{ 0 };
0477     for ( unsigned int i = 0; i < gaudiHisto.totNBins(); i++ ) { totNEntries += gaudiHisto.nEntries( i ); }
0478     histo.SetEntries( totNEntries );
0479     return histo;
0480   }
0481 
0482   /// Direct conversion of 3D histograms from Gaudi to ROOT format
0483   template <Accumulators::atomicity Atomicity = Accumulators::atomicity::full, typename Arithmetic = double>
0484   details::ProfileWrapper<TProfile3D> profileHisto3DToRoot( std::string name, Monitoring::Hub::Entity const& ent ) {
0485     // get original histogram from the Entity
0486     auto const& gaudiHisto =
0487         *reinterpret_cast<Gaudi::Accumulators::StaticProfileHistogram<3, Atomicity, Arithmetic>*>( ent.id() );
0488     // convert to Root
0489     auto const&                         gaudiXAxis = gaudiHisto.template axis<0>();
0490     auto const&                         gaudiYAxis = gaudiHisto.template axis<1>();
0491     auto const&                         gaudiZAxis = gaudiHisto.template axis<2>();
0492     details::ProfileWrapper<TProfile3D> histo{ name.c_str(),          gaudiHisto.title().c_str(), gaudiXAxis.numBins(),
0493                                                gaudiXAxis.minValue(), gaudiXAxis.maxValue(),      gaudiYAxis.numBins(),
0494                                                gaudiYAxis.minValue(), gaudiYAxis.maxValue(),      gaudiZAxis.numBins(),
0495                                                gaudiZAxis.minValue(), gaudiZAxis.maxValue() };
0496     histo.Sumw2();
0497     if ( !gaudiXAxis.labels().empty() ) {
0498       auto& axis = *histo.GetXaxis();
0499       for ( unsigned int i = 0; i < gaudiXAxis.labels().size(); i++ ) {
0500         axis.SetBinLabel( i + 1, gaudiXAxis.labels()[i].c_str() );
0501       }
0502     }
0503     if ( !gaudiYAxis.labels().empty() ) {
0504       auto& axis = *histo.GetYaxis();
0505       for ( unsigned int i = 0; i < gaudiYAxis.labels().size(); i++ ) {
0506         axis.SetBinLabel( i + 1, gaudiYAxis.labels()[i].c_str() );
0507       }
0508     }
0509     if ( !gaudiZAxis.labels().empty() ) {
0510       auto& axis = *histo.GetZaxis();
0511       for ( unsigned int i = 0; i < gaudiZAxis.labels().size(); i++ ) {
0512         axis.SetBinLabel( i + 1, gaudiZAxis.labels()[i].c_str() );
0513       }
0514     }
0515     for ( unsigned int i = 0; i < gaudiHisto.totNBins(); i++ ) {
0516       auto const& [tmp, sumw2] = gaudiHisto.binValue( i );
0517       auto const& [nent, sumw] = tmp;
0518       histo.SetBinContent( i, sumw );
0519       histo.setBinNEntries( i, nent );
0520       histo.setBinW2( i, sumw2 );
0521     }
0522     unsigned long totNEntries{ 0 };
0523     for ( unsigned int i = 0; i < gaudiHisto.totNBins(); i++ ) { totNEntries += gaudiHisto.nEntries( i ); }
0524     histo.SetEntries( totNEntries );
0525     return histo;
0526   }
0527 
0528   template <unsigned int N, Accumulators::atomicity Atomicity = Accumulators::atomicity::full,
0529             typename Arithmetic = double>
0530   void saveProfileHisto( TFile& file, std::string dir, std::string name, Monitoring::Hub::Entity const& ent ) {
0531     // fix name and dir if needed
0532     details::fixNameAndDir( name, dir );
0533     // Change to the proper directory in the ROOT file
0534     auto previousDir = details::changeDir( file, dir );
0535     // write to file
0536     if constexpr ( N == 1 ) {
0537       profileHisto1DToRoot<Atomicity, Arithmetic>( name, ent ).Write();
0538     } else if constexpr ( N == 2 ) {
0539       profileHisto2DToRoot<Atomicity, Arithmetic>( name, ent ).Write();
0540     } else if constexpr ( N == 3 ) {
0541       profileHisto3DToRoot<Atomicity, Arithmetic>( name, ent ).Write();
0542     }
0543     // switch back to the previous directory
0544     previousDir->cd();
0545   }
0546 
0547 } // namespace Gaudi::Histograming::Sink
0548 
0549 namespace nlohmann {
0550   /// automatic translation of TAxis to json
0551   inline void to_json( nlohmann::json& j, TAxis const& axis ) {
0552     j = nlohmann::json{ { "nBins", axis.GetNbins() },
0553                         { "minValue", axis.GetXmin() },
0554                         { "maxValue", axis.GetXmax() },
0555                         { "title", axis.GetTitle() } };
0556   }
0557   /// automatic translation of Root histograms to json
0558   inline void to_json( nlohmann::json& j, TH1D const& h ) {
0559     j = Gaudi::Histograming::Sink::details::rootHistogramToJson( h );
0560   }
0561   inline void to_json( nlohmann::json& j, TH2D const& h ) {
0562     j = Gaudi::Histograming::Sink::details::rootHistogramToJson( h );
0563   }
0564   inline void to_json( nlohmann::json& j, TH3D const& h ) {
0565     j = Gaudi::Histograming::Sink::details::rootHistogramToJson( h );
0566   }
0567   inline void to_json( nlohmann::json& j, TProfile const& h ) {
0568     j = Gaudi::Histograming::Sink::details::rootHistogramToJson( h );
0569   }
0570   inline void to_json( nlohmann::json& j, TProfile2D const& h ) {
0571     j = Gaudi::Histograming::Sink::details::rootHistogramToJson( h );
0572   }
0573 
0574 } // namespace nlohmann