CMS 3D CMS Logo

GEMDQMEfficiencyClientBase.cc
Go to the documentation of this file.
2 
5 
6 #include "TEfficiency.h"
7 #include "TPRegexp.h"
8 #include <regex>
9 
11  : kConfidenceLevel_(ps.getUntrackedParameter<double>("confidenceLevel")),
12  kLogCategory_(ps.getUntrackedParameter<std::string>("logCategory")) {}
13 
14 // Returns a tuple of
15 // - a boolean indicating whether the parsing is successful or not
16 // - name of a variable used in the efficiency monitoring
17 // - GEM subdetector name like GE11-P-L1
18 // - a boolean indicating whether the name is a numerator name.
19 std::tuple<bool, std::string, std::string, bool> GEMDQMEfficiencyClientBase::parseEfficiencySourceName(
20  std::string name) {
21  // NOTE This expression must be consistent with TODO
22  // TODO use regex
23  const bool success = TPRegexp("\\w+(?:_match)?_GE\\d1-(P|M)[0-9\\-]*").MatchB(name);
24  if (not success) {
25  return std::make_tuple(success, "", "", false);
26  }
27 
28  const std::string numerator_pattern = "_match";
29  const auto numerator_pattern_start = name.find(numerator_pattern);
30  const bool is_numerator = numerator_pattern_start != std::string::npos;
31  if (is_numerator) {
32  // keep a delimiter between a variable name and a GEM name
33  // e.g. 'muon_pt_matched_GE11-L1' --> 'muon_pt_GE11-L1'
34  name.erase(numerator_pattern_start, numerator_pattern.length());
35  }
36  // find the position of the delimiter.
37  // Because variable name can has "_", find the last one.
38  // NOTE The GEM name must not contains "_"
39  const unsigned long last_pos = name.find_last_of('_');
40 
41  // "muon_pt"
42  const std::string var_name = name.substr(0, last_pos);
43 
44  // "GE11-L1"
45  const std::string gem_name = name.substr(last_pos + 1);
46  return std::make_tuple(success, var_name, gem_name, is_numerator);
47 }
48 
50  // GE11-P
51  // GE11-P-L1
52  // GE11-P-E1
53 
54  int region = 0;
55  int station = 0;
56  int layer = 0;
57  int chamber = 0;
58  int ieta = 0;
59 
60  std::vector<std::string> tokens;
61 
62  // static const?
63  const std::regex re_station{"GE\\d1"};
64  const std::regex re_region{"(P|M)"};
65  const std::regex re_layer{"L\\d"};
66  const std::regex re_chamber_layer{"\\d+L\\d"};
67  const std::regex re_ieta{"E\\d+"};
68 
69  std::string::size_type last_pos = gem_label.find_first_not_of(delimiter, 0);
70  std::string::size_type pos = gem_label.find_first_of(delimiter, last_pos);
71  while ((pos != std::string::npos) or (last_pos != std::string::npos)) {
72  const std::string token = gem_label.substr(last_pos, pos - last_pos);
73 
74  if (std::regex_match(token, re_region)) {
75  region = (token == "P") ? 1 : -1;
76 
77  } else if (std::regex_match(token, re_station)) {
78  station = std::stoi(token.substr(2, 1));
79 
80  } else if (std::regex_match(token, re_layer)) {
81  layer = std::stoi(token.substr(1));
82 
83  } else if (std::regex_match(token, re_chamber_layer)) {
84  const unsigned long layer_prefix_pos = token.find('L');
85  chamber = std::stoi(token.substr(0, layer_prefix_pos));
86  layer = std::stoi(token.substr(layer_prefix_pos + 1));
87 
88  } else if (std::regex_match(token, re_ieta)) {
89  ieta = std::stoi(token.substr(1));
90 
91  } else {
92  edm::LogError(kLogCategory_) << "unknown pattern: " << gem_label << " --> " << token;
93  }
94  }
95 
96  const GEMDetId id{region, 1, station, layer, chamber, ieta};
97  return id;
98 }
99 
100 std::map<std::string, GEMDQMEfficiencyClientBase::MEPair> GEMDQMEfficiencyClientBase::makeEfficiencySourcePair(
101  DQMStore::IBooker& ibooker, DQMStore::IGetter& igetter, const std::string& folder, const std::string prefix) {
102  ibooker.setCurrentFolder(folder);
103  igetter.setCurrentFolder(folder);
104 
105  std::map<std::string, MEPair> me_pairs;
106 
107  for (const std::string& name : igetter.getMEs()) {
108  // If name doesn't start with prefix
109  // The default prefix is empty string.
110  if (name.rfind(prefix, 0) != 0) {
111  // TODO LogDebug
112  continue;
113  }
114 
115  const std::string fullpath = folder + "/" + name;
116  const MonitorElement* me = igetter.get(fullpath);
117  if (me == nullptr) {
118  edm::LogError(kLogCategory_) << "failed to get " << fullpath;
119  continue;
120  }
121 
122  const auto [parsing_success, var_name, gem_name, is_matched] = parseEfficiencySourceName(name);
123  if (not parsing_success) {
124  // TODO LogDebug
125  continue;
126  }
127 
128  const std::string key = var_name + "_" + gem_name;
129 
130  if (me_pairs.find(key) == me_pairs.end()) {
131  me_pairs[key] = {nullptr, nullptr};
132  }
133 
134  if (is_matched)
135  me_pairs[key].first = me;
136  else
137  me_pairs[key].second = me;
138  }
139 
140  // remove invalid pairs
141  for (auto it = me_pairs.cbegin(); it != me_pairs.cend();) {
142  auto [me_numerator, me_denominator] = (*it).second;
143 
144  bool okay = true;
145  if (me_numerator == nullptr) {
146  okay = false;
147 
148  } else if (me_denominator == nullptr) {
149  okay = false;
150 
151  } else if (me_numerator->kind() != me_denominator->kind()) {
152  okay = false;
153  }
154 
155  // anyways, move on to the next one
156  if (okay) {
157  it++;
158 
159  } else {
160  it = me_pairs.erase(it);
161  }
162  }
163 
164  return me_pairs;
165 }
166 
167 void GEMDQMEfficiencyClientBase::setBins(TH1F* dst_hist, const TAxis* src_axis) {
168  const int nbins = src_axis->GetNbins();
169  if (src_axis->IsVariableBinSize()) {
170  std::vector<double> edges;
171  edges.reserve(nbins + 1);
172 
173  for (int bin = 1; bin <= nbins; bin++) {
174  edges.push_back(src_axis->GetBinLowEdge(bin));
175  }
176  edges.push_back(src_axis->GetBinUpEdge(nbins));
177 
178  dst_hist->SetBins(nbins, &edges[0]);
179 
180  } else {
181  const double xlow = src_axis->GetBinLowEdge(1);
182  const double xup = src_axis->GetBinUpEdge(nbins);
183 
184  dst_hist->SetBins(nbins, xlow, xup);
185  }
186 
187  for (int bin = 1; bin <= nbins; bin++) {
188  const TString label{src_axis->GetBinLabel(bin)};
189  if (label.Length() > 0) {
190  dst_hist->GetXaxis()->SetBinLabel(bin, label);
191  }
192  }
193 }
194 
195 // Returns a boolean indicating whether the numerator and the denominator are
196 // consistent.
197 //
198 // TEfficiency::CheckConsistency raises errors and leads to an exception.
199 // So, the efficiency client will skip inconsitent two histograms.
200 // https://github.com/root-project/root/blob/v6-24-06/hist/hist/src/TEfficiency.cxx#L1494-L1512
201 bool GEMDQMEfficiencyClientBase::checkConsistency(const TH1& pass, const TH1& total) {
202  if (pass.GetDimension() != total.GetDimension()) {
203  edm::LogError(kLogCategory_) << "numerator and denominator have different dimensions: " << pass.GetName() << " & "
204  << total.GetName();
205  return false;
206  }
207 
208  if (not TEfficiency::CheckBinning(pass, total)) {
209  edm::LogError(kLogCategory_) << "numerator and denominator have different binning: " << pass.GetName() << " & "
210  << total.GetName();
211  return false;
212  }
213 
214  if (not TEfficiency::CheckEntries(pass, total)) {
215  edm::LogError(kLogCategory_) << "numerator and denominator do not have consistent bin contents " << pass.GetName()
216  << " & " << total.GetName();
217  return false;
218  }
219 
220  return true;
221 }
222 
223 // MonitorElement doesn't support TGraphAsymmErrors
224 TH1F* GEMDQMEfficiencyClientBase::makeEfficiency(const TH1F* h_numerator,
225  const TH1F* h_denominator,
226  const char* name,
227  const char* title) {
228  if (h_numerator == nullptr) {
229  edm::LogError(kLogCategory_) << "numerator is nullptr";
230  return nullptr;
231  }
232 
233  if (h_denominator == nullptr) {
234  edm::LogError(kLogCategory_) << "denominator is nulpptr";
235  return nullptr;
236  }
237 
238  if (not checkConsistency(*h_numerator, *h_denominator)) {
239  return nullptr;
240  }
241 
242  if (name == nullptr) {
243  name = Form("eff_%s", h_denominator->GetName());
244  }
245 
246  if (title == nullptr) {
247  title = h_denominator->GetTitle();
248  }
249 
250  const TAxis* x_axis = h_denominator->GetXaxis();
251 
252  // create an empty TProfile for storing efficiencies and uncertainties.
253  TH1F* h_eff = new TH1F();
254  h_eff->SetName(name);
255  h_eff->SetTitle(title);
256  h_eff->GetXaxis()->SetTitle(x_axis->GetTitle());
257  h_eff->GetYaxis()->SetTitle("Efficiency");
258  setBins(h_eff, h_denominator->GetXaxis());
259 
260  // efficiency calculation
261  const int nbins = x_axis->GetNbins();
262  for (int bin = 1; bin <= nbins; bin++) {
263  const double passed = h_numerator->GetBinContent(bin);
264  const double total = h_denominator->GetBinContent(bin);
265 
266  if (total < 1) {
267  continue;
268  }
269 
270  const double efficiency = passed / total;
271  const double lower_boundary = TEfficiency::ClopperPearson(total, passed, kConfidenceLevel_, false);
272  const double upper_boundary = TEfficiency::ClopperPearson(total, passed, kConfidenceLevel_, true);
273  const double error = std::max(efficiency - lower_boundary, upper_boundary - efficiency);
274 
275  h_eff->SetBinContent(bin, efficiency);
276  h_eff->SetBinError(bin, error);
277  }
278 
279  return h_eff;
280 }
281 
282 //
283 TH2F* GEMDQMEfficiencyClientBase::makeEfficiency(const TH2F* h_numerator,
284  const TH2F* h_denominator,
285  const char* name,
286  const char* title) {
287  if (h_numerator == nullptr) {
288  edm::LogError(kLogCategory_) << "numerator is nullptr";
289  return nullptr;
290  }
291 
292  if (h_denominator == nullptr) {
293  edm::LogError(kLogCategory_) << "denominator is nulpptr";
294  return nullptr;
295  }
296 
297  if (not checkConsistency(*h_numerator, *h_denominator)) {
298  return nullptr;
299  }
300 
301  if (name == nullptr) {
302  name = Form("eff_%s", h_denominator->GetName());
303  }
304 
305  if (title == nullptr) {
306  title = h_denominator->GetTitle();
307  }
308 
309  TEfficiency eff(*h_numerator, *h_denominator);
310  auto h_eff = dynamic_cast<TH2F*>(eff.CreateHistogram());
311  h_eff->SetName(name);
312  h_eff->SetTitle(title);
313 
314  return h_eff;
315 }
316 
317 // FIXME TH2D::ProjectionX looks buggy
318 TH1F* GEMDQMEfficiencyClientBase::projectHistogram(const TH2F* h_2d, const unsigned int on_which_axis) {
319  if ((on_which_axis != TH1::kXaxis) and (on_which_axis != TH1::kYaxis)) {
320  edm::LogError(kLogCategory_) << "invalid choice: " << on_which_axis << "."
321  << " choose from [TH1::kXaxis (=1), TH1::kYaxis (=2)]";
322  return nullptr;
323  }
324 
325  const bool on_x_axis = (on_which_axis == TH1::kXaxis);
326 
327  // on which axis is the histogram projected?
328  const TAxis* src_proj_axis = on_x_axis ? h_2d->GetXaxis() : h_2d->GetYaxis();
329  // along which axis do the entries accumulate?
330  const TAxis* src_accum_axis = on_x_axis ? h_2d->GetYaxis() : h_2d->GetXaxis();
331 
332  const TString prefix = on_x_axis ? "_proj_on_x" : "_proj_on_y";
333  const TString name = h_2d->GetName() + prefix;
334  const TString title = h_2d->GetTitle();
335 
336  TH1F* h_proj = new TH1F();
337  h_proj->SetName(name);
338  h_proj->SetTitle(title);
339  h_proj->GetXaxis()->SetTitle(src_proj_axis->GetTitle());
340  setBins(h_proj, src_proj_axis);
341 
342  for (int proj_bin = 1; proj_bin <= src_proj_axis->GetNbins(); proj_bin++) {
343  double cumsum = 0.0;
344  for (int accum_bin = 1; accum_bin <= src_accum_axis->GetNbins(); accum_bin++) {
345  if (on_x_axis) {
346  cumsum += h_2d->GetBinContent(proj_bin, accum_bin);
347  } else {
348  cumsum += h_2d->GetBinContent(accum_bin, proj_bin);
349  }
350  }
351  h_proj->SetBinContent(proj_bin, cumsum);
352  }
353  h_proj->Sumw2();
354  return h_proj;
355 }
356 
358  DQMStore::IGetter& igetter,
359  const std::string& folder) {
360  const std::map<std::string, MEPair> me_pairs = makeEfficiencySourcePair(ibooker, igetter, folder);
361 
362  for (auto& [key, value] : me_pairs) {
363  const auto& [me_numerator, me_denominator] = value;
364 
365  const MonitorElement::Kind me_kind = me_numerator->kind();
366  if (me_kind == MonitorElement::Kind::TH1F) {
367  TH1F* h_numerator = me_numerator->getTH1F();
368  if (h_numerator == nullptr) {
369  edm::LogError(kLogCategory_) << "failed to get TH1F from h_numerator " << key;
370  continue;
371  }
372 
373  TH1F* h_denominator = me_denominator->getTH1F();
374  if (h_denominator == nullptr) {
375  edm::LogError(kLogCategory_) << "failed to get TH1F from h_denominator" << key;
376  continue;
377  }
378 
379  if (TH1F* eff = makeEfficiency(h_numerator, h_denominator)) {
380  ibooker.book1D(eff->GetName(), eff);
381 
382  } else {
383  // makeEfficiency will report the error.
384  continue;
385  }
386 
387  } else if (me_kind == MonitorElement::Kind::TH2F) {
388  TH2F* h_numerator = me_numerator->getTH2F();
389  if (h_numerator == nullptr) {
390  edm::LogError(kLogCategory_) << "failed to get TH1F from h_numerator " << key;
391  continue;
392  }
393 
394  TH2F* h_denominator = me_denominator->getTH2F();
395  if (h_denominator == nullptr) {
396  edm::LogError(kLogCategory_) << "failed to get TH1F from h_denominator" << key;
397  continue;
398  }
399 
400  if (TH2F* eff = makeEfficiency(h_numerator, h_denominator)) {
401  ibooker.book2D(eff->GetName(), eff);
402 
403  } else {
404  // makeEfficiency will report the error.
405  continue;
406  }
407 
408  } else {
409  edm::LogError(kLogCategory_) << "got an unepxected MonitorElement::Kind "
410  << "0x" << std::hex << static_cast<int>(me_kind);
411  continue;
412  }
413 
414  } // me_pairs
415 }
GEMDetId parseGEMLabel(const std::string, const std::string delimiter="-")
virtual void setCurrentFolder(std::string const &fullpath)
Definition: DQMStore.cc:36
virtual std::vector< std::string > getMEs() const
Definition: DQMStore.cc:744
TH1F * makeEfficiency(const TH1F *, const TH1F *, const char *name=nullptr, const char *title=nullptr)
GEMDQMEfficiencyClientBase(const edm::ParameterSet &)
void setBins(TH1F *, const TAxis *)
void bookEfficiencyAuto(DQMStore::IBooker &, DQMStore::IGetter &, const std::string &)
Log< level::Error, false > LogError
uint16_t size_type
bool checkConsistency(const TH1 &, const TH1 &)
constexpr std::array< uint8_t, layerIndexSize< TrackerTraits > > layer
virtual TH2F * getTH2F() const
char const * label
The Signals That Services Can Subscribe To This is based on ActivityRegistry and is current per Services can connect to the signals distributed by the ActivityRegistry in order to monitor the activity of the application Each possible callback has some defined which we here list in angle e< void, edm::EventID const &, edm::Timestamp const & > We also list in braces which AR_WATCH_USING_METHOD_ is used for those or
Definition: Activities.doc:12
TH1F * projectHistogram(const TH2F *, const unsigned int)
Definition: value.py:1
MonitorElement * book2D(TString const &name, TString const &title, int nchX, double lowX, double highX, int nchY, double lowY, double highY, FUNC onbooking=NOOP())
Definition: DQMStore.h:212
virtual MonitorElement * get(std::string const &fullpath) const
Definition: DQMStore.cc:697
MonitorElement * book1D(TString const &name, TString const &title, int const nchX, double const lowX, double const highX, FUNC onbooking=NOOP())
Definition: DQMStore.h:98
std::tuple< bool, std::string, std::string, bool > parseEfficiencySourceName(std::string)
std::map< std::string, MEPair > makeEfficiencySourcePair(DQMStore::IBooker &, DQMStore::IGetter &, const std::string &, const std::string prefix="")