diff --git a/Control/AthenaMonitoring/AthenaMonitoring/HistogramDef.h b/Control/AthenaMonitoring/AthenaMonitoring/HistogramDef.h index 3e9ed68b7672e8c1bc560eb89ba1548b63c98a36..901749673fff7bd2ed2f5c9f17ed2d3b48a2aa2b 100644 --- a/Control/AthenaMonitoring/AthenaMonitoring/HistogramDef.h +++ b/Control/AthenaMonitoring/AthenaMonitoring/HistogramDef.h @@ -30,10 +30,12 @@ namespace Monitored { int xbins{0}; //!< number of bins in X float xmin{0}; //!< left float xmax{0}; //!< right + std::vector<double> xArray; //!< array of x bins int ybins{0}; //!< number of bins in Y float ymin{0}; //!< bottom float ymax{0}; //!< top + std::vector<double> yArray; //!< array of y bins float zmin{0}; //!< in float zmax{0}; //!< out diff --git a/Control/AthenaMonitoring/python/ExampleMonitorAlgorithm.py b/Control/AthenaMonitoring/python/ExampleMonitorAlgorithm.py index 4b58715f781c7f3e65afc017e1afebde2c00f85c..702beffb33ef8a04d371fab5825b42a11575662e 100644 --- a/Control/AthenaMonitoring/python/ExampleMonitorAlgorithm.py +++ b/Control/AthenaMonitoring/python/ExampleMonitorAlgorithm.py @@ -86,6 +86,10 @@ def ExampleMonitoringConfig(inputFlags): path='ToFindThem',xbins=1000,xmin=-0.5,xmax=999.5,weight='testweight') myGroup.defineHistogram('random', title='LB;x;Events', path='ToBringThemAll',xbins=30,xmin=0,xmax=1,opt='kLBNHistoryDepth=10') + myGroup.defineHistogram('random', title='title;x;y',path='ToBringThemAll', + xbins=[0,.1,.2,.4,.8,1.6]) + myGroup.defineHistogram('random,pT', type='TH2F', title='title;x;y',path='ToBringThemAll', + xbins=[0,.1,.2,.4,.8,1.6],ybins=[0,10,30,40,60,70,90]) myGroup.defineHistogram('pT_passed,pT',type='TEfficiency',title='Test TEfficiency;x;Eff', path='AndInTheDarkness',xbins=100,xmin=0.0,xmax=50.0) diff --git a/Control/AthenaMonitoring/python/GenericMonitoringTool.py b/Control/AthenaMonitoring/python/GenericMonitoringTool.py index 5c8a786f59002f93c83d5e42fb56cae498d1e641..f4cc01cf95d05256d46e384e636eae2a35dd19f1 100644 --- a/Control/AthenaMonitoring/python/GenericMonitoringTool.py +++ b/Control/AthenaMonitoring/python/GenericMonitoringTool.py @@ -27,14 +27,18 @@ class GenericMonitoringTool(_GenericMonitoringTool): # @param title Histogram title and optional axis title (same syntax as in TH constructor) # @param opt Histrogram options (see GenericMonitoringTool) # @param labels List of bin labels (for a 2D histogram, sequential list of x- and y-axis labels) + def defineHistogram(varname, type='TH1F', path=None, title=None,weight='', xbins=100, xmin=0, xmax=1, - ybins=None, ymin=None, ymax=None, zmin=None, zmax=None, opt='', labels=None): + ybins=None, ymin=None, ymax=None, + zmin=None, zmax=None, opt='', labels=None): # Assert argument types assert path is not None, "path is required" assert labels is None or isinstance(labels, list), "labels must be of type list" + # assert labels is None or !isinstance(labels, list), \ + # "Mixed use of variable bin widths and bin labels." if title is None: title = varname @@ -43,9 +47,19 @@ def defineHistogram(varname, type='TH1F', path=None, log.warning('Histogram %s of type %s is not supported for online running and will not be added', varname, type) return "" - coded = "%s, %s, %s, %s, %s, %d, %f, %f" % (path, type, weight, varname, title, xbins, xmin, xmax) + coded = "%s, %s, %s, %s, %s, " % (path, type, weight, varname, title) + + if not isinstance(xbins,list): + coded += '%d, %f, %f' % (xbins, xmin, xmax) + else: + # List of :-separated bins, plus two empty spaces for xmin and xmax + coded += ':'.join([str(xbin) for xbin in xbins]) + if ybins is not None: - coded += ", %d, %f, %f" % (ybins, ymin, ymax) + if not isinstance(ybins,list): + coded += ", %d, %f, %f" % (ybins, ymin, ymax) + else: + coded += ', ' + ':'.join([str(ybin) for ybin in ybins]) if zmin is not None: coded += ", %f, %f" % (zmin, zmax) diff --git a/Control/AthenaMonitoring/src/ExampleMonitorAlgorithm.cxx b/Control/AthenaMonitoring/src/ExampleMonitorAlgorithm.cxx index 619983eeb3f33332056c08c952ae8a3ae9882925..2ca2d01be4758059790513a349f4dd4731217963 100644 --- a/Control/AthenaMonitoring/src/ExampleMonitorAlgorithm.cxx +++ b/Control/AthenaMonitoring/src/ExampleMonitorAlgorithm.cxx @@ -6,7 +6,7 @@ ExampleMonitorAlgorithm::ExampleMonitorAlgorithm( const std::string& name, ISvcLocator* pSvcLocator ) :AthMonitorAlgorithm(name,pSvcLocator) -,m_doRandom(false) +,m_doRandom(true) {} diff --git a/Control/AthenaMonitoring/src/HistogramDef.cxx b/Control/AthenaMonitoring/src/HistogramDef.cxx index f377be46e5cbeb26238eac12696490c1233b65aa..8f454735d8f4cbf10e28eb13b65d5e48db5a9e49 100644 --- a/Control/AthenaMonitoring/src/HistogramDef.cxx +++ b/Control/AthenaMonitoring/src/HistogramDef.cxx @@ -1,7 +1,6 @@ /* Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration */ - #include <boost/algorithm/string.hpp> #include <boost/lexical_cast.hpp> #include <boost/tokenizer.hpp> @@ -108,32 +107,44 @@ void HistogramDef::resolveAlias(HistogramDef &histogramDefinition) { } void HistogramDef::resolveAxies(HistogramDef &histogramDefinition, std::vector<std::string> &properties, std::vector<std::string>::iterator &propertiesIterator) { - if (distance(propertiesIterator, properties.end()) < 3) { + if (distance(propertiesIterator, properties.end()) < 1) { throw TokenException("NOT enough parameters for defining 1-D histogram"); } + std::string xbins = nextProperty(propertiesIterator); + // Parse information regarding x-bins + if ( xbins.find(":")!=std::string::npos ) { // Bins are specified by array + std::vector<std::string> splitResult = splitWithSeparator(xbins, ":"); + for (auto binString : splitResult ) // Convert to vector of floats + histogramDefinition.xArray.push_back(parseToken<double>(binString,"double expected for bin edge")); + histogramDefinition.xbins = histogramDefinition.xArray.size()-1; + } else { // Bins are specified by nx,xmin,xmax (equal spacing) + if (distance(propertiesIterator, properties.end()) < 2) // Must have xmin and xmax available + throw TokenException("NOT enough parameters for defining 1-D histogram by N,xmin,xmax"); + histogramDefinition.xbins = parseToken<int>(xbins, "int expected for xbins"); + histogramDefinition.xmin = parseToken<double>(nextProperty(propertiesIterator), "double expected for xmin"); + histogramDefinition.xmax = parseToken<double>(nextProperty(propertiesIterator), "double expected for xmax"); + } - histogramDefinition.xbins = parseToken<int>(nextProperty(propertiesIterator), "int expected for xbins"); - histogramDefinition.xmin = parseToken<double>(nextProperty(propertiesIterator), "double expected for xmin"); - histogramDefinition.xmax = parseToken<double>(nextProperty(propertiesIterator), "double expected for xmax"); - - if (histogramDefinition.type.find("TH2") == 0) { - if (distance(propertiesIterator, properties.end()) < 3) { - throw TokenException("y-axis definition expected for TH2"); + // Parse information regarding y-bins + if (histogramDefinition.type.find("TH2")==0 || histogramDefinition.type.find("TProfile2D")==0) { + if (distance(propertiesIterator, properties.end()) < 1) { + throw TokenException("y-axis definition expected for TH2 or TProfile2D"); } - - histogramDefinition.ybins = parseToken<int>(nextProperty(propertiesIterator), "int expected for ybins"); - histogramDefinition.ymin = parseToken<double>(nextProperty(propertiesIterator), "double expected for ymin"); - histogramDefinition.ymax = parseToken<double>(nextProperty(propertiesIterator), "double expected for ymax"); - } else if (histogramDefinition.type == "TProfile2D") { - if (distance(propertiesIterator, properties.end()) < 3) { - throw TokenException("y-axis definition expected for TProfile2D"); + std::string ybins = nextProperty(propertiesIterator); + if ( ybins.find(":")!=std::string::npos ) { // Bins are specified by array + std::vector<std::string> splitResult = splitWithSeparator(ybins, ":"); + for (auto binString : splitResult) // Convert to vector of floats + histogramDefinition.yArray.push_back(parseToken<double>(binString,"double expected for bin edge")); + histogramDefinition.ybins = histogramDefinition.yArray.size()-1; + } else { // Bins are specified by ny,ymin,ymax (equal spacing) + if (distance(propertiesIterator, properties.end()) < 2) // Must have ymin and ymax available + throw TokenException("NOT enough parameters for defining 1-D histogram by N,ymin,ymax"); + histogramDefinition.ybins = parseToken<int>(ybins, "int expected for ybins"); + histogramDefinition.ymin = parseToken<double>(nextProperty(propertiesIterator), "double expected for ymin"); + histogramDefinition.ymax = parseToken<double>(nextProperty(propertiesIterator), "double expected for ymax"); } - - histogramDefinition.ybins = parseToken<int>(nextProperty(propertiesIterator), "int expected for ybins"); - histogramDefinition.ymin = parseToken<double>(nextProperty(propertiesIterator), "double expected for ymin"); - histogramDefinition.ymax = parseToken<double>(nextProperty(propertiesIterator), "double expected for ymax"); - - if (distance(propertiesIterator, properties.end()) >= 2) { + // Parse information regarding z range + if (histogramDefinition.type=="TProfile2D" && distance(propertiesIterator, properties.end())>1) { histogramDefinition.zmin = parseToken<double>(nextProperty(propertiesIterator), "double expected for zmin of TProfile2D"); histogramDefinition.zmax = parseToken<double>(nextProperty(propertiesIterator), "double expected for zmax of TProfile2D"); histogramDefinition.zcut = true; diff --git a/Control/AthenaMonitoring/src/HistogramFiller/HistogramFactory.cxx b/Control/AthenaMonitoring/src/HistogramFiller/HistogramFactory.cxx index 35f5d0ffafc4b5fd89c20fc3b04031c849047b55..defb0eacb1d8271af79c0fe87f5c972ab1963ce0 100644 --- a/Control/AthenaMonitoring/src/HistogramFiller/HistogramFactory.cxx +++ b/Control/AthenaMonitoring/src/HistogramFiller/HistogramFactory.cxx @@ -59,26 +59,57 @@ TNamed* HistogramFactory::create(const HistogramDef& def) { template<class H> TH1* HistogramFactory::create1D(const HistogramDef& def) { - return create<H,TH1>(def, def.xbins, def.xmin, def.xmax); + if ( def.xArray.size()!=0 ) { + return create<H,TH1>(def, def.xbins, &(def.xArray)[0]); + } else { + return create<H,TH1>(def, def.xbins, def.xmin, def.xmax); + } } template<class H> TH1* HistogramFactory::create1DProfile(const HistogramDef& def) { - return create<H,TH1>(def, def.xbins, def.xmin, def.xmax, - def.ymin, def.ymax); + if (def.xArray.size()!=0) { + return create<H,TH1>(def, def.xbins, &(def.xArray)[0], + def.ymin, def.ymax); + } else { + return create<H,TH1>(def, def.xbins, def.xmin, def.xmax, + def.ymin, def.ymax); + } } template<class H> TH2* HistogramFactory::create2D(const HistogramDef& def) { - return create<H,TH2>(def, def.xbins, def.xmin, def.xmax, - def.ybins, def.ymin, def.ymax); + if (def.xArray.size()!=0 && def.yArray.size()!=0) { + return create<H,TH2>(def, def.xbins, &(def.xArray)[0], + def.ybins, &(def.yArray)[0]); + } else if (def.yArray.size()!=0) { + return create<H,TH2>(def, def.xbins, def.xmin, def.xmax, + def.ybins, &(def.yArray)[0]); + } else if (def.xArray.size()!=0) { + return create<H,TH2>(def, def.xbins, &(def.xArray)[0], + def.ybins, def.ymin, def.ymax); + } else { + return create<H,TH2>(def, def.xbins, def.xmin, def.xmax, + def.ybins, def.ymin, def.ymax); + } } template<class H> TH2* HistogramFactory::create2DProfile(const HistogramDef& def) { - return create<H,TH2>(def, def.xbins, def.xmin, def.xmax, - def.ybins, def.ymin, def.ymax, - def.zmin, def.zmax); + if (def.xArray.size()!=0 && def.yArray.size()!=0) { + return create<H,TH2>(def, def.xbins, &(def.xArray)[0], + def.ybins, &(def.yArray)[0]); + } else if (def.yArray.size()!=0) { + return create<H,TH2>(def, def.xbins, def.xmin, def.xmax, + def.ybins, &(def.yArray)[0]); + } else if (def.xArray.size()!=0) { + return create<H,TH2>(def, def.xbins, &(def.xArray)[0], + def.ybins, def.ymin, def.ymax); + } else { + return create<H,TH2>(def, def.xbins, def.xmin, def.xmax, + def.ybins, def.ymin, def.ymax, + def.zmin, def.zmax); + } } TEfficiency* HistogramFactory::createEfficiency(const HistogramDef& def) { diff --git a/Control/AthenaMonitoring/test/GenericMonParsing_test.cxx b/Control/AthenaMonitoring/test/GenericMonParsing_test.cxx index b18b39cceeabb7800c8d1b55ceb885cd60f4f732..c7e2429f104feea6fdbc26a24c253a090271d374 100644 --- a/Control/AthenaMonitoring/test/GenericMonParsing_test.cxx +++ b/Control/AthenaMonitoring/test/GenericMonParsing_test.cxx @@ -89,15 +89,32 @@ bool parsingLabeledWorks() { } bool parsingWeightedWorks() { - auto def = HistogramDef::parse("EXPERT, TH1F, Weight, Cut, Cut counter, 5, 0, 5"); - VALUE ( def.ok ) EXPECTED ( true ) ; - VALUE ( def.path ) EXPECTED ( "EXPERT" ); - VALUE ( def.type ) EXPECTED ( "TH1F" ); - VALUE ( def.name.size() ) EXPECTED ( 1 ); - VALUE ( std::string(def.name[0]) ) EXPECTED ( "Cut"); - VALUE ( def.labels.size() )EXPECTED ( 5 ); - VALUE ( def.weight ) EXPECTED ("Weight"); + auto def = HistogramDef::parse("EXPERT, TH1F, Weight, var, title, 5, 0, 5"); + VALUE ( def.ok ) EXPECTED ( true ); + VALUE ( def.path ) EXPECTED ( "EXPERT" ); + VALUE ( def.type ) EXPECTED ( "TH1F" ); + VALUE ( def.weight ) EXPECTED ( "Weight" ); + VALUE ( def.name.size() ) EXPECTED ( 1 ); + VALUE ( std::string(def.name[0]) ) EXPECTED ( "var" ); + + return true; +} +bool parsing1DArrayWorks() { + auto def = HistogramDef::parse("EXPERT, TH1F, , var, title, 0:1:2:4:8"); + VALUE ( def.ok ) EXPECTED ( true ); + VALUE ( def.xbins ) EXPECTED ( 4 ); + VALUE ( std::equal(def.xArray.begin(),def.xArray.end(),std::vector<double>({0,1,2,4,8}).begin()) ) EXPECTED ( true ); + return true; +} + +bool parsing2DArrayWorks() { + auto def = HistogramDef::parse("EXPERT, TH2F, , var1,var2, title, 0:1:2:4:8, 0:4:6:7"); + VALUE ( def.ok ) EXPECTED ( true ); + VALUE ( def.xbins ) EXPECTED ( 4 ); + VALUE ( std::equal(def.xArray.begin(),def.xArray.end(),std::vector<double>({0,1,2,4,8}).begin()) ) EXPECTED ( true ); + VALUE ( def.ybins ) EXPECTED ( 3 ); + VALUE ( std::equal(def.yArray.begin(),def.yArray.end(),std::vector<double>({0,4,6,7}).begin()) ) EXPECTED ( true ); return true; } @@ -107,14 +124,17 @@ bool badDefGeneratesExecption() { return true; } - int main() { assert( parsing1DWorks() ); assert( parsing2DWorks() ); assert( parsing2DWorks() ); assert( parsing3DWorks() ); assert( parsingLabeledWorks() ); + assert( parsingWeightedWorks() ); + assert( parsing1DArrayWorks() ); + assert( parsing2DArrayWorks() ); assert( badDefGeneratesExecption() ); + std::cout << "all ok" << std::endl; return 0; } diff --git a/Control/AthenaMonitoring/test/test_defineHistogram.py b/Control/AthenaMonitoring/test/test_defineHistogram.py index 35bf12280062fce3407852be75b5de1bc1b6d8eb..21b794f17290a8117720a12edbd6f44672335039 100644 --- a/Control/AthenaMonitoring/test/test_defineHistogram.py +++ b/Control/AthenaMonitoring/test/test_defineHistogram.py @@ -43,5 +43,13 @@ class Test( unittest.TestCase ): s = defineHistogram('var', 'TEfficiency', 'EXPERT', 'title', '', 10, 0.0, 10.0) self.assertEqual(s, 'EXPERT, TEfficiency, , var, title, 10, 0.000000, 10.000000') + def test_1D_array( self ): + s = defineHistogram('var', 'TH1F', 'EXPERT', 'title', '', [0,1,2,4,8]) + self.assertEqual(s, 'EXPERT, TH1F, , var, title, 0:1:2:4:8') + + def test_2D_array( self ): + s = defineHistogram('var1,var2', 'TH2F', 'EXPERT', 'title', '', [0,1,2], ybins=[1,2,3,7]) + self.assertEqual(s, 'EXPERT, TH2F, , var1,var2, title, 0:1:2, 1:2:3:7') + if __name__ == '__main__': unittest.main()