def load(self): # Run default loading templates.Templates.load(self) mc_channels = set(["mc", ]) channel.expand(self._channel_config, mc_channels) for var in ['pt', 'eta', 'phi']: for flavor in ['b', 'c', 'l']: key = '%s_%cjet' % (var, flavor) input = '/BTagEff_Chi2sel/%s_%cJet' % (var, flavor) for channel_, plot_ in self.plots[input].items(): if "data" == channel_ or "qcd" == channel_: break elif channel_ in mc_channels: if key in self._btag: self._btag[key].Add(plot_) self._btag[key+'_btag'].Add(self.plots[input+'_bTag'][channel_]) else: self._btag[key] = plot_.Clone() self._btag[key+'_btag'] = self.plots[input+'_bTag'][channel_].Clone()
def load(self): ''' Load all channels into memory Child classes may explicitly call this function to load plots ''' bg_channels = set(["mc", ]) channel.expand(self._channel_config, bg_channels) for channel_ in self._channels: ch_loader = self._channel_loader(self._prefix, verbose=self._verbose) ch_loader.load(self._channel_config, self._plot_config, channel_, plot_patterns=self._plot_patterns) channel_scale_ = (self._channel_scale and self._channel_scale.get(channel_, None)) if self._verbose and channel_scale_: print("custom scale", channel_, "x{0:.3f}".format(channel_scale_)) # Channel plots are loaded. Store plots in the dictionary with # keys equal to plot name and values are dictionaries with keys # being the channel and values are plots; scale the plots if scale # is provided # for key, hist in ch_loader.plots.items(): if key not in self.plots: self._plots[key] = {} if channel_scale_: hist.Scale(channel_scale_) if self._bg_error and channel_ in bg_channels: error.add(hist, self._bg_error) self._plots[key][channel_] = hist del(ch_loader)
def __init__(self, options, args, config): SignalOverBackground.__init__(self, options, args, config) self._background_channels = set(["mc", ]) channel.expand(self._channel_config, self._background_channels)
def __init__(self, options, args, config, channel_loader=loader.ChannelLoader): self._verbose = (options.verbose if options.verbose else config["core"]["verbose"]) self._batch_mode = (options.batch if options.batch else config["core"]["batch"]) if self._batch_mode and not options.save and self._verbose: print("warning: script is run in batch mode but canvases are not " "saved") # load channel configuration # channel_config = (options.channel_config if options.channel_config else config["template"]["channel"]) if not channel_config: raise RuntimeError("channel config is not defined") self._channel_config = channel.load(os.path.expanduser(channel_config)) # load channel scale(s) # self._channel_scale = ( scale.load(os.path.expanduser(options.channel_scale), self._channel_config) if options.channel_scale else None) # load plot configuration # plot_config = (options.plot_config if options.plot_config else config["template"]["plot"]) if not plot_config: raise RuntimeError("plot config is not defined") self._plot_config = plot.load(os.path.expanduser(plot_config)) # Get list of channels to be loaded # if not options.channels: raise RuntimeError("no channels are specified") use, ban = split_use_and_ban(ch.strip() for ch in options.channels.split(',')) if not use: raise RuntimeError("no channels are turned ON") use = channel.expand(self._channel_config, use, verbose=self._verbose) if ban: use -= channel.expand(self._channel_config, ban, verbose=self._verbose) if not use: raise RuntimeError("all channels are turned OFF") self._channels = use # Loaded plots are kept in the structures like: # plot_name: { # channel1: hist_obj, # channel2: hist_obj, # etc. # } # self._plots = {} self._channel_loader = channel_loader self._prefix = options.prefix if options.save: value = options.save.lower() if value not in ["ps", "pdf"]: raise RuntimeError("unsupported save format: " + value) self._save = value else: self._save = False self._bg_error = options.bg_error if self._bg_error: self._bg_error = float(self._bg_error) if not (0 <= self._bg_error <= 100): raise RuntimeError("background error is out of range") else: self._bg_error /= 100 self._label = options.label or os.getenv("EXO_PLOT_LABEL", None) self._sub_label = options.sub_label or os.getenv("EXO_PLOT_SUBLABEL", None) if options.plots: self._plot_patterns = options.plots.split(':') else: self._plot_patterns = [] self._legend_align = "right" self._legend_valign = "top" self._log = options.log self._ratio= options.ratio
def plot(self): ''' Process loaded histograms and draw them The plot() method is responsible for processing loaded channels, e.g. put data into Stack, conbine signals, etc. ''' canvases = [] bg_channels = set(["mc", "qcd"]) channel.expand(self._channel_config, bg_channels) for plot_, channels in self.plots.items(): # Prepare stacks for data, background and signal signal = ROOT.THStack() background = ROOT.THStack() data = None legend = ROOT.TLegend(0, 0, 0, 0) # coordinates will be adjusted # prepare channels order and append any missing channels to the # end in random order order = self._channel_config["order"] order.extend(set(channels.keys()) - set(order)) # process channels in order backgrounds = [] # backgrounds should be added to THStack in # reverse order to match legend order for channel_ in order: if channel_ not in channels: continue hist = channels[channel_] if channel_ in bg_channels: backgrounds.append(hist) label = "fe" elif (channel_.startswith("zp") or channel_.startswith("rsg")): # Signal order does not matter signal.Add(hist) label = "l" elif channel_ == "data": data = hist label = "lpe" legend.AddEntry(hist, self._channel_config["channel"][channel_] ["legend"], label) # Add backgrounds to the Stack if backgrounds: map(background.Add, reversed(backgrounds)) canvas = self.draw_canvas(plot_, signal=signal, background=background, data=data, legend=legend) if canvas: canvases.append(canvas) return canvases
def load(self): # Run default loading templates.Templates.load(self) # Run TFraction Fitter to get MC and QCD scales try: if self._verbose: print("{0:-<80}".format("-- TFractionFitter ")) if self._tff_input not in self.plots: raise RuntimeError("load plot " + self._tff_input) # Extract met DATA, QCD and MC met = {} mc_channels = set(["mc"]) channel.expand(self._channel_config, mc_channels) for channel_, plot_ in self.plots[self._tff_input].items(): if "data" == channel_: met["data"] = plot_ elif "qcd" == channel_: met["qcd"] = plot_ elif channel_ in mc_channels: if "mc" in met: met["mc"].Add(plot_) else: met["mc"] = plot_.Clone() print(met.keys()) missing_channels = set(["data", "qcd", "mc"]) - set(met.keys()) if missing_channels: raise RuntimeError("channels {0!r} are not loaded".format(missing_channels)) # prepare variable tempaltes for TFractionFitter templates_ = ROOT.TObjArray(2) templates_.Add(met["mc"]) templates_.Add(met["qcd"]) # Setup TFractionFitter fitter = ROOT.TFractionFitter(met["data"], templates_) # Run TFRactionFitter fit_status = fitter.Fit() if fit_status: raise RuntimeError("fitter error {0}".format(fit_status)) # Extract MC and QCD scales from TFractionFitter and keep # only central values (drop errors) fraction = ROOT.Double(0) fraction_error = ROOT.Double(0) fractions = {} fraction_errors = {} scales = {} scale_errors = {} data_integral_ = met["data"].Integral(0, met["data"].GetNbinsX() + 1) qcd_integral_ = met["qcd"].Integral(0, met["qcd"].GetNbinsX() + 1) mc_integral_ = met["mc"].Integral(0, met["mc"].GetNbinsX() + 1) mc_integral_error_ = 0.0 for i in range(0, met["mc"].GetNbinsX() + 2): mc_integral_error_ = mc_integral_error_ + met["mc"].GetBinError(i) ** 2 mc_integral_error_ = math.sqrt(mc_integral_error_) / mc_integral_ fitter.GetResult(0, fraction, fraction_error) fractions["mc"] = float(fraction) fraction_errors["mc"] = float(fraction_error) / float(fraction) scales["mc"] = float(fraction) * data_integral_ / mc_integral_ scale_errors["mc"] = math.sqrt((1 / data_integral_) + mc_integral_error_ ** 2 + fraction_errors["mc"] ** 2) fitter.GetResult(1, fraction, fraction_error) fractions["qcd"] = float(fraction) fraction_errors["qcd"] = float(fraction_error) / float(fraction) scales["qcd"] = float(fraction) * data_integral_ / qcd_integral_ scale_errors["qcd"] = math.sqrt( (1 / data_integral_) + mc_integral_error_ ** 2 + fraction_errors["qcd"] ** 2 ) # Print found fractions if self._verbose: print( "\n".join( "{0:>3} fraction: {1:.3f} +- {2:.1f}%".format(key.upper(), value, 100 * fraction_errors[key]) for key, value in fractions.items() ) ) print( "\n".join( "{0:>3} scale: {1:.3f} +- {2:.1f}%".format(key.upper(), value, 100 * scale_errors[key]) for key, value in scales.items() ) ) # Scale all MC and QCD samples with fractions for plot_, channels_ in self.plots.items(): # Skip normalization if one of the channels is missing if "data" not in channels_ or "qcd" not in channels_: continue # Get MC sum mc_sum_ = None for channel_, hist_ in channels_.items(): if channel_ in mc_channels: if mc_sum_: mc_sum_.Add(hist_) else: mc_sum_ = hist_.Clone() if not mc_sum_: continue data_integral_ = channels_["data"].Integral() for channel_, hist_ in channels_.items(): if channel_ in mc_channels: hist_.Scale(scales["mc"]) elif "qcd" == channel_: hist_.Scale(scales["qcd"]) except RuntimeError as error: if self._verbose: print("failed to use TFractionFitter - {0}".format(error), file=sys.stderr) finally: if self._verbose: print()
def plot(self): """ Process loaded histograms and draw these """ # the main executable script should make sure only one plot is loaded: # /cutflow or /cutflow_no_weight channels = self.plots[self.plots.keys().pop()] signal_channels = set(["zp", "zpwide", "kk"]) channel.expand(self._channel_config, signal_channels) background_channels = set(["mc"]) channel.expand(self._channel_config, background_channels) signal_ = {} background_ = {} total_background_ = None data_ = None for channel_, hist in channels.items(): if channel_ in signal_channels: signal_[channel_] = cutflow(hist) elif channel_ in background_channels: background_[channel_] = cutflow(hist) if not total_background_: total_background_ = hist.Clone() else: total_background_.Add(hist) elif channel_ == "data": data_ = cutflow(hist) total_background_ = cutflow(total_background_) channel_names = ( { "zprime_m1000_w10": r"Z' 1 Tev/c\textsuperscript{2}", "zprime_m2000_w20": r"Z' 2 Tev/c\textsuperscript{2}", "zprime_m3000_w30": r"Z' 3 Tev/c\textsuperscript{2}", "stop": r"Single-Top", "zjets": r"$Z/\gamma^{\ast}\rightarrow l^{+}l^{-}$", # "wjets": r"$W\rightarrow l\nu$", "wb": r"$W\rightarrow l\nu$ (bX)", "wc": r"$W\rightarrow l\nu$ (cX)", "wlight": r"$W\rightarrow l\nu$ (lightX)", "ttbar": r"QCD $t\bar{t}$", } if "text" != self._print_mode else { "zprime_m1000_w10": r"Z' 1 Tev", "zprime_m2000_w20": r"Z' 2 Tev", "zprime_m3000_w30": r"Z' 3 Tev", "stop": r"Single-Top", "zjets": r"Z/gamma -> l+ l-", # "wjets": r"W -> l nu", "wb": r"W-> l nu (bX)", "wc": r"W-> l nu (cX)", "wlight": r"W-> l nu (lightX)", "ttbar": r"QCD ttbar", } ) print_function = print_cutflow_in_text if self._print_mode == "text" else print_cutflow_in_tex fields_to_print = [["jets", "electron", "veto_lepton", "twod_cut"], ["jet1", "htlep", "tricut", "met"]] if self._non_threshold: fields_to_print.append(["chi2", "non_threshold", "eff"]) for samples_cutflow_ in (signal_, background_): if not samples_cutflow_: continue for channel_, cutflow_ in samples_cutflow_.items(): cutflow_["eff"] = efficiency(cutflow_.get("non_threshold", 0), cutflow_.get("chi2", 0)) for cutflow_ in (total_background_, data_): cutflow_["eff"] = efficiency(cutflow_.get("non_threshold", 0), cutflow_.get("chi2", 0)) for fields in fields_to_print: print_function(None, None, fields) for channel_ in self._channel_config["order"]: if not channel_.startswith("zprime") or channel_ not in signal_: continue print_function(channel_names.get(channel_, channel_), signal_[channel_], fields) for channel_ in ["stop", "zjets", "wb", "wc", "wlight", "ttbar"]: if channel_ not in background_: continue print_function(channel_names.get(channel_, channel_), background_[channel_], fields) print_function("Total MC", total_background_, fields) if data_: print_function("Data 2011", data_, fields) print() return None