The Gaudi Framework  v36r9p1 (5c15b2bb)
RootHistogramSinkBase.h
Go to the documentation of this file.
1 /***********************************************************************************\
2 * (c) Copyright 1998-2022 CERN for the benefit of the LHCb and ATLAS collaborations *
3 * *
4 * This software is distributed under the terms of the Apache version 2 licence, *
5 * copied verbatim in the file "LICENSE". *
6 * *
7 * In applying this licence, CERN does not waive the privileges and immunities *
8 * granted to it by virtue of its status as an Intergovernmental Organization *
9 * or submit itself to any jurisdiction. *
10 \***********************************************************************************/
11 
12 #include "GaudiKernel/Service.h"
13 
14 #include <Gaudi/MonitoringHub.h>
15 
16 #include <functional>
17 
18 #include <TDirectory.h>
19 #include <TFile.h>
20 
21 #include <range/v3/numeric/accumulate.hpp>
22 #include <range/v3/range/conversion.hpp>
23 #include <range/v3/view/split_when.hpp>
24 #include <range/v3/view/transform.hpp>
25 // upstream has renamed namespace ranges::view -> ranges::views
26 #if RANGE_V3_VERSION < 900
27 namespace ranges::views {
28  using namespace ranges::view;
29 }
30 #endif
31 
32 #include <nlohmann/json.hpp>
33 
34 #include <algorithm>
35 #include <deque>
36 #include <functional>
37 #include <map>
38 #include <string>
39 #include <vector>
40 
41 namespace {
42  bool isHistogram( Gaudi::Monitoring::Hub::Entity const& ent ) {
43  return std::string_view( ent.type ).substr( 0, 10 ) == "histogram:";
44  }
45 } // namespace
46 
48 
49  namespace details {
50 
51  struct Axis {
52  unsigned int nBins;
53  double minValue;
54  double maxValue;
56  };
57 
59  return { jAxis.at( "nBins" ).get<unsigned int>(), jAxis.at( "minValue" ).get<double>(),
60  jAxis.at( "maxValue" ).get<double>(),
61  ";" + jAxis.at( "title" ).get<std::string>() }; // ";" to prepare concatenations of titles
62  }
63 
67  template <typename Traits, std::size_t... index>
69  std::index_sequence<index...> ) {
70  // extract data from json
71  auto jsonAxis = j.at( "axis" );
72  auto axis = std::array{ toAxis( jsonAxis[index] )... };
73  auto weights = j.at( "bins" ).get<std::vector<typename Traits::WeightType>>();
74  auto title = j.at( "title" ).get<std::string>();
75  auto nentries = j.at( "nEntries" ).get<unsigned int>();
76  // weird way ROOT has to give titles to axis
77  title += ( axis[index].title + ... );
78  // compute total number of bins, multiplying bins per axis
79  auto totNBins = ( ( axis[index].nBins + 2 ) * ... );
80  assert( weights.size() == totNBins );
81 
82  if ( name[0] == '/' ) {
83  dir = "";
84  name = name.substr( 1 );
85  }
86 
87  // take into account the case where name contains '/'s (e.g. "Group/Name") by
88  // moving the prefix into dir
89  if ( auto pos = name.rfind( '/' ); pos != std::string::npos ) {
90  dir += '/' + name.substr( 0, pos );
91  name = name.substr( pos + 1 );
92  }
93 
94  // remember the current directory
95  auto previousDir = gDirectory;
96 
97  // find or create the directory for the histogram
98  {
99  using namespace ranges;
100  auto is_delimiter = []( auto c ) { return c == '/' || c == '.'; };
101  auto transform_to_string = views::transform( []( auto&& rng ) { return rng | to<std::string>; } );
102 
103  auto currentDir = accumulate( dir | views::split_when( is_delimiter ) | transform_to_string,
104  file.GetDirectory( "" ), []( auto current, auto&& dir_level ) {
105  if ( current ) {
106  // try to get next level
107  auto nextDir = current->GetDirectory( dir_level.c_str() );
108  // if it does not exist, create it
109  if ( !nextDir ) nextDir = current->mkdir( dir_level.c_str() );
110  // move to next level
111  current = nextDir;
112  }
113  return current;
114  } );
115 
116  if ( !currentDir )
117  throw GaudiException( "Could not create directory " + dir, "Histogram::Sink::Root", StatusCode::FAILURE );
118  // switch to the directory
119  currentDir->cd();
120  }
121 
122  // Create Root histogram calling constructors with the args tuple
123  auto histo = Traits::create( name, title, axis[index]... );
124 
125  // fill Histo
126  for ( unsigned int i = 0; i < totNBins; i++ ) Traits::fill( histo, i, weights[i] );
127  // fill histo metadata, e.g. bins and number of entries
128  Traits::fillMetaData( histo, jsonAxis, nentries );
129  // write to file
130  histo.Write();
131 
132  // switch back to the previous directory
133  previousDir->cd();
134  }
135 
151  template <typename Traits>
152  void saveRootHisto( TFile& file, std::string dir, std::string name, nlohmann::json& j ) {
153  details::saveRootHistoInternal<Traits>( file, std::move( dir ), std::move( name ), j,
154  std::make_index_sequence<Traits::Dimension>() );
155  }
156  } // namespace details
157 
158  /*
159  * a Base class for Root related Sinks dealing with Histograms.
160  *
161  * provides the common method plus a generic way of registering handler for different types
162  */
163  class Base : public Service, public Gaudi::Monitoring::Hub::Sink {
164  public:
168 
170  std::function<bool( Monitoring::Hub::Entity const& )> const isRelevant = isHistogram )
171  : Service( name, svcloc ), m_isRelevant( std::move( isRelevant ) ) {}
172 
173  StatusCode initialize() override {
174  return Service::initialize().andThen( [&] { serviceLocator()->monitoringHub().addSink( this ); } );
175  }
176 
177  StatusCode stop() override {
178  auto ok = Service::stop();
179  if ( !ok ) return ok;
181  std::sort( begin( m_monitoringEntities ), end( m_monitoringEntities ), []( const auto& a, const auto& b ) {
182  return std::tie( a.name, a.component ) > std::tie( b.name, b.component );
183  } );
184  TFile histoFile( m_fileName.value().c_str(), "RECREATE" );
185  std::for_each( begin( m_monitoringEntities ), end( m_monitoringEntities ), [&histoFile, this]( auto& ent ) {
186  auto j = ent.toJSON();
187  auto dim = j.at( "dimension" ).template get<unsigned int>();
188  auto type = j.at( "type" ).template get<std::string>();
189  // cut type after last ':' if there is one. The rest is precision parameter that we do not need here
190  // as ROOT anyway treats everything as doubles in histograms
191  type = type.substr( 0, type.find_last_of( ':' ) );
192  auto saver = m_registry.find( { type, dim } );
193  if ( saver != m_registry.end() ) ( saver->second )( histoFile, ent.component, ent.name, j );
194  } );
195  return ok;
196  }
197 
199  if ( m_isRelevant( ent ) ) { m_monitoringEntities.emplace_back( std::move( ent ) ); }
200  }
201 
202  void removeEntity( Monitoring::Hub::Entity const& ent ) override {
203  auto it = std::find( begin( m_monitoringEntities ), end( m_monitoringEntities ), ent );
204  if ( it != m_monitoringEntities.end() ) { m_monitoringEntities.erase( it ); }
205  }
206 
208  m_registry.emplace( std::piecewise_construct, std::make_tuple( id ), std::make_tuple( func ) );
209  }
210 
211  private:
213  HistoRegistry m_registry{};
214 
217 
220 
221  Gaudi::Property<std::string> m_fileName{ this, "FileName", "testHisto.root",
222  "Name of file where to save histograms" };
223  };
224 
225 } // namespace Gaudi::Histograming::Sink
Gaudi::Histograming::Sink::details::saveRootHistoInternal
void saveRootHistoInternal(TFile &file, std::string dir, std::string name, nlohmann::json const &j, std::index_sequence< index... >)
generic function to handle histograms - internal implemenatation
Definition: RootHistogramSinkBase.h:68
Gaudi::Monitoring::Hub::Sink
Interface reporting services must implement.
Definition: MonitoringHub.h:156
std::make_tuple
T make_tuple(T... args)
std::for_each
T for_each(T... args)
Service::initialize
StatusCode initialize() override
Definition: Service.cpp:118
std::string
STL class.
StatusCode::andThen
StatusCode andThen(F &&f, ARGS &&... args) const
Chain code blocks making the execution conditional a success result.
Definition: StatusCode.h:163
std::move
T move(T... args)
MonitoringHub.h
HistoEx.histo
histo
Definition: HistoEx.py:105
std::pair
Gaudi::Histograming::Sink::details::saveRootHisto
void saveRootHisto(TFile &file, std::string dir, std::string name, nlohmann::json &j)
generic method to save histograms to files
Definition: RootHistogramSinkBase.h:152
Gaudi::Histograming::Sink::Base::m_monitoringEntities
std::deque< Gaudi::Monitoring::Hub::Entity > m_monitoringEntities
list of entities handled by this Sink
Definition: RootHistogramSinkBase.h:219
Gaudi::Histograming::Sink::Base::stop
StatusCode stop() override
Definition: RootHistogramSinkBase.h:177
std::vector
STL class.
std::find
T find(T... args)
Gaudi::Histograming::Sink::details::Axis::minValue
double minValue
Definition: RootHistogramSinkBase.h:53
ISvcLocator
Definition: ISvcLocator.h:46
GaudiException
Definition: GaudiException.h:31
ranges
Definition: FunctionalDetails.h:34
Gaudi::Histograming::Sink::Base::removeEntity
void removeEntity(Monitoring::Hub::Entity const &ent) override
Definition: RootHistogramSinkBase.h:202
Gaudi::Histograming::Sink::Base::initialize
StatusCode initialize() override
Definition: RootHistogramSinkBase.h:173
Gaudi::Histograming::Sink::Base::Base
Base(std::string name, ISvcLocator *svcloc, std::function< bool(Monitoring::Hub::Entity const &)> const isRelevant=isHistogram)
Definition: RootHistogramSinkBase.h:169
gaudirun.c
c
Definition: gaudirun.py:525
std::function
ranges::views
Definition: FunctionalDetails.h:34
Gaudi::Histograming::Sink::details::Axis::title
std::string title
Definition: RootHistogramSinkBase.h:55
jsonFromLHCbLog.json
dictionary json
Definition: jsonFromLHCbLog.py:87
std::sort
T sort(T... args)
Service
Definition: Service.h:46
std::tie
T tie(T... args)
TimingHistograms.name
name
Definition: TimingHistograms.py:25
StatusCode
Definition: StatusCode.h:65
details
Definition: AnyDataWrapper.h:18
std::vector::at
T at(T... args)
GaudiPluginService.cpluginsvc.func
func
Definition: cpluginsvc.py:236
Gaudi::Histograming::Sink
Definition: RootHistogramSinkBase.h:47
CLHEP::begin
double * begin(CLHEP::HepVector &v)
Definition: TupleAlg.cpp:45
Gaudi::Histograming::Sink::Base::registerHandler
void registerHandler(HistoIdentification const &id, HistoHandler const &func)
Definition: RootHistogramSinkBase.h:207
std::array
STL class.
Gaudi::Histograming::Sink::Base::registerEntity
void registerEntity(Monitoring::Hub::Entity ent) override
Definition: RootHistogramSinkBase.h:198
std::deque< Gaudi::Monitoring::Hub::Entity >
std::map< HistoIdentification, HistoHandler >
IOTest.end
def end
Definition: IOTest.py:128
Gaudi::Histograming::Sink::details::Axis
Definition: RootHistogramSinkBase.h:51
Service.h
gaudirun.type
type
Definition: gaudirun.py:160
Gaudi::Histograming::Sink::details::Axis::nBins
unsigned int nBins
Definition: RootHistogramSinkBase.h:52
Gaudi::Histograming::Sink::Base::m_isRelevant
std::function< bool(Monitoring::Hub::Entity const &)> const m_isRelevant
function saying whether a given entity is relevant for this Sink
Definition: RootHistogramSinkBase.h:216
Service::stop
StatusCode stop() override
Definition: Service.cpp:181
std
STL namespace.
Gaudi::Histograming::Sink::Base
Definition: RootHistogramSinkBase.h:163
std::size_t
Gaudi::Utils::Histos::fill
GAUDI_API void fill(AIDA::IHistogram1D *histo, const double value, const double weight=1.0)
simple function to fill AIDA::IHistogram1D objects
Definition: Fill.cpp:45
Gaudi::Histograming::Sink::details::toAxis
Axis toAxis(nlohmann::json &jAxis)
Definition: RootHistogramSinkBase.h:58
StatusCode::FAILURE
constexpr static const auto FAILURE
Definition: StatusCode.h:101
Gaudi::Monitoring::Hub::Entity
Wrapper class for arbitrary monitoring objects.
Definition: MonitoringHub.h:89
Gaudi::Accumulators::accumulate
void accumulate(Counter &counter, const Container &container, Fun f=Identity{})
A helper function for accumulating data from a container into a counter This is internally using buff...
Definition: Accumulators.h:1195
Gaudi::Histograming::Sink::details::Axis::maxValue
double maxValue
Definition: RootHistogramSinkBase.h:54
Gaudi::Property< std::string >
Gaudi::Monitoring::Hub::Entity::type
std::string type
type of the entity, see comment above concerning its format and usage
Definition: MonitoringHub.h:109