def main (): # Parse command-line arguments args = parser.parse_args() # Settings signals = ['RPV', 'Rhadron'] variables = ['pt', 'prodR'] histname = "IDPerformanceMon/LargeD0/basicPlot/SignalParticles/truth{var}" rebin = { 'RPV': 2, 'Rhadron': 1, } # Loop variables for var in variables: # Load histograms histograms = list() hn = histname.format(var=var) for signal in signals: f = ROOT.TFile(filename.format(signal=signal), 'READ') try: h = f.Get(hn) h.SetDirectory(0) if signal in rebin: h.Rebin(rebin[signal]) except: # Plot doesn't exist for 'signal' print "Histogram '%s' does not exist for signal '%s'" % (hn, signal) h = None continue histograms.append(h) pass # Draw figure c = ap.canvas(batch=not args.show) for hist, signal, col in zip(histograms, signals, colours): c.hist(hist, label=signal_line(signal), linecolor=col, normalise=True) pass c.xlabel(displayNameUnit(var)) c.ylabel("Fraction of signal particles") c.text(["MC truth"], qualifier=qualifier) c.legend() c.logy() if args.save: c.save('plots/distributions_{var}.pdf'.format(var=var)) if args.show: c.show() pass return
def main(): # Parse command-line arguments args = parser.parse_args() # Initialise categories for which to plot distinct curved for each histogram algorithms = ['Standard', 'LargeD0'] names = ['Standard', 'Large radius', 'Combined'] types = ['', 'Primary', 'Secondary', 'Signal'] signals = ['RPV', 'Rhadron'] # Initialise variable versus which to plot the physics efficiency basic_vars = [ 'eta', 'phi', 'd0', 'z0', 'pt', 'R', 'Z', 'pt_low', 'pt_high' ] # Initialise list of histograms to be plotted base = 'IDPerformanceMon/LargeD0/' histname = base + 'EffPlots/{alg}Tracks/{t}trackeff_vs_{var}' # Read in and plot each histogram for fn, var, t, signal in itertools.product([filename], basic_vars, types, signals): # Generate list of (path, histname) pairs to plot pathHistnamePairs = zip([fn.format(signal=signal)] * len(algorithms), [ histname.format(t=t + ('/' if t != '' else ''), alg=alg, var=var) for alg in algorithms ]) # Load in histograms histograms = list() for path, hn in pathHistnamePairs: f = ROOT.TFile(path, 'READ') h = f.Get(hn) h.SetDirectory(0) if '_vs_R' in hn: if signal == 'Rhadron': h.Rebin(2) else: xbins = [ 0., 10., 20., 30., 40., 50., 70., 90., 110., 130., 150., 175., 200., 225., 250., 275., 300. ] h.Rebin(len(xbins) - 1, 'hnew', array('d', xbins)) h = ROOT.gDirectory.Get('hnew') h.SetDirectory(0) pass histograms.append(h) f.Close() pass # Compute combined efficiency h0 = histograms[0] h1 = histograms[1] ax = h0.GetXaxis() combined_eff = h0.Clone( h0.GetName() + '_comb' ) # ROOT.TProfile(h0.GetName() + '_comb', "", ax.GetNbins(), ax.GetXmin(), ax.GetXmax()) combined_eff.Reset() for bin in range(1, ax.GetNbins() + 1): num_total = int(h0.GetBinEntries(bin)) num_pass1 = int(h0.GetBinContent(bin) * num_total) num_pass2 = int(h1.GetBinContent(bin) * num_total) num_pass = num_pass1 + num_pass2 num_fail = num_total - num_pass x = combined_eff.GetBinCenter(bin) for _ in range(num_pass): combined_eff.Fill(x, 1) for _ in range(num_fail): combined_eff.Fill(x, 0) pass histograms.append(combined_eff) # Draw figure c = ap.canvas(batch=not args.show, size=(700, 500)) for ihist, (hist, name, col) in enumerate(zip(histograms, names, colours)): c.plot(hist, linecolor=col, markercolor=col, linestyle=1 + ihist, markerstyle=20 + ihist, label=name + (" tracks" if name != "Combined" else ""), legend_option='PL') pass c.text( [signal_line(signal)], # + ([t + " particles"] if t != '' else []), qualifier=qualifier) if ('Signal' in hn) and ('_vs_R' in hn): c.ylim(0, 1.6) pass c.xlabel(displayNameUnit( var)) # hist.GetXaxis().GetTitle().replace('prod.', 'prod')) c.ylabel("Reconstruction effiency") c.legend(width=0.28) # Radial locations of detector stuff if '_vs_R' in hn: """ Ugly vertical lines opts = {'linecolor': ROOT.kRed, 'linestyle': 3, 'text_horisontal': 'R', 'text_vertical': 'M'} c.xline( 33.25, **opts) c.xline( 50.5, **opts) c.xline( 88.5, **opts) c.xline(122.5, **opts) opts['linecolor'] = ROOT.kBlue c.xline( 45.5, **opts) c.xline(242, **opts) c.xline(255, **opts) opts['linecolor'] = ROOT.kGreen c.xline( 229, **opts) """ """ Pretty vertical lines opts = {'linecolor': ROOT.kRed, 'linestyle': 3, 'text_horisontal': 'R', 'text_vertical': 'M'} opts['linecolor'] = ROOT.kGray + 1 c.xline( 33.25, text='IBL', **opts) c.xline( 50.5, text='Pix. Layer 1', **opts) c.xline( 88.5, text='Pix. Layer 2', **opts) if signal == 'Rhadron': opts['text_vertical'] = 'T' pass c.xline(122.5, text='Pix. Layer 3', **opts) #c.xline(299., text='Layer 4', **opts) opts['linecolor'] = ROOT.kBlue opts['text_horisontal'] = 'L' opts['text_vertical'] = 'M' opts['linecolor'] = ROOT.kGray + 2 c.xline( 45.5, text='Envelope 1', **opts) opts['text_vertical'] = 'T' c.xline(242, text='Envelope 2', **opts) c.xline(255, text='Envelope 3', **opts) opts['linecolor'] = ROOT.kGreen opts['linecolor'] = ROOT.kGray + 3 c.xline( 229, text='Pixel tube', **opts) """ #xlines = [33.25, 50.5, 88.5, 122.5, 299, # layers # 45.5, 242, 255, # envelopes # ] #c.xlines(xlines, linecolor=ROOT.kRed - 4) pass # Show/save savename = '_'.join([signal] + histname.format( t=t + ('/' if t != '' else ''), alg='', var=var).split('/')[2:]) + '.pdf' if args.show: c.show() if args.save: c.save('plots/' + savename) pass return
def main (): # Macro-specific styles ROOT.gROOT.GetStyle("AStyle").SetEndErrorSize(.5) # Parse command-line arguments args = parser.parse_args() # Initialise categories for which to plot distinct curves for each histogram algorithms = ['Standard', 'LargeD0'] names = ['Standard', 'Large radius'] types = ['Signal'] # ['All', 'Signal'] signals = ['RPV', 'Rhadron'] groups = ['R10mm_30mm/', 'R30mm_100mm/', 'R100mm_300mm/', ] #groups = ['R20mm_50mm/', # 'R100mm_150mm/', # 'R200mm_300mm/', # ] group_names = [ '[%s]' % grp[1:-1].replace('_', ', ').replace('p', '.').replace('mm', ' mm') for grp in groups ] # @TEMP: Fix type in histogram names: 30mm_300mm -> 100mm_300mm #group_names = [gn.replace('30mm, 300mm', '100mm, 300mm') for gn in group_names] # Initialise list of histograms to be plotted base = 'IDPerformanceMon/LargeD0/' histname = base + 'EffPlots/{alg}Tracks/{t}/{group}trackeff_vs_mu' edges = [0, 10, 15, 20, 25, 30, 40] # Loop all combinations of truth particle type and signal process for t, signal in itertools.product(types, signals): # Open file from which to read histograms. f = ROOT.TFile(filename.format(signal=signal), 'READ') ROOT.TH1.AddDirectory(False) ROOT.TH2.AddDirectory(False) # Get list of histograms tp plot, manually. histograms = list() # Get histograms for alg in algorithms: # Loop production radii groups for group in groups: h = f.Get(histname.format(alg=alg, t=t, group=group)) h.SetDirectory(0) # Keep in memory after file is closed. #h.RebinX(2) newname = h.GetName() + "_rebinned_" + group + "_" + alg hn = h.Rebin(len(edges)-1, newname, array.array('d',edges)) #histograms.append(h) histograms.append(hn) pass pass # Close file f.Close() # Efficiency of STD and LRT separately # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Draw figure c = ap.canvas(batch=not args.show, size=(700, 500)) for i, alg in enumerate(algorithms): N = len(group_names) N1, N2 = i * N, (i + 1) * N for hist, name, col in zip(histograms[N1:N2], group_names, colours): c.plot(hist, linecolor=col, markercolor=col, markerstyle=4*i+20, linewidth=2, linestyle=i+1, label=name if i == 0 else None) pass pass c.text([signal_line(signal)] + (["%s particles" % t] if t != 'Signal' else []), qualifier=qualifier) c.legend(header=displayName('r') + " in:", categories=[(name, {'linestyle': i+1, 'markerstyle': 4*i+20, 'option': 'PL', 'linewidth': 2}) for i, name in enumerate(names)], width=0.28) c.xlabel("#LT#mu#GT") c.ylabel("Reconstruction efficiency") c.ylim(0, 1.8) # Show/save savename = '_'.join([signal] + histname.format(alg='', t=t, group='').split('/')[2:]) + '.pdf' if args.show: c.show() if args.save: c.save('plots/' + savename) # Efficiency of STD and LRT combined # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Compute combined efficiency comb_histograms = list() for h1, h2 in zip(histograms[:len(groups)], histograms[len(groups):]): # h1: standard | h2: large radius ax = h1.GetXaxis() h_comb = h1.Clone('h_comb')#ROOT.TProfile('h_comb', "", ax.GetNbins(), ax.GetXmin(), ax.GetXmax()) h_comb.Reset() for bin in range(1, h1.GetXaxis().GetNbins() + 1): N_total = int(h1.GetBinEntries(bin)) # == h2.GetBinEntries(bin) N_1 = int(h1.GetBinContent(bin) * N_total) N_2 = int(h2.GetBinContent(bin) * N_total) mu = h1.GetBinCenter(bin) N_pass = N_1 + N_2 N_fail = N_total - N_pass for _ in xrange(N_pass): h_comb.Fill(mu, True) for _ in xrange(N_fail): h_comb.Fill(mu, False) pass comb_histograms.append(h_comb) pass # Draw figure c = ap.canvas(batch=not args.show, size=(700, 500)) for ihist, (hist, name, col) in enumerate(zip(comb_histograms, group_names, colours)): c.plot(hist, linecolor=col, markercolor=col, markerstyle=20+ihist, linestyle=1+ihist, linewidth=2, label=name) pass c.text([signal_line(signal)] + (["%s particles" % t] if t != 'Signal' else []) + ["Large radius and standard tracks"], qualifier=qualifier) c.legend(header=displayName('r') + " in:", width=0.28) c.xlabel("#LT#mu#GT") c.ylabel("Reconstruction efficiency") c.ylim(0, 1.8) # Show/save savename = '_'.join([signal] + histname.format(alg='Combined', t=t, group='').split('/')[2:]) + '.pdf' if args.show: c.show() if args.save: c.save('plots/' + savename) pass return
def main(): # Parse command-line arguments args = parser.parse_args() # Setup. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Get signal file sig_DSID = get_signal_DSID(args.mass, tolerance=10) if sig_DSID is None: return sig_file = 'objdef_MC_{DSID:6d}.root'.format(DSID=sig_DSID) # Load data files = glob.glob(tf.config['base_path'] + 'objdef_MC_3610*.root') + [ tf.config['base_path'] + sig_file ] if len(files) == 0: warning("No files found.") return data = loadData(files, tf.config['tree'], prefix=tf.config['prefix']) info = loadData(files, tf.config['outputtree'], stop=1) # Scaling by cross section xsec = loadXsec(tf.config['xsec_file']) # Append new DSID field # @TODO: Make more elegant? data = append_fields(data, 'DSID', np.zeros((data.size, )), dtypes=int) for idx in info['id']: msk = ( data['id'] == idx ) # Get mask of all 'data' entries with same id, i.e. from same file DSID = info['DSID'][idx] # Get DSID for this file data['weight'][msk] *= xsec[ DSID] # Scale by cross section x filter eff. for this DSID data['DSID'][msk] = DSID # Store DSID pass data['weight'] *= tf.config['lumi'] # Scale all events (MC) by luminosity # Check output. if data.size == 0: warning("No data was loaded.") return # Compute new variables data = append_fields(data, 'logpt', np.log(data['pt'])) # Separate out signal MC msk_sig = (data['DSID'] == sig_DSID) msk_data = ~msk_sig print "DATA STATISTICS:", np.sum(data[msk_data]['weight']) signal = data[msk_sig] if not args.inject: # If we're not injecting signal, explicitly remove it from the 'data' array data = data[~msk_sig] pass # Toys # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if args.toys: # Get masks msk_pass = tf.config['pass'](data) msk_fail = ~msk_pass # Create histograms if args.inject: pdf_pass = get_histogram(data, tf.config['params'], tf.config['axes'], mask=msk_pass & ~msk_sig) pdf_fail = get_histogram(data, tf.config['params'], tf.config['axes'], mask=msk_fail & ~msk_sig) else: pdf_pass = get_histogram(data, tf.config['params'], tf.config['axes'], mask=msk_pass) pdf_fail = get_histogram(data, tf.config['params'], tf.config['axes'], mask=msk_fail) pass # Smooth (only leading background) for _ in range(2): pdf_pass.Smooth() pdf_fail.Smooth() pass # Inject afterwards if args.inject: pdf_pass.Add( get_histogram(data, tf.config['params'], tf.config['axes'], mask=msk_pass & msk_sig)) pdf_fail.Add( get_histogram(data, tf.config['params'], tf.config['axes'], mask=msk_fail & msk_sig)) # Create p.d.f.s # -- Define variables rhoDDT = ROOT.RooRealVar('rhoDDT', 'rhoDDT', tf.config['axes'][0][0], tf.config['axes'][0][-1]) logpt = ROOT.RooRealVar('logpt', 'logpt', tf.config['axes'][1][0], tf.config['axes'][1][-1]) rhoDDT.setBins(len(tf.config['axes'][0]) - 1) logpt.setBins(len(tf.config['axes'][1]) - 1) # -- Define histograms rdh_pass = ROOT.RooDataHist('rdh_pass', 'rdh_pass', ROOT.RooArgList(rhoDDT, logpt), pdf_pass) rdh_fail = ROOT.RooDataHist('rdh_fail', 'rdh_fail', ROOT.RooArgList(rhoDDT, logpt), pdf_fail) # -- Turn histograms into pdf's rhp_pass = ROOT.RooHistPdf('rhp_pass', 'rhp_pass', ROOT.RooArgSet(rhoDDT, logpt), rdh_pass) rhp_fail = ROOT.RooHistPdf('rhp_fail', 'rhp_fail', ROOT.RooArgSet(rhoDDT, logpt), rdh_fail) # Generate toys mult = 1. N_pass = int(np.sum(data['weight'][msk_pass]) * mult) N_fail = int(np.sum(data['weight'][msk_fail]) * mult) dtype = ['rhoDDT', 'logpt', 'tau21DDT', 'pt', 'm', 'weight'] dtype = [(var, 'f8') for var in dtype] toys_pass = np.zeros(N_pass, dtype=dtype) toys_fail = np.zeros(N_fail, dtype=dtype) print "Generating toys (pass: %d, fail: %d)" % (N_pass, N_fail) rds_pass = rhp_pass.generate(ROOT.RooArgSet(rhoDDT, logpt), N_pass, True, False) rds_fail = rhp_fail.generate(ROOT.RooArgSet(rhoDDT, logpt), N_fail, True, False) for idx in range(N_pass): toys_pass['rhoDDT'][idx] = rds_pass.get(idx).getRealValue('rhoDDT') toys_pass['logpt'][idx] = rds_pass.get(idx).getRealValue('logpt') toys_pass['pt'][idx] = np.exp(toys_pass['logpt'][idx]) toys_pass['m'][idx] = np.sqrt( np.exp(toys_pass['rhoDDT'][idx]) * toys_pass['pt'][idx] * 1.) toys_pass['weight'][idx] = 1. / float(mult) toys_pass['tau21DDT'][idx] = 0. pass for idx in range(N_fail): toys_fail['rhoDDT'][idx] = rds_fail.get(idx).getRealValue('rhoDDT') toys_fail['logpt'][idx] = rds_fail.get(idx).getRealValue('logpt') toys_fail['pt'][idx] = np.exp(toys_fail['logpt'][idx]) toys_fail['m'][idx] = np.sqrt( np.exp(toys_fail['rhoDDT'][idx]) * toys_fail['pt'][idx] * 1.) toys_fail['weight'][idx] = 1. / float(mult) toys_fail['tau21DDT'][idx] = 1. pass data = np.concatenate((toys_pass, toys_fail)) # ??? pass # Transfer factor # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - calc = tf.calculator(data=data, config=tf.config) # Using default configuration calc.mass = args.mass calc.fullfit() # Pass/fail masks msk_data_pass = tf.config['pass'](data) msk_data_fail = ~msk_data_pass msk_sig_pass = tf.config['pass'](signal) msk_sig_fail = ~msk_sig_pass print " -- Computing data weights" w_nom, w_up, w_down = calc.fullweights(data[msk_data_fail]) print " -- Computing signal weights" w_sig, _, _ = calc.fullweights(signal[msk_sig_fail]) print " -- Final fit done" if args.show or args.save: calc.plot(show=args.show, save=args.save, prefix='plots/new_signalinjection_%s%s_' % ("toys_" if args.toys else "", "injected" if args.inject else "notinjected")) # Performing signal injection test # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if True or args.show or args.save: bestfit_mu = None for mu, fit, prefit, subtract in zip([0, 1, 1, None], [False, False, True, False], [True, True, True, False], [True, True, False, True]): if not prefit: mu = bestfit_mu[0] pass # Plotting # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - c = ap.canvas(num_pads=2, batch=not args.show) p0, p1 = c.pads() # -- Histograms: Main pad bins = tf.config['massbins'] h_bkg = c.hist(data['m'][msk_data_fail], bins=bins, weights=data['weight'][msk_data_fail] * w_nom, display=False) h_bkg_up = c.hist(data['m'][msk_data_fail], bins=bins, weights=data['weight'][msk_data_fail] * w_up, display=False) h_bkg_down = c.hist(data['m'][msk_data_fail], bins=bins, weights=data['weight'][msk_data_fail] * w_down, display=False) h_sig = c.hist(signal['m'][msk_sig_pass], bins=bins, weights=signal['weight'][msk_sig_pass], scale=mu, display=False) h_sfl = c.hist(signal['m'][msk_sig_fail], bins=bins, weights=signal['weight'][msk_sig_fail] * w_sig, scale=mu, display=False) h_data = c.plot(data['m'][msk_data_pass], bins=bins, weights=data['weight'][msk_data_pass], display=False) for bin in range(1, h_bkg.GetXaxis().GetNbins() + 1): width = float(h_bkg.GetBinWidth(bin)) h_bkg.SetBinContent(bin, h_bkg.GetBinContent(bin) / width) h_bkg.SetBinError(bin, h_bkg.GetBinError(bin) / width) h_bkg_up.SetBinContent(bin, h_bkg_up.GetBinContent(bin) / width) h_bkg_up.SetBinError(bin, h_bkg_up.GetBinError(bin) / width) h_bkg_down.SetBinContent(bin, h_bkg_down.GetBinContent(bin) / width) h_bkg_down.SetBinError(bin, h_bkg_down.GetBinError(bin) / width) h_sig.SetBinContent(bin, h_sig.GetBinContent(bin) / width) h_sig.SetBinError(bin, h_sig.GetBinError(bin) / width) h_sfl.SetBinContent(bin, h_sfl.GetBinContent(bin) / width) h_sfl.SetBinError(bin, h_sfl.GetBinError(bin) / width) h_data.SetBinContent(bin, h_data.GetBinContent(bin) / width) h_data.SetBinError(bin, h_data.GetBinError(bin) / width) pass if not fit: h_bkg.Add(h_sfl, -1) # Subtracting signal h_bkg_up.Add(h_sfl, -1) # -- h_bkg_down.Add(h_sfl, -1) # -- pass c.hist( h_bkg, option='HIST', linestyle=0, fillstyle=0, fillcolor=0 ) # Staring with standard histogram, not THStack, just to get y-axis to coorperate h_bkg = c.stack(h_bkg, fillcolor=ROOT.kAzure + 7, label='Background pred.') h_sig = c.stack(h_sig, fillcolor=ROOT.kRed - 4, label="Z' (#mu = %s)" % ("%.0f" % mu if prefit else "%.2f #pm %.2f" % (mu, bestfit_mu[1]))) h_sum = h_bkg h_sum = c.hist(h_sum, fillstyle=3245, fillcolor=ROOT.kGray + 3, option='E2', label='Stat. uncert.') h_bkg_up = c.hist(h_bkg_up, linecolor=ROOT.kGreen + 1, linestyle=2, option='HIST', label='Syst. uncert.') h_bkg_down = c.hist(h_bkg_down, linecolor=ROOT.kGreen + 1, linestyle=2, option='HIST') h_data = c.plot(h_data, label='Pseudo-data') c.hist(h_bkg, option='AXIS') # Re-draw axes # -- Histograms: Ratio pad c.ratio_plot((h_sig, h_sum), option='HIST', offset=1) c.ratio_plot((h_sum, h_sum), option='E2') c.ratio_plot((h_bkg_up, h_sum), option='HIST') c.ratio_plot((h_bkg_down, h_sum), option='HIST') c.ratio_plot((h_data, h_sum)) # -- Axis labels c.xlabel('Large-#it{R} jet mass [GeV]') c.ylabel('Events / GeV') p1.ylabel('Data / Est.') # -- Axis limits c.ylim(1.0E+00, 1.0E+06) p1.ylim(0.80, 1.20) # -- Line(s) p1.yline(1.0) # -- Region(s) c.region("SR", 0.8 * args.mass, 1.2 * args.mass) # -- Text c.text( [ "#sqrt{s} = 13 TeV, %s fb^{-1}" % tf.config['lumi'], "Incl. #gamma Monte Carlo", "Photon channel", #("Signal" if args.inject else "No signal") + " injected", ] + (["Using toys"] if args.toys else []), qualifier='Simulation Internal') # -- Log c.log() # -- Legend c.legend() if args.save and not fit: c.save('plots/new_signalinjection_%s%dGeV_pm%d_%s_%s.pdf' % ("toys_" if args.toys else "", args.mass, 20., ('prefit_mu%d' % mu if prefit else 'postfit'), ('injected' if args.inject else 'notinjected'))) if args.show and not fit: c.show() # Fitting # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if fit: bestfit_mu = list() hs_save = [ h_bkg_down.Clone('h_save_down'), h_bkg.Clone('h_save_nom'), h_bkg_up.Clone('h_save_up'), ] for variation in range(3): print "Variation: " + ("Nominal" if variation == 1 else ( "Up" if variation == 0 else "Down")) # Get correct histogram fore this variation h_bkg_use = hs_save[variation] # -- Define jet mass variable mJ = ROOT.RooRealVar('mJ', 'mJ', 50, 300) #mJ.setBins(50) roobinning = ROOT.RooBinning( len(tf.config['massbins']) - 1, tf.config['massbins']) mJ.setBinning(roobinning) # -- Define histograms rdh_bkg = ROOT.RooDataHist('rdh_bkg', 'rdh_bkg', ROOT.RooArgList(mJ), h_bkg_use) rdh_sig = ROOT.RooDataHist('rdh_sig', 'rdh_sig', ROOT.RooArgList(mJ), h_sig) rdh_sfl = ROOT.RooDataHist('rdh_sfl', 'rdh_sfl', ROOT.RooArgList(mJ), h_sfl) # -- Turn histograms into pdf's rhp_bkg = ROOT.RooHistPdf('rhp_bkg', 'rhp_bkg', ROOT.RooArgSet(mJ), rdh_bkg) rhp_sig = ROOT.RooHistPdf('rhp_sig', 'rhp_sig', ROOT.RooArgSet(mJ), rdh_sig) rhp_sfl = ROOT.RooHistPdf('rhp_sfl', 'rhp_sfl', ROOT.RooArgSet(mJ), rdh_sfl) # -- Define integrals as constants n_bkg = ROOT.RooRealVar('n_bkg', 'n_bkg', h_bkg_use.Integral()) n_sig = ROOT.RooRealVar('n_sig', 'n_sig', h_sig.Integral()) n_sfl = ROOT.RooRealVar('n_sfl', 'n_sfl', h_sfl.Integral()) # -- Define signal strength and constant(s) mu = ROOT.RooRealVar('mu', 'mu', 1, 0, 5) neg1 = ROOT.RooRealVar('neg1', 'neg1', -1) # -- Define fittable normalisation factors c_bkg = ROOT.RooFormulaVar('c_bkg', 'c_bkg', '@0', ROOT.RooArgList(n_bkg)) c_sig = ROOT.RooFormulaVar('c_sig', 'c_sig', '@0 * @1', ROOT.RooArgList(mu, n_sig)) c_sfl = ROOT.RooFormulaVar( 'c_sfl', 'c_sfl', '@0 * @1 * @2', ROOT.RooArgList(neg1, mu, n_sfl)) # -- Construct combined pdf pdf = ROOT.RooAddPdf( 'pdf', 'pdf', ROOT.RooArgList(rhp_bkg, rhp_sig, rhp_sfl), ROOT.RooArgList(c_bkg, c_sig, c_sfl)) # -- Construct data histogram rdh_data = ROOT.RooDataHist('rdh_data', 'rdh_data', ROOT.RooArgList(mJ), h_data) # -- Fit pdf to data histogram pdf.chi2FitTo(rdh_data, ROOT.RooLinkedList()) print "Best fit mu: %.3f +/- %.3f" % (mu.getValV(), mu.getError()) bestfit_mu.append((mu.getValV(), mu.getError())) pass bestfit_mu = bestfit_mu[1][0], np.sqrt( np.power( abs(bestfit_mu[0][0] - bestfit_mu[2][0]) / 2., 2.) + np.power(bestfit_mu[1][1], 2.)) pass pass pass return
def main (): # Parse command-line arguments args = parser.parse_args() # Setup. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Input file paths paths = { 'bkg': glob.glob(tf.config['base_path'] + 'objdef_MC_3610*.root'), 'WZ': glob.glob(tf.config['base_path'] + 'objdef_MC_3054*.root'), } # Load data treename = tf.config['finaltree'].replace('Jet_tau21DDT', 'Blinding') prefix = 'Jet_' bkg = loadData(paths['bkg'], treename, prefix=prefix) WZ = loadData(paths['WZ'], treename, prefix=prefix) info_bkg = loadData(paths['bkg'], tf.config['outputtree'], stop=1) info_WZ = loadData(paths['WZ'], tf.config['outputtree'], stop=1) # Check output if bkg.size == 0 or WZ.size == 0: warning("No data was loaded.") return # Scaling by cross-section xsec = loadXsec(tf.config['xsec_file']) bkg = scale_weights(bkg, info_bkg, xsec, lumi=tf.config['lumi']) WZ = scale_weights(WZ, info_WZ, xsec, lumi=tf.config['lumi']) # -- All events print "Number of background and W/Z events: {:9.1f} and {:9.1f} (all)".format( np.sum(bkg['weight']), np.sum(WZ['weight']) ) # -- Impose mass window msk = (bkg['m'] > 68.) & (bkg['m'] < 102.) bkg = bkg[msk] msk = (WZ['m'] > 68.) & (WZ['m'] < 102.) WZ = WZ[msk] print "Number of background and W/Z events: {:9.1f} and {:9.1f} (mass window)".format( np.sum(bkg['weight']), np.sum(WZ['weight']) ) # -- Impose default tau21DDT cut msk = bkg['tau21DDT'] < 0.5 bkg = bkg[msk] msk = WZ['tau21DDT'] < 0.5 WZ = WZ[msk] print "Number of background and W/Z events: {:9.1f} and {:9.1f} (def. tau21DDT)".format( np.sum(bkg['weight']), np.sum(WZ['weight']) ) # -- Cut to reduce SM W/Z + jets yield by 70% def wpercentile (data, percents, weights=None): """ percents in units of 1% weights specifies the frequency (count) of data. From [https://stackoverflow.com/a/31539746] """ if weights is None: return np.percentile(data, percents) ind = np.argsort(data) d = data[ind] w = weights[ind] p = 100. * w.cumsum() / w.sum() y = np.interp(percents, p, d) return y cut = wpercentile(WZ['tau21DDT'], 30, WZ['weight']) print "Percentile giving a 70% reduction in SM W/Z yield: {:.2f}".format(cut) msk_bkg = bkg['tau21DDT'] < cut msk_WZ = WZ['tau21DDT'] < cut print "Number of background and W/Z events: {:9.1f} and {:9.1f} (70% red. W/Z)".format( np.sum(bkg['weight'][msk_bkg]), np.sum(WZ['weight'][msk_WZ]) ) # -- Reduce to CMS yeild, if ATLAS = 1.7 * CMS cut = wpercentile(WZ['tau21DDT'], 100. / 1.7, WZ['weight']) print "Percentile giving a 70% reduction in SM W/Z yield: {:.2f}".format(cut) msk_bkg = bkg['tau21DDT'] < cut msk_WZ = WZ['tau21DDT'] < cut print "Number of background and W/Z events: {:9.1f} and {:9.1f} (1/170% red. W/Z)".format( np.sum(bkg['weight'][msk_bkg]), np.sum(WZ['weight'][msk_WZ]) ) # -- Cut to reduce background by 90% cut = wpercentile(bkg['tau21DDT'], 10, bkg['weight']) print "Percentile giving a 90% reduction in SM background yield: {:.2f}".format(cut) msk_bkg = bkg['tau21DDT'] < cut msk_WZ = WZ['tau21DDT'] < cut print "Number of background and W/Z events: {:9.1f} and {:9.1f} (90% red. bkg.)".format( np.sum(bkg['weight'][msk_bkg]), np.sum(WZ['weight'][msk_WZ]) ) print bkg.shape return # Compute significance improvements # -------------------------------------------------------------------------- # Loop tau21DDT bins cuts = np.linspace(axis['tau21DDT'][1], axis['tau21DDT'][2], 10 * axis['tau21DDT'][0] + 1, endpoint=True) bins = np.linspace(axis['m'] [1], axis['m'] [2], axis['m'] [0] + 1, endpoint=True) significances = [list() for _ in range(len(set(sig['DSID'])))] plot_cut=36 c_temp = ap.canvas(batch=True) for icut, cut in enumerate(cuts): print "Bin %2d: tau21DDT < %.3f" % (icut, cut) # Create histograms msk = bkg['tau21DDT'] < cut h_bkg = c_temp.hist(bkg['m'][msk], bins=bins, weights=bkg['weight'][msk], fillcolor=ROOT.kAzure+7, name='Background', display=(icut == plot_cut)) h_sigs = list() for idx, (DSID, name) in enumerate(zip(sorted(list(set(sig['DSID']))), sig_names)): msk = (sig['DSID'] == DSID) & (sig['tau21DDT'] < cut) h_sigs.append(c_temp.hist(sig['m'][msk], bins=bins, weights=sig['weight'][msk], linestyle=1+idx, label=name, display=(icut == plot_cut))) pass # Fill histograms for i,h_sig in enumerate(h_sigs): print " -- Signal %d |" % (i + 1), sign = 0. for bin in range(1, h_bkg.GetXaxis().GetNbins() + 1): s = h_sig.GetBinContent(bin) b = max(h_bkg.GetBinContent(bin), 0.5) if icut == 35: print s,b pass #sign += np.square(s/np.sqrt(b)) sign += 2 * ((s + b) * np.log(1 + s/b) - s) # Slide 29 in [http://www2.warwick.ac.uk/fac/sci/physics/research/epp/events/seminars/cowan_warwick_2011.pdf] -- here using the _square_ of the per-bin significance, in order to add them in quadrature pass print "Significance:", sign sign = np.sqrt(sign) significances[i].append(sign) pass pass # Fix for icut == 35, isig == 2 # @TEMP!!! significances[2][35] = 0.5 * (significances[2][34] + significances[2][36]) c_temp.xlabel("Large-#it{R} jet mass [GeV]") c_temp.logy() c_temp.legend() if args.save: c_temp.save("plots/cutoptimisation_massspectrum.pdf") # Compute significance improvements improvements = [np.array(sign) / sign[-1] for sign in significances] improvements_avg = np.mean(improvements, axis=0) idx_max = np.argmax(improvements_avg) print "Optimal cut: %.2f (avg. improvement: %.3f)" % (cuts[idx_max], improvements_avg[idx_max]) msk = bkg['tau21DDT'] < cuts[idx_max] print "Background efficiency: %.2f%%" % (np.sum(bkg['weight'][msk])/np.sum(bkg['weight']) * 100.) print "Signal efficiencies:" for name, DSID in zip(sig_names, sorted(list(set(sig['DSID'])))): msk_num = (sig['DSID'] == DSID) & (sig['tau21DDT'] < cuts[idx_max]) msk_den = (sig['DSID'] == DSID) print " %s: %.2f%%" % (name, np.sum(sig['weight'][msk_num])/np.sum(sig['weight'][msk_den]) * 100.) pass print "" cut_man = 0.50 print "For manual cut: %.2f (avg. improvement: %.3f)" % (cut_man, float(improvements_avg[np.where(cuts == cut_man)])) msk = bkg['tau21DDT'] < cut_man print "Background efficiency: %.2f%%" % (np.sum(bkg['weight'][msk])/np.sum(bkg['weight']) * 100.) print "Signal efficiencies:" for name, DSID in zip(sig_names, sorted(list(set(sig['DSID'])))): msk_num = (sig['DSID'] == DSID) & (sig['tau21DDT'] < cut_man) msk_den = (sig['DSID'] == DSID) print " %s: %.2f%%" % (name, np.sum(sig['weight'][msk_num])/np.sum(sig['weight'][msk_den]) * 100.) pass # Plot improvements # -------------------------------------------------------------------------- # Create canvas c = ap.canvas(batch=not args.show) bins = np.linspace(axis['tau21DDT'][1], axis['tau21DDT'][2], axis['tau21DDT'][0] + 1, endpoint=True) # Draw histograms msk = bkg['tau21DDT'] < cut h_bkg = c.hist(bkg['tau21DDT'][msk], bins=bins, weights=bkg['weight'][msk], fillcolor=ROOT.kAzure+7, name='Incl. #gamma MC', normalise=True) h_sigs = list() for idx, (DSID, name) in enumerate(zip(sorted(list(set(sig['DSID']))), sig_names)): msk = (sig['DSID'] == DSID) & (sig['tau21DDT'] < cut) h_sigs.append(c.hist(sig['tau21DDT'][msk], bins=bins, weights=sig['weight'][msk], linecolor=ROOT.kRed + idx, label=name, normalise=True)) pass # Overlay o = ap.overlay(c, color=ROOT.kViolet) graphs = list() for idx, impr in enumerate(improvements): graphs.append(o.graph(impr, bins=cuts, display=None)) pass gr = o.graph(improvements_avg, bins=cuts, linecolor=ROOT.kViolet, linewidth=2, markerstyle=0, option='L') o.padding(0.50) o.label("Average significance improvement") # Text c.padding(0.40) c.text(["#sqrt{s} = 13 TeV, L = 36.1 fb^{-1}", "ISR #gamma selection", ], qualifier='Simulation Internal') c.xlabel('Signal jet #tau_{21}^{DDT}') c.ylabel('Events (normalised)') # Lines o.line(cuts[idx_max], 0, cuts[idx_max], improvements_avg[idx_max]) # Legend c.legend(width=0.28) # Show if args.save: c.save('plots/cutoptimisation.pdf') if args.show: c.show() # Write output to file f = ROOT.TFile('output/hists_isrgamma_cutoptimisation.root', 'RECREATE') h_bkg.SetName('h_tau21DDT_bkg') h_bkg.Write() for idx in range(len(h_sigs)): name = sig_names[idx] m = re.search('\(([0-9]+) GeV\)', name) h_sigs[idx].SetName('h_tau21DDT_sig%s' % m.group(1)) h_sigs[idx].Write() graphs[idx].SetName('gr_improvements_sig%s' % m.group(1)) graphs[idx].Write() pass gr.SetName('gr_improvements_avg') gr.Write() f.Write() f.Close() return
def main (): # Parse command-line arguments args = parser.parse_args() # Setup. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Load data files = glob.glob(tf.config['base_path'] + 'objdef_MC_30836*.root') if len(files) == 0: warning("No files found.") return masses = np.array([100, 130, 160, 190, 220], dtype=float) DSIDs = [int(re.search('objdef_MC_(\d{6})\.root', f).group(1)) for f in files] # Plot acceptance, and acc x eff, with uncertainties versus signal mass point # -------------------------------------------------------------------------- acceptance = dict() acceptanceTimesEfficiency = dict() # Number of events relative to which the acceptance is computed base = { 308363: 50000, 308364: 48000, 308365: 50000, 308366: 50000, 308367: 48000, } genfilteff = { 308363: 2.9141e-01, 308364: 1.3795e-01, 308365: 7.5920e-02, 308366: 4.5985e-02, 308367: 2.9914e-02, } categories = dict() # Loop signal mass points for DSID, filename in zip(DSIDs, files): f = ROOT.TFile(filename, 'READ') # Get systematic variation categories f.cd("BoostedJet+ISRgamma") categories[DSID] = [e.GetName() for e in ROOT.gDirectory.GetListOfKeys()] acceptance [DSID] = dict() acceptanceTimesEfficiency[DSID] = dict() # Loop categories for cat in categories[DSID]: precut = f.Get('BoostedJet+ISRgamma/{cat}/EventSelection/Pass/Jet_tau21DDT/Precut' .format(cat=cat)) postcut = f.Get('BoostedJet+ISRgamma/{cat}/EventSelection/Pass/Jet_tau21DDT/Postcut'.format(cat=cat)) acceptance [DSID][cat] = precut .GetEntries() / float(base[DSID]) * genfilteff[DSID] acceptanceTimesEfficiency[DSID][cat] = postcut.GetEntries() / float(base[DSID]) * genfilteff[DSID] pass pass acceptance_nominal = {DSID: acceptance [DSID]['Nominal'] for DSID in DSIDs} acceptanceTimesEfficiency_nominal = {DSID: acceptanceTimesEfficiency[DSID]['Nominal'] for DSID in DSIDs} acceptance_syst = dict() acceptance_stat = dict() acceptanceTimesEfficiency_syst = dict() acceptanceTimesEfficiency_stat = dict() for DSID in DSIDs: # Systematic variation groups groups = set(categories[DSID]) - set(['Nominal']) groups = sorted(list(set([grp.replace('__1up', '').replace('__1down', '') for grp in list(groups)]))) # Acceptance # - - - - - - - - - - - - - - - - - - - - - - - - - - f = acceptance_nominal[DSID] stat = 0 # np.sqrt( f * (1. - f) / base[DSID] ) # Only systematics syst = 0. nom = acceptance_nominal[DSID] for grp in groups: up = acceptance[DSID][grp + '__1up'] down = acceptance[DSID][grp + '__1down'] diff = max(abs(up - nom), abs(down - nom)) syst += np.square(diff) pass syst = np.sqrt(syst) acceptance_stat[DSID] = stat acceptance_syst[DSID] = syst # Acceptance x efficiency # - - - - - - - - - - - - - - - - - - - - - - - - - - f = acceptanceTimesEfficiency_nominal[DSID] stat = 0 # np.sqrt( f * (1. - f) / base[DSID] ) # Only systematics! syst = 0. nom = acceptanceTimesEfficiency_nominal[DSID] for grp in groups: up = acceptanceTimesEfficiency[DSID][grp + '__1up'] down = acceptanceTimesEfficiency[DSID][grp + '__1down'] diff = max(abs(up - nom), abs(down - nom)) syst += np.square(diff) pass syst = np.sqrt(syst) acceptanceTimesEfficiency_stat[DSID] = stat acceptanceTimesEfficiency_syst[DSID] = syst pass # Get total uncertainty acceptance_uncert = np.array([np.sqrt( np.square(acceptance_stat [DSID]) + np.square(acceptance_syst [DSID])) for DSID in DSIDs], dtype=float) acceptanceTimesEfficiency_uncert = np.array([np.sqrt( np.square(acceptanceTimesEfficiency_stat[DSID]) + np.square(acceptanceTimesEfficiency_syst[DSID])) for DSID in DSIDs], dtype=float) # Produce acceptance graphs with errors graph_acceptance = ROOT.TGraphErrors(len(DSIDs), masses, np.array([acceptance_nominal [DSID] for DSID in DSIDs], dtype=float), np.zeros_like(masses), acceptance_uncert) graph_acceptanceTimesEfficiency = ROOT.TGraphErrors(len(DSIDs), masses, np.array([acceptanceTimesEfficiency_nominal[DSID] for DSID in DSIDs], dtype=float), np.zeros_like(masses), acceptanceTimesEfficiency_uncert) # Output histograms for harmonised plotting if args.save: f = ROOT.TFile('output/hists_isrgamma_acceptance.root', 'RECREATE') graph_acceptance.SetName('acc') graph_acceptance.Write() graph_acceptanceTimesEfficiency.SetName('acceff') graph_acceptanceTimesEfficiency.Write() f.Write() f.Close() pass return # Plot jet-level acceptance versus signal mass point # -------------------------------------------------------------------------- counts = dict() labels = list() base_events = dict() for DSID, filename in zip(DSIDs, files): f = ROOT.TFile(filename, 'READ') cutflow = f.Get('BoostedJet+ISRgamma/Nominal/LargeRadiusJets/Nominal/Cutflow') ax = cutflow.GetXaxis() for bin in range(1, ax.GetNbins() + 1): label = ax.GetBinLabel(bin) if label not in counts: counts[label] = dict() if len(labels) < bin: labels.append(label) counts[label][DSID] = cutflow.GetBinContent(bin) pass # Get number of events passing pre-selection cutflow = f.Get('BoostedJet+ISRgamma/Nominal/PreSelection/Nominal/Cutflow') base_events[DSID] = cutflow.GetBinContent(cutflow.GetXaxis().GetNbins()) pass acceptances = {DSID: np.zeros((len(labels),)) for DSID in DSIDs} for idx, label in enumerate(labels): for DSID in DSIDs: acceptances[DSID][idx] = counts[label][DSID] / float(base_events[DSID]) #float(counts[labels[0]][DSID]) pass pass c = ap.canvas(batch=not args.show) names = ["Pre-seleciton", "|#eta| < 2.0", "p_{T} > 200 GeV", "|#Delta#phi(#gamma,J)| > #pi/2", "p_{T} > 2 #times m", "#rho^{DDT} > 1.5"] for idx, (label, name) in enumerate(zip(labels, names)): acceptance_values = np.array([acceptances[DSID][idx] for DSID in DSIDs]) acc = ROOT.TGraphErrors(len(DSIDs), masses, acceptance_values) c.graph(acc, linecolor=colours[idx], markercolor=colours[idx], linewidth=2, option=('A' if idx == 0 else '') + 'PL', label=name, legend_option='PL') pass c.padding(0.5) c.xlabel("Signal mass point [GeV]") c.ylabel("#LTNum. large-#it{R} jets / event#GT") c.text(["#sqrt{s} = 13 TeV", "ISR #gamma channel", "Jet selection"], qualifier="Simulation Internal") c.legend() if args.save: c.save('plots/acceptance_jet.pdf') if args.show: c.show() # Plot event-level acceptance versus signal mass point # -------------------------------------------------------------------------- treenames = ['BoostedJet+ISRgamma/Nominal/PreSelection/Nominal/HLT_g140_loose/Postcut', tf.config['finaltree'].replace('Jet_tau21DDT', 'NumPhotons'), tf.config['finaltree'].replace('Jet_tau21DDT', 'NumLargeRadiusJets'), tf.config['finaltree']] data = {treename: loadData(files, treename, prefix=tf.config['prefix']) for treename in treenames} info = loadData(files, tf.config['outputtree'], stop=1) # Scaling by cross section xsec = loadXsec(tf.config['xsec_file']) # Append new DSID field # @TODO: Make more elegant? for key in data: data[key] = append_fields(data[key], 'DSID', np.zeros((data[key].size,)), dtypes=int) for idx in info['id']: msk = (data[key]['id'] == idx) # Get mask of all 'data' entries with same id, i.e. from same file DSID = info['DSID'][idx] # Get DSID for this file data[key]['weight'][msk] *= xsec[DSID] # Scale by cross section x filter eff. for this DSID data[key]['DSID'] [msk] = DSID # Store DSID pass data[key]['weight'] *= tf.config['lumi'] # Scale all events (MC) by luminosity pass # Check output. if 0 in [data[key].size for key in data]: warning("No data was loaded.") return base_events = np.array([50000., 48000., 50000., 50000., 48000.], dtype=float) acceptances = list() for key in treenames: accepted_events = np.zeros_like(base_events) for idx, DSID in enumerate(DSIDs): accepted_events[idx] = np.sum(data[key]['DSID'] == DSID) pass acceptances.append(accepted_events / base_events) pass print "Mu scaling factors for full MC:" nsb = acceptances[-1] / acceptances[-2] for (mass, factor) in zip(masses, nsb): print "%3d GeV: %.3f" % (mass, factor) pass interpolation_masses = np.linspace(100, 220, (220 - 100) / 5 + 1, endpoint=True) nsb_interpolated = np.exp(np.interp(interpolation_masses, masses, np.log(nsb))) print "Mu scaling factors, logarithmically interpolated:" for m,n in zip(interpolation_masses, nsb_interpolated): print "%d GeV: %.3f" % (m,n) pass acc_interpolated = np.interp(interpolation_masses, masses, acceptances[-2]) print "Signal acceptance up until tau21DDT cut, linearly interpolated:" for m,a in zip(interpolation_masses, acc_interpolated): print "%d GeV: %.3f" % (m,a) pass # Plot: Event-level ecceptance c = ap.canvas(batch=not args.show) names = ["Pre-selection", "= 1 #gamma", "#geq 1 jet", "#tau_{21}^{DDT} < 0.5"] for icut, (name, acceptance) in enumerate(zip(names,acceptances)): acc = ROOT.TGraphErrors(len(DSIDs), masses, acceptances[icut]) c.graph(acc, linecolor=colours[icut], markercolor=colours[icut], linewidth=2, option=('A' if icut == 0 else '') + 'PL', label=name, legend_option='PL') pass c.xlabel("Signal mass point [GeV]") c.ylabel("Acceptance (Z' #rightarrow large-#it{R} jet + #gamma)") c.text(["#sqrt{s} = 13 TeV", "ISR #gamma channel", "Event selection"], qualifier="Simulation Internal") c.legend() if args.save: c.save('plots/acceptance_event.pdf') if args.show: c.show() return
def main(): # Parse command-line arguments args = parser.parse_args() path = "/afs/cern.ch/user/a/asogaard/public/dijetisr/paperfigures/" compcolours = { 'QCD': ROOT.kAzure + 7, 'WHad': ROOT.kRed - 4, 'ZHad': ROOT.kOrange + 4, 'syst': ROOT.kGreen + 1, } qualifier = "" # "Internal" # "Internal" # Setup # -- Limit the number of axis digits to 4 from 5, to reduce clutter ROOT.TGaxis.SetMaxDigits(4) # Tau21 decorrelation plots # -------------------------------------------------------------------------- basedir = { 'isrjet': '/eos/atlas/unpledged/group-wisc/users/lkaplan/dijetISR_all/dijetISR/plotsForNote/outputs/', 'isrgamma': '/afs/cern.ch/user/a/asogaard/public/dijetisr/', } plottypes = ['correlation', 'decorrelation'] for plottype in plottypes: if plottype == 'decorrelation': filenames = { 'isrjet': 'hists_isrjet_decorrelation.root', 'isrgamma': 'hists_isrgamma_decorrelation_tau21DDT_vs_m.root', } elif plottype == 'correlation': filenames = { 'isrjet': 'hists_isrjet_correlation.root', 'isrgamma': 'hists_isrgamma_decorrelation_tau21_vs_rhoDDT.root' } else: assert False pass for key, filename in filenames.iteritems(): # Open file f = ROOT.TFile.Open(basedir[key] + filename) # Get list of profile names names = sorted( list(map(lambda tkey: tkey.GetName(), f.GetListOfKeys()))) # Load profiles profiles = list() for name in names: # Load profie h = f.Get(name) h.SetDirectory(0) # Get pT range m = re.search('.*\_([\d]+)\_([\d]+)\_.*', name) pt = map(int, (m.group(1), m.group(2))) # Get signal status sig = ('sig' in name) profiles.append((h, sig, pt)) pass # Create canvas c = ap.canvas(batch=not args.show) # Plot profiles colors = [ROOT.kBlack, compcolours['WHad'], compcolours['QCD']] for plot_signal in [False, True]: idx = 0 for profile, sig, pt in profiles: # Select desired class if sig != plot_signal: continue # Define style variables color = colors[idx] markerstyle = (24 if plot_signal else 20) # Plot profile c.plot(profile, markerstyle=markerstyle, markersize=1, markercolor=color, linecolor=color, option='PE', legend_option='PE', label="p_{T} #in [%d, %d] GeV" % (pt[0], pt[1])) # Increment counter idx += 1 pass # Draw class-specific header header = "Signal, Z'(160 GeV):" if plot_signal else "Background:" if plottype == 'decorrelation': c.legend(header=header, width=0.25, ymax=0.38, xmin=0.17 + 0.38 * plot_signal) elif plottype == 'correlation': c.legend(header=header, width=0.25, ymax=0.38 + 0.35 * plot_signal, xmin=0.17 + 0.38 * plot_signal) else: assert False pass pass # Re-draw axes on top c.pads()[0]._primitives[0].Draw('AXIS SAME') # Decorations c.ylim(0, 1) if plottype == 'decorrelation': c.xlim(50, 300) c.xlabel("Large-#it{R} jet mass [GeV]") c.ylabel("#LT#tau_{21}^{DDT}#GT") elif plottype == 'correlation': c.xlim(1, 6) c.xlabel("Large-#it{R} jet #rho^{DDT}") c.ylabel("#LT#tau_{21}#GT") else: assert False pass c.text([ "#sqrt{s} = 13 TeV", "{} channel".format("Jet" if key == 'isrjet' else "Photon") ], qualifier="Simulation") # Show/save if args.show: c.show() pass if args.save: c.save('plots/fig2_{}_{}.pdf'.format(plottype, key.replace('isr', ''))) pass pass pass # W/Z plots # -------------------------------------------------------------------------- for isrjet in [True, False]: if isrjet: f = ROOT.TFile(path + "hists_isrjet_WZ.root") else: f = ROOT.TFile(path + "hists_isrgamma_WZ.root") pass names = ['QCD', 'QCD_up', 'QCD_down', 'data', 'WHad', 'ZHad'] histograms = dict() for name in names: hist = f.Get('h_mJ_' + name) hist.SetDirectory(0) # Store histogram histograms[name] = hist pass f.Close() # Combine W/Z component histograms['WZHad'] = histograms['WHad'].Clone( histograms['WHad'].GetName().replace('W', 'WZ')) histograms['WZHad'].Add(histograms['ZHad']) # Compute systematics band histograms['syst'] = histograms['QCD'].Clone("h_mJ_syst") for bin in range(1, histograms['syst'].GetXaxis().GetNbins() + 1): nom = histograms['QCD'].GetBinContent(bin) up = histograms['QCD_up'].GetBinContent(bin) down = histograms['QCD_down'].GetBinContent(bin) histograms['syst'].SetBinError(bin, max(abs(up - nom), abs(nom - down))) pass # -- canvas c = ap.canvas(num_pads=2, fraction=0.45, batch=not args.show) pads = c.pads() # -- main pad h_mc = c.stack(histograms['QCD'], fillcolor=compcolours['QCD'], label='Background est.') h_zhad = c.stack(histograms['ZHad'], fillcolor=compcolours['ZHad'], label='Z + %s' % ('jets' if isrjet else '#gamma')) h_whad = c.stack(histograms['WHad'], fillcolor=compcolours['WHad'], label='W + %s' % ('jets' if isrjet else '#gamma')) h_sum = c.hist(histograms['QCD'], fillstyle=3245, fillcolor=ROOT.kGray + 3, option='E2', label='Bkg. stat. uncert.') """ Using green dashed line to indicate syst error" h_bkg_up = c.hist(histograms['QCD_up'], linestyle=2, linecolor=compcolours['syst'], label='Syst. uncert.') h_bkg_down = c.hist(histograms['QCD_down'], linestyle=2, linecolor=compcolours['syst']) """ h_syst = c.hist(histograms['syst'], fillstyle=1001, alpha=0.30, fillcolor=ROOT.kGray + 3, option='E2', label='Bkg. syst. uncert.') h_data = c.plot(histograms['data'], label='Data', option='PE0X0', legend_option='PE') # -- diff pad pads[1].hist(histograms['WZHad'], fillcolor=compcolours['WHad'], option='HIST') pads[1].hist(histograms['ZHad'], fillcolor=compcolours['ZHad'], option='HIST') #c.diff_plot((h_bkg_up, histograms['QCD']), option='HIST') #c.diff_plot((h_bkg_down, histograms['QCD']), option='HIST') c.diff_plot((histograms['syst'], histograms['QCD']), fillcolor=ROOT.kGray + 3, alpha=0.30, option='E2', uncertainties=False) c.diff_plot((histograms['data'], histograms['QCD']), option='PE0X0') # -- statistical test h_mc.Add(h_zhad) h_mc.Add(h_whad) for bin in range(1, h_mc.GetXaxis().GetNbins()): stat = h_mc.GetBinError(bin) #nom = h_sum .GetBinContent(bin) #up = h_bkg_up .GetBinContent(bin) #down = h_bkg_down.GetBinContent(bin) #syst = max(abs(up-nom), abs(down-nom)) syst = histograms['syst'].GetBinError(bin) h_mc.SetBinError(bin, np.sqrt(np.square(stat) + np.square(syst))) pass chi2 = h_data.Chi2Test(h_mc, "UW CHI2 P") KS = h_data.KolmogorovTest(h_mc) ndf = h_mc.GetXaxis().GetNbins() - 1 # -- decorations c.text([ "#sqrt{s} = 13 TeV, 36.1 fb^{-1}", "W/Z validation", "%s channel" % ('Jet' if isrjet else 'Photon') ], qualifier=qualifier) c.xlabel('Large-#it{R} jet mass [GeV]') c.ylabel('Events / 2 GeV') c.padding(0.47) # (0.45) if isrjet: pads[1].ylim(-1000, 5000) else: pads[1].ylim(-140, 700) pads[1].ylabel('Data - background est.') pads[1].yline(0, linestyle=1, linecolor=ROOT.kBlack) c.region("SR", 0.8 * 85., 1.2 * 85.) c.legend(ymax=0.891) # ..., sort=True) stats_string = "KS prob. = %.3f | #chi^{2}/N_{dof} (prob.) = %.1f/%d (%.3f)" % ( KS, chi2, ndf, ROOT.TMath.Prob(chi2, ndf)) #c.latex(stats_string, 0.95 , 0.96, NDC=True, align=31, textsize=16) if args.save: c.save('plots/paperplot_wz_%s.pdf' % ('jet' if isrjet else 'gamma')) if args.show: c.show() pass # Search plots # -------------------------------------------------------------------------- for isrjet, mass, log in itertools.product([True, False], [140, 150, 160, 220], [True]): if isrjet and mass == 140: continue # Sample not available if isrjet: f = ROOT.TFile(path + "hists_isrjet_%d.root" % mass) else: try: # Try to get file with signal... f = ROOT.TFile( path + "hists_isrgamma_datadistributions_%dGeV_mu100.root" % mass) except: # ... otherwise, default to file without f = ROOT.TFile(path + "hists_isrgamma_datadistributions_%dGeV.root" % mass) pass pass names = ['QCD', 'QCD_up', 'QCD_down', 'data', 'WHad', 'ZHad', 'sig'] def normalise(hist): """Divide histogram by binwidth.""" for bin in range(1, hist.GetXaxis().GetNbins() + 1): hist.SetBinContent( bin, hist.GetBinContent(bin) / float(hist.GetBinWidth(bin))) hist.SetBinError( bin, hist.GetBinError(bin) / float(hist.GetBinWidth(bin))) pass return hist histograms = dict() for name in names: hist = f.Get('h_mJ_' + name) try: hist.SetDirectory(0) if not isrjet: hist = normalise(hist) pass histograms[name] = hist except: # This mass point doesn't have a signal shape warning("Mass point %d doesn't have a signal shape." % mass) # Try to access the interpolated signal interp_path = "/eos/atlas/user/a/asogaard/Analysis/2016/BoostedJetISR/StatsInputs/2017-08-06/" interp_filename = "{}_{:d}.root".format( "sig" if isrjet else "ISRgamma_signal", mass) interp_f = ROOT.TFile(interp_path + interp_filename, "READ") interp_treename = ("Signal_ISRjet_{:d}" if isrjet else "SIGNAL_{:d}_Nominal").format(mass) interp_t = interp_f.Get(interp_treename) array = root_numpy.tree2array(interp_t) fields = list(array.dtype.names) # -- Fill new histogram for interpolated signal bins = tf.config['massbins'] c = ap.canvas(batch=True) warning("Fields : [{}, {}]".format(*fields)) h_interp = c.hist(array[fields[0]], bins=bins, weights=array[fields[1]], display=False) warning( " Setting interpolated histogram as '{}'".format(name)) histograms[name] = normalise(h_interp) pass pass f.Close() # Divide by bins width """ if not isrjet: for hist in histograms.itervalues(): for bin in range(1, hist.GetXaxis().GetNbins() + 1): hist.SetBinContent(bin, hist.GetBinContent(bin) / float(hist.GetBinWidth(bin))) hist.SetBinError (bin, hist.GetBinError (bin) / float(hist.GetBinWidth(bin))) pass pass pass #""" # Combine W/Z components histograms['WZHad'] = histograms['WHad'].Clone( histograms['WHad'].GetName().replace('W', 'WZ')) histograms['WZHad'].Add(histograms['ZHad']) # Add stat. and syst. uncert. in quadrature for bin in range(1, histograms['QCD'].GetXaxis().GetNbins() + 1): stat = histograms['QCD'].GetBinError(bin) nom = histograms['QCD'].GetBinContent(bin) up = histograms['QCD_up'].GetBinContent(bin) down = histograms['QCD_down'].GetBinContent(bin) syst = max(abs(up - nom), abs(down - nom)) histograms['QCD_up'].SetBinContent( bin, nom + np.sqrt(np.square(stat) + np.square(syst))) histograms['QCD_down'].SetBinContent( bin, nom - np.sqrt(np.square(stat) + np.square(syst))) pass # Add W/Z component to background variations histograms['QCD_up'].Add(histograms['WZHad']) histograms['QCD_down'].Add(histograms['WZHad']) # -- canvas c = ap.canvas(num_pads=2, batch=not args.show) pads = c.pads() # -- main pad h_mc = c.stack(histograms['WZHad'], fillcolor=compcolours['WHad'], label='W/Z + %s' % ('jets' if isrjet else '#gamma')) h_qcd = c.stack(histograms['QCD'], fillcolor=compcolours['QCD'], label='Background est.') if 'sig' in histograms: c.hist(histograms['sig'], linestyle=1, linecolor=ROOT.kViolet + 2, label="Z' (%d GeV)" % mass) else: warning("Key 'sig' not in `histograms`") pass h_sum = c.getStackSum() h_sum = c.hist(h_sum, fillstyle=3245, fillcolor=ROOT.kGray + 3, option='E2', label='Bkg. stat. uncert.') """ Using green dashed line to indicate stat + syst error" h_bkg_up = c.hist(histograms['QCD_up'], linestyle=2, linecolor=compcolours['syst'], label='Stat. #oplus syst.') h_bkg_down = c.hist(histograms['QCD_down'], linestyle=2, linecolor=compcolours['syst']) """ h_bkg_var = h_sum.Clone("h_bkg_var") for bin in range(1, h_bkg_var.GetXaxis().GetNbins() + 1): nom = h_sum.GetBinContent(bin) up = histograms['QCD_up'].GetBinContent(bin) down = histograms['QCD_down'].GetBinContent(bin) h_bkg_var.SetBinError(bin, max(abs(up - nom), abs(nom - down))) pass h_bkg_var = c.hist(h_bkg_var, fillstyle=1001, alpha=0.30, fillcolor=ROOT.kGray + 3, option='E2', label='Bkg. stat. #oplus syst.') h_data = c.plot(histograms['data'], label='Data', option='PE0X0', legend_option='PE') # -- diff pad if isrjet: pads[1].ylim(0.98, 1.02) else: pads[1].ylim(0.85, 1.15) pulls_stat_syst = c.ratio_plot((h_bkg_var, h_sum), option='E2') pulls_bkg_stat = c.ratio_plot((h_sum, h_sum), option='E2') pulls_data = c.ratio_plot((histograms['data'], h_sum), option='PE0X0', oob=True) # -- statistical test h_mc.Add(h_qcd) for bin in range(1, h_mc.GetXaxis().GetNbins()): nom = h_mc.GetBinContent(bin) #up = h_bkg_up .GetBinContent(bin) #down = h_bkg_down.GetBinContent(bin) #statPlusSyst = max(abs(up-nom), abs(down-nom)) statPlusSyst = h_bkg_var.GetBinError(bin) # h_bkg_{up,down} are stats. oplus syst. h_mc.SetBinError(bin, statPlusSyst) pass chi2 = h_data.Chi2Test(h_mc, "UW CHI2 P") KS = h_data.KolmogorovTest(h_mc) ndf = h_mc.GetXaxis().GetNbins() - 1 # -- decorations c.text([ "#sqrt{s} = 13 TeV, 36.1 fb^{-1}", "%s channel" % ('Jet' if isrjet else 'Photon') ], qualifier=qualifier) c.xlabel('Large-#it{R} jet mass [GeV]') c.ylabel('Events / GeV') # ... / 5 GeV if isrjet: c.ymin(150. / 5.) else: c.ymin(10. / 5.) pass if log: c.padding(0.42) # 0.45 | 0.50 else: c.padding(0.35) pass pads[1].ylabel('Data / est.') pads[1].yline(1, linestyle=1, linecolor=ROOT.kBlack) c.region("SR", 0.8 * mass, 1.2 * mass, offset=0.07 ) #, offset=(0.14 if (mass == 150 and not isrjet) else 0.07)) c.legend(ymax=0.89) # ..., sort=True) c.log(log) stats_string = "KS prob. = %.3f | #chi^{2}/N_{dof} (prob.) = %.1f/%d (%.3f)" % ( KS, chi2, ndf, ROOT.TMath.Prob(chi2, ndf)) if args.save: c.save('plots/paperplot_%s_%dGeV%s.pdf' % ('jet' if isrjet else 'gamma', mass, '_log' if log else '')) if args.show: c.show() # Temp: Pre-fit pull plots for journal reviewer if mass not in [160, 220]: continue print "=" * 40 print "isrjet: {}, mass: {}".format(isrjet, mass) pulls = list() diffs = list() errs = list() CRs = list() for ibin in range(1, pulls_stat_syst.GetXaxis().GetNbins() + 1): # @TODO: Only in signal region? CRs.append( abs(pulls_stat_syst.GetXaxis().GetBinCenter(ibin) - mass) / mass > 0.2) diff = pulls_data.GetBinContent(ibin) - 1. err_bkg = pulls_stat_syst.GetBinError(ibin) err_data = pulls_data.GetBinError(ibin) err = np.sqrt(err_bkg**2 + err_data**2) pull = diff / np.sqrt(err_bkg**2 + err_data**2) diffs.append(diff) errs.append(err) pulls.append(pull) pass pulls_cr = [pull for cr, pull in zip(CRs, pulls) if cr] print "CR: mean +/- rms (eom) of pulls: {} +/- {} ({})".format( np.mean(pulls_cr), np.std(pulls_cr), np.std(pulls_cr) / np.sqrt(len(pulls_cr))) c1 = ap.canvas(batch=not args.show) bins = np.linspace(-3, 3, 6 * 2 + 1, endpoint=True) h1 = c1.hist(pulls, bins=bins, linecolor=ROOT.kRed, label='All bins') h1_cr = c1.hist(pulls_cr, bins=bins, linecolor=ROOT.kBlue, linestyle=2, label='Only CR bins') print "==== FULL:" h1.Fit('gaus', 'V+') print "\n==== CR:" h1_cr.Fit('gaus', 'V+') c1.xlabel('Pre-fit pulls') c1.ylabel('Number of large-#it{R} jet mass bins') c1.text([ "{} channel".format('Jet' if isrjet else 'Photon'), "Mass point: {} GeV".format(mass) ], qualifier='Internal') c1.legend() c1.legend() c1.save('plots/temp_pulls_{}_{}.pdf'.format( 'jet' if isrjet else 'photon', mass)) #c1.show() pass # tau21DDT cut optimisation # -------------------------------------------------------------------------- mass = 160 for isrjet in [True, False]: # [True, False]: if isrjet: f = ROOT.TFile(path + "hists_isrjet_cutoptimisation.root") else: f = ROOT.TFile(path + "hists_isrgamma_cutoptimisation.root") pass if not isrjet: names = [ 'h_tau21DDT_bkg', 'h_tau21DDT_sig%d' % mass, 'gr_improvements_sig%d' % mass ] else: names = ['h_bkg_plot', 'h_sig_plot', 'Graph'] pass histograms = dict() for name, title in zip(names, ['bkg', 'sig', 'impr']): hist = f.Get(name) try: hist.SetDirectory(0) except: # TGraph pass histograms[title] = hist pass f.Close() # Normalise graph gr = histograms['impr'] N = gr.GetN() base, x, y = 0, ROOT.Double(0), ROOT.Double(-inf) gr.GetPoint(N - 1, x, y) base = float(y) for i in range(N): gr.GetPoint(i, x, y) gr.SetPoint(i, float(x), float(y) / base) pass # -- canvas c = ap.canvas(num_pads=1, batch=not args.show) pads = c.pads() # -- main pad c.hist(histograms['bkg'], fillcolor=compcolours['QCD'], label="Incl. #gamma MC" if not isrjet else "Multijet MC") c.hist(histograms['sig'], linecolor=compcolours['WHad'], label="Z' (%d GeV)" % mass) # -- overlay o = ap.overlay(c, color=ROOT.kViolet) o.graph(histograms['impr'], linecolor=ROOT.kViolet, linewidth=2, option='L') # Get point of maximum improvement gr = histograms['impr'] N = gr.GetN() ymax, xmax, x, y = -inf, 0, ROOT.Double(0), ROOT.Double(-inf) for i in range(N): gr.GetPoint(i, x, y) if float(y) > ymax: ymax = float(y) xmax = float(x) pass pass o.line(xmax, 0, xmax, ymax) # -- decorations c.text([ "#sqrt{s} = 13 TeV, 36.1 fb^{-1}", "%s channel" % ('Jet' if isrjet else 'Photon') ], qualifier="Simulation " + qualifier) c.xlabel("Large-#it{R} jet #tau_{21}^{DDT}") c.ylabel("Fraction of events") o.label("Relative improvement in significance") c.legend(width=0.26) if args.show: c.show() if args.save: c.save('plots/paperplot_%s_%dGeV_cutoptimisation.pdf' % ('jet' if isrjet else 'gamma', mass)) pass # Signal acceptance # -------------------------------------------------------------------------- # Create canvas c = ap.canvas(batch=not args.show) for idx, isrjet in enumerate([False, True]): if isrjet: f = ROOT.TFile(path + "hists_isrjet_sigacc.root") else: f = ROOT.TFile(path + "hists_isrgamma_acceptance.root") pass if isrjet: acc = f.Get('g_ptj') acceff = f.Get('g_tau') else: acc = f.Get('acc') acceff = f.Get('acceff') pass print "" print "-" * 40 print "ISR jet:", isrjet x, y = ROOT.Double(), ROOT.Double() print "Acceptance:" for i in range(acc.GetN()): acc.GetPoint(i, x, y) print " (x,y) = ({},{})".format(x, y) pass print "Acceptance x efficiency:" for i in range(acceff.GetN()): acceff.GetPoint(i, x, y) print " (x,y) = ({},{})".format(x, y) pass # -- Draw graphs opts = { 'linecolor': colours[idx], 'fillcolor': colours_light[idx], 'markercolor': colours[idx], 'linewidth': 2 } c.graph(acc, option=('A' if idx == 0 else '') + '3', label="%s channel" % ('Photon' if not isrjet else 'Jet'), legend_option='L', **opts) c.graph(acc, option='LX', **opts) opts['fillcolor'] = colours[idx] c.graph(acceff, option='3L', fillstyle=3245, **opts) pass c.log() #c.padding(0.4) #c.ymin(1.0E-04) c.ylim(1E-04, 1E+00) c.xlabel("m_{Z'} [GeV]") c.ylabel( "Acceptance, acc. #times efficiency (Z' #rightarrow large-#it{R} jet + #gamma/jet)" ) c.text(["#sqrt{s} = 13 TeV"], qualifier="Simulation " + qualifier) c.legend(categories=[ ("Acceptance", { 'fillcolor': ROOT.kGray, 'option': 'LF' }), ("Acc. #times eff.", { 'fillcolor': ROOT.kGray + 2, 'fillstyle': 3245, 'option': 'LF' }), ], reverse=True) #, ymax=0.84) if args.save: c.save('plots/paperplot_acceptance.pdf') if args.show: c.show() # Tau21 distributions # -------------------------------------------------------------------------- for isrjet, ddt in itertools.product([True, False], ['', 'DDT']): # Create canvas c = ap.canvas(batch=not args.show) if isrjet: f = ROOT.TFile(path + "hists_isrjet_tau21{ddt}slices.root".format( ddt=ddt)) else: f = ROOT.TFile( path + "hists_isrgamma_tau21{ddt}distributions.root".format(ddt=ddt)) pass if isrjet: slices = { 'pt': [ (500, 700), (700, 900), (900, 1100), ], 'm': [ (100, 150), (150, 200), (200, 250), ], } else: slices = { 'pt': [ (200, 300), (300, 500), (500, 1000), ], 'm': [ (100, 150), (150, 200), (200, 250), ], } pass if isrjet: histograms = [ f.Get('h_%d_%d_%d_%d' % (sl['pt'][0], sl['pt'][1], sl['m'][0], sl['m'][1])) for sl in dict_product(slices) ] else: histograms = [ f.Get('h_m_%d_%d_pt_%d_%d' % (sl['m'][0], sl['m'][1], sl['pt'][0], sl['pt'][1])) for sl in dict_product(slices) ] pass category_names = [ "[%.0f, %.0f] GeV" % (slices['m'][i][0], slices['m'][i][1]) for i in range(len(slices['m'])) ] # Draw for i, hist in enumerate(histograms): name = hist.GetName() if isrjet: pt1, pt2 = int(name.split('_')[1]), int(name.split('_')[2]) else: pt1, pt2 = int(name.split('_')[5]), int(name.split('_')[6]) pass #print "--", hist.GetName() label = "[%.0f, %.0f] GeV" % (pt1, pt2) c.hist(hist, fillstyle=0, linecolor=ROOT.kRed + (i % 3) * 2, linestyle=1 + (i // 3), label=label if i < 3 else None, normalise=True, option='HIST') pass # Decorations c.xlabel('Signal jet #tau_{21}^{%s}' % (ddt.upper())) c.ylabel('Fraction of events') c.text([ "#sqrt{s} = 13 TeV", "%s selection" % ('Jet' if isrjet else 'Photon') ], qualifier="Simulation " + qualifier) c.padding(0.50) ymax = 0.735 c.legend(header='Jet p_{T} in:', ymax=ymax) c.legend(header='Jet mass in:', categories=[(category_names[idx], { 'linestyle': 1 + idx }) for idx in range(len(category_names))], ymax=ymax, xmin=0.19) if args.show: c.show() if args.save: c.save('plots/paperplot_%s_tau21%sdistribution.pdf' % ('jet' if isrjet else 'gamma', ddt)) pass return
def main(): # Macro-specific styles ROOT.gROOT.GetStyle("AStyle").SetEndErrorSize(.5) # Parse command-line arguments args = parser.parse_args() ap.canvas(batch=not args.show) # Initialise categories for which to plot distinct curves for each histogram algorithms = ['Standard', 'LargeD0'] names = ['Standard', 'Large radius'] types = ['Signal'] # ['All', 'Signal'] signals = ['Rhadron', 'RPV'] groups = { 'Rhadron': [ 'Rprod_10mm_30mm/', 'Rprod_30mm_100mm/', 'Rprod_100mm_300mm/', ], 'RPV': [ 'Rprod_10mm_30mm/', 'Rprod_30mm_100mm/', 'Rprod_100mm_300mm/', ], } ylabel = "Standard deviation of the error on %s" deps = ['mu', 'pt'] group_names = { signal: [ '[%s]' % grp[6:-1].replace('_', ', ').replace('p', '.').replace( 'mm', ' mm') for grp in groups[signal] ] for signal in signals } # Initialise variable versus which to plot the physics efficiency basic_vars = ['theta', 'phi', 'd0', 'z0', 'qOverP'] # Initialise list of histograms to be plotted base = 'IDPerformanceMon/LargeD0/' histname = base + 'ResolutionPlots/{alg}Tracks/{t}/{group}res{depdim}_{var}_vs_{dep}' # Accessor function to get the y-axis maximum def get_ymax(var): if var == 'theta': return 0.01 * 1 if var == 'phi': return 0.01 * 1 if var == 'd0': return 1.0 * 1 if var == 'z0': return 2.0 * 1 if var == 'qOverP': return 0.05 * 1 return 0.01 def get_ymax_comb(var): if var == 'theta': return 0.008 * 1 if var == 'phi': return 0.007 * 1 if var == 'd0': return 1.2 # 0.8 if var == 'z0': return 1.2 * 1 if var == 'qOverP': return 0.020 return 0.01 # pT-binned RMS profiles # -------------------------------------------------------------------------- histograms = dict() ptgroups = [ 'pT_1GeV_3GeV/', 'pT_10GeV_30GeV/', ] ptgroup_names = [ '[%s]' % grp[3:-1].replace('_', ', ').replace('p', '.').replace('GeV', ' GeV') for grp in ptgroups ] f = ROOT.TFile(filename.format(signal='Rhadron'), 'READ') ROOT.TH2.AddDirectory(False) # Create canvas c = ap.canvas(batch=not args.show, size=(700, 500)) c_temp = ap.canvas(batch=True) # Loop Rprod bins for igroup, (group, groupname) in enumerate( zip(groups['Rhadron'], group_names['Rhadron'])): print group, '(%s)' % groupname # Loop pT bins for ipt, (ptgroup, ptgroupname) in enumerate(zip(ptgroups, ptgroup_names)): print "--", ptgroup, '(%s)' % ptgroup_names hist = None # Loop tracking algorithms; add for alg in algorithms: hn = base + 'ResolutionPlots/{alg}Tracks/Signal/{group}{ptgroup}res_d0_vs_mu'.format( alg=alg, group=group, ptgroup=ptgroup) h = f.Get(hn) h.SetDirectory(0) if hist is None: hist = h.Clone(h.GetName() + '_clone') else: hist.Add(h) pass pass # Define bin edges if igroup < 2: pairs = [ (1, 2), # 5 - 10 (3, 3), # 10 - 15 (4, 4), # 15 - 20 (5, 5), # 20 - 25 (6, 6), # 25 - 30 (7, 8), # 30 - 40 ] else: pairs = [ (1, 3), # 0 - 15 (4, 5), # 15 - 25 (6, 8), # 25 - 40 ] pass # Initialise graph point lists xs, ys, xels, xehs, yes = list(), list(), list(), list(), list() # Loop edge pairs to get projection for ibin, pair in enumerate(pairs): # Get and fit projection proj = hist.ProjectionY('_py', *pair) ax = hist.GetXaxis() # Clean-up (?) -- overflow bins nx = proj.GetXaxis().GetNbins() proj.SetBinContent(0, 0) proj.SetBinContent(nx + 1, 0) # Rebin (?) #if igroup > 0: # #proj.RebinX(2*igroup) # pass # Get graph variables x = 0.5 * (ax.GetBinCenter(pair[0]) + ax.GetBinCenter(pair[1])) wl = (x - ax.GetBinLowEdge(pair[0])) wh = (ax.GetBinUpEdge(pair[1]) - x) # Get parameter RMS and assoc. error ROOT.TH1.StatOverflows(False) # Set number of sigmas to use in core RMS calculation sigma = 3 par, err, fit = getCoreStd(proj, sigma=sigma, fix_mean=0) par2p5, _, _ = getCoreStd(proj, sigma=2.5, fix_mean=0) par2p0, _, _ = getCoreStd(proj, sigma=2.0, fix_mean=0) syst = max(abs(par2p5 - par), abs(par2p0 - par)) print "==> err:", err, "| syst:", syst err = np.sqrt(np.square(err) + np.square(syst)) # Store data point if par > 0.: xs.append(x) ys.append(par) xels.append(wl) xehs.append(wh) yes.append(err) pass # Projection slice c_proj = ap.canvas(batch=not args.show) rms2sig, err2sig, fit2 = getCoreStd(proj, sigma=2, fix_mean=0) rms3sig, err3sig, fit3 = getCoreStd(proj, sigma=3, fix_mean=0) c_proj.hist(proj) c_proj._bare().cd() fit2.SetLineColor(ROOT.kBlue) fit3.SetLineColor(ROOT.kGreen) c_proj.xline(-2 * rms2sig, text='-2#sigma', linecolor=ROOT.kBlue) c_proj.xline(+2 * rms2sig, text='+2#sigma', linecolor=ROOT.kBlue) c_proj.xline(-3 * rms3sig, text='-3#sigma', linecolor=ROOT.kGreen) c_proj.xline(+3 * rms3sig, text='+3#sigma', linecolor=ROOT.kGreen) fit2.Draw('SAME') fit3.Draw('SAME') c_proj.xlim(-8 * rms3sig, +8 * rms3sig) c_proj.xlabel(displayNameUnit('d0')) c_proj.ylabel("Tracks") c_proj.text([ displayName('r') + " #in " + group_names['Rhadron'][igroup], displayName('pt') + " #in " + ptgroup_names[ipt], signal_line('Rhadron') + ' / ' + 'Large radius and standard tracks', ] + [ "%s #in [%.1f, %.1f] %s" % (displayName('mu'), ax.GetBinLowEdge( pair[0]), ax.GetBinUpEdge(pair[1]), displayUnit('mu')) ] + [ "Tracks: %d (%d)" % (proj.Integral(), proj.Integral(0, proj.GetXaxis().GetNbins() + 1)) ], qualifier=qualifier) if args.save: c_proj.save( 'plots/%s_RobustnessResolution_slice__%s_vs_%s__%s_%s_%d_%d.pdf' % ('Signal', 'd0', 'mu', 'Both', ptgroup[:-1], igroup, ibin)) pass # end: loop bins graph = ROOT.TGraphAsymmErrors(len(xs), array('d', xs), array('d', ys), array('d', xels), array('d', xehs), array('d', yes), array('d', yes)) # Create x-axis for graph graph = c_temp.graph(graph) c_temp._bare().cd() ROOT.gPad.Update() # Draw graph with x-axis c.graph(graph, linecolor=colours[igroup], markercolor=colours[igroup], markerstyle=4 * ipt + 20, linewidth=2, linestyle=ipt + 1, label=groupname if ipt == 0 else None) pass pass c.text([signal_line('Rhadron'), "Large radius and standard tracks"], qualifier=qualifier) c.legend(header=displayName('r') + " in:", width=0.28, ymax=0.872) c.legend(header=displayName('pt') + " in:", categories=[(name, { 'linestyle': i + 1, 'markerstyle': 4 * i + 20, 'option': 'PL', 'linewidth': 2 }) for i, name in enumerate(ptgroup_names)], width=0.28, ymax=0.65) c.padding(0.65) c.logy() c.xlim(0, 40) c.xlabel(displayNameUnit('mu')) c.ylabel(ylabel % displayNameUnit('d0')) # Show/save savename = 'Rhadron_ResolutionPlots_BothTracks_Signal_res_d0_vs_mu_pTbinned.pdf' if args.show: c.show() if args.save: c.save('plots/' + savename) # Regular stuff # -------------------------------------------------------------------------- # Loop all combinations of track parameter, truth particle type, signal process, and dependency variable for var, t, dep in itertools.product(basic_vars, types, deps): combined_graphs = {signal: list() for signal in signals} # Loop signal separately, in order to (optionally) compare the two for signal in signals: # Open file from which to read histograms. f = ROOT.TFile(filename.format(signal=signal), 'READ') ROOT.TH1.AddDirectory(False) ROOT.TH2.AddDirectory(False) # Get list of histograms to plot, manually. histograms = list() comb_projs = dict() comb_xs = dict() #comb_ws = dict() comb_wls = dict() comb_whs = dict() # Get histograms bin_pairs = { 'pt': list(), 'mu': list(), } for alg in algorithms: for igroup, group in enumerate(groups[signal]): h = f.Get( histname.format(alg=alg, var=var, t=t, group=group, dep=dep, depdim='2D' if dep == 'pt' else '')) if h is None: pass try: h.SetDirectory( 0) # Keep in memory after file is closed. except AttributeError: print "PROBLEM: '%s'" % histname.format( alg=alg, var=var, t=t, group=group, dep=dep, depdim='2D' if dep == 'pt' else '') continue #xs, ys, xes, yes = list(), list(), list(), list() xs, ys, xels, xehs, yes = list(), list(), list(), list( ), list() # Allocate space if necessary if not (group in comb_projs): comb_projs[group] = list() comb_xs[group] = list() comb_wls[group] = list() comb_whs[group] = list() pass # Dynamically choose binning if dep == 'pt' and len(bin_pairs['pt']) <= igroup: nx, ny = h.GetXaxis().GetNbins(), h.GetYaxis( ).GetNbins() if signal == 'Rhadron': edges = np.logspace(np.log10(1), np.log10(50), 8 - 2 * igroup + 1, endpoint=True) else: edges = np.linspace(0, 400, max(8 - 4 * igroup + 1, 2), endpoint=True) #edges -= 0.2 # @TEMP: FIX FOR NICE BIN EDGES edges[0] = 1 pass bin_pairs['pt'].append(zip(edges[:-1], edges[1:])) ax = h.GetXaxis() bin_pairs['pt'][igroup] = [ (ax.FindBin(pair[0]), ax.FindBin(pair[1])) for pair in bin_pairs['pt'][igroup] ] # Ensure there is no overlap between bins for idx in range(len(bin_pairs['pt'][igroup]) - 1): if bin_pairs['pt'][igroup][idx][1] >= bin_pairs[ 'pt'][igroup][idx + 1][0]: bin_pairs['pt'][igroup][idx + 1] = ( bin_pairs['pt'][igroup][idx][1] + 1, bin_pairs['pt'][igroup][idx + 1][1]) pass pass if signal == 'RPV': bin_pairs['pt'][igroup][-1] = ( bin_pairs['pt'][igroup][-1][0], nx) pass pass if dep == 'mu' and len(bin_pairs['mu']) <= igroup: if igroup < 2: pairs = [ (1, 2), # 5 - 10 (3, 3), # 10 - 15 (4, 4), # 15 - 20 (5, 5), # 20 - 25 (6, 6), # 25 - 30 (7, 8), # 30 - 40 ] else: pairs = [ (1, 3), # 0 - 15 (4, 5), # 15 - 25 (6, 8), # 25 - 40 ] pass bin_pairs['mu'].append(pairs) pass # Loop x-axis bins in 2D histogram for ibin in range(len(bin_pairs[dep][igroup])): pair = bin_pairs[dep][igroup][ibin] ax = h.GetXaxis() # Get and fit projection proj = h.ProjectionY('_py', *pair) # Clean-up (?) if dep == 'mu': # If highest bin content is larger than the average bin content in the remaining bins, remove it nx = proj.GetXaxis().GetNbins() proj.SetBinContent(0, 0) proj.SetBinContent(nx + 1, 0) pass # Rebin (?) if dep == 'mu' and igroup > 0: proj.RebinX(2 * igroup) pass # Get graph variables x = 0.5 * (ax.GetBinCenter(pair[0]) + ax.GetBinCenter(pair[1])) wl = (x - ax.GetBinLowEdge(pair[0])) wh = (ax.GetBinUpEdge(pair[1]) - x) # Add projection to list of combined projections (LRT + STD) if len(comb_projs[group]) > ibin: comb_projs[group][ibin].Add(proj) else: comb_projs[group].append(proj) comb_xs[group].append(x) comb_wls[group].append(wl) comb_whs[group].append(wh) pass # Get parameter RMS and assoc. error ROOT.TH1.StatOverflows(False) # Set number of sigmas to use in core RMS calculation sigma = 3 par, err, fit = getCoreStd(proj, sigma=sigma, fix_mean=0) par2p5, _, _ = getCoreStd(proj, sigma=2.5, fix_mean=0) par2p0, _, _ = getCoreStd(proj, sigma=2.0, fix_mean=0) syst = max(abs(par2p5 - par), abs(par2p0 - par)) if var == 'd0' and dep == 'mu': print "==> err, syst:", err, syst pass err = np.sqrt(np.square(err) + np.square(syst)) # Store data point if par > 0.: xs.append(x) ys.append(par) xels.append(wl / 2.) xehs.append(wh / 2.) yes.append(err) pass # Easy access ax = h.GetXaxis() pair = bin_pairs[dep][igroup][ibin] # Save projections slices ''' c_proj = ap.canvas(batch=not args.show) #rms2sig, err2sig, sigma2_min, sigma2_max = getCoreRMS(proj, sigma=2, fix_mean=0) #rms3sig, err3sig, sigma3_min, sigma3_max = getCoreRMS(proj, sigma=3, fix_mean=0) rms2sig, err2sig, fit2 = getCoreStd(proj, sigma=2, fix_mean=0) rms3sig, err3sig, fit3 = getCoreStd(proj, sigma=3, fix_mean=0) #fit.SetRange(-3*rms3sig,3*rms3sig) c_proj.hist(proj) c_proj._bare().cd() fit2.SetLineColor(ROOT.kBlue) fit3.SetLineColor(ROOT.kGreen) c_proj.xline(-2 * rms2sig, text='-2#sigma', linecolor=ROOT.kBlue) c_proj.xline(+2 * rms2sig, text='+2#sigma', linecolor=ROOT.kBlue) c_proj.xline(-3 * rms3sig, text='-3#sigma', linecolor=ROOT.kGreen) c_proj.xline(+3 * rms3sig, text='+3#sigma', linecolor=ROOT.kGreen) fit2.Draw('SAME') fit3.Draw('SAME') c_proj.xlim(-8*rms3sig, +8*rms3sig) c_proj.xlabel(displayNameUnit(var)) c_proj.ylabel("Tracks") c_proj.text([displayName('r') + " #in " + group_names[signal][igroup], signal_line(signal) + ' / ' + alg, ] + ["%s #in [%.1f, %.1f] %s" % (displayName(dep), ax.GetBinLowEdge(pair[0]), ax.GetBinUpEdge (pair[1]), displayUnit(dep))] + ["Tracks: %d (%d)" % (proj.Integral(), proj.Integral(0, proj.GetXaxis().GetNbins() + 1))], qualifier=qualifier) if args.save: c_proj.save('plots/%s_RobustnessResolution_slice__%s_vs_%s__%s_%d_%d.pdf' % (signal, var, dep, alg, igroup, ibin)) ''' pass # Create profile graph from points if len(xs) > 0: graph = ROOT.TGraphAsymmErrors(len(xs), array('d', xs), array('d', ys), array('d', xels), array('d', xehs), array('d', yes), array('d', yes)) else: graph = ROOT.TGraphErrors() pass histograms.append(graph) pass pass # Close file f.Close() # Profiles for STD and LRT separately # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Draw figure c = ap.canvas(batch=not args.show, size=(700, 500)) c_temp = ap.canvas(batch=True) for i, alg in enumerate(algorithms): N = len(group_names[signal]) N1, N2 = i * N, (i + 1) * N for hist, name, col in zip(histograms[N1:N2], group_names[signal], colours): # Create x-axis for graph hist = c_temp.graph(hist) c_temp._bare().cd() ROOT.gPad.Update() # Draw graph with x-axis c.graph(hist, linecolor=col, markercolor=col, markerstyle=4 * i + 20, linewidth=2, linestyle=i + 1, label=name if i == 0 else None) pass pass c.text([signal_line(signal)] + (["%s particles" % t] if t != 'Signal' else []), qualifier=qualifier) c.legend(header=displayName('r') + " in:", categories=[(name, { 'linestyle': i + 1, 'markerstyle': 4 * i + 20, 'option': 'PL', 'linewidth': 2 }) for i, name in enumerate(names)], width=0.28) c.padding(0.50) c.xlim(h.GetXaxis().GetBinLowEdge(bin_pairs[dep][0][0][0]), h.GetXaxis().GetBinUpEdge(bin_pairs[dep][0][-1][1])) c.xlabel(displayNameUnit(dep)) c.ylabel(ylabel % displayNameUnit(var)) if dep == 'pt': c.logy() pass # Show/save savename = '_'.join([signal] + histname.format( alg='', var=var, t=t, group='', dep=dep, depdim='').split('/') [2:]) + '.pdf' if args.show: c.show() if args.save: c.save('plots/' + savename) # Profile for STD and LRT combined # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Compute combined plots. comb_histograms = list() for igroup, (group, name) in enumerate( zip(groups[signal], group_names[signal]) ): # if signal == 'Rhadron' else groups[:-1]): projs = comb_projs[group] xs = comb_xs[group] wls = comb_wls[group] whs = comb_whs[group] xels = [w for w in wls] xehs = [w for w in whs] ys, yes, _ = zip(*[ getCoreStd(proj, sigma=sigma, fix_mean=0) for proj in projs ]) ys2p5, _, _ = zip(*[ getCoreStd(proj, sigma=2.5, fix_mean=0) for proj in projs ]) ys2p0, _, _ = zip(*[ getCoreStd(proj, sigma=2.0, fix_mean=0) for proj in projs ]) syst = np.maximum(np.abs(np.array(ys2p5) - np.array(ys)), np.abs(np.array(ys2p0) - np.array(ys))) yes = np.sqrt(np.square(np.array(yes)) + np.square(syst)) graph = ROOT.TGraphAsymmErrors(len(xs), array('d', xs), array('d', ys), array('d', xels), array('d', xehs), array('d', yes), array('d', yes)) combined_graphs[signal].append((name, graph)) comb_histograms.append(graph) # Draw projection slice ''' #for ibin, (proj, x, w) in enumerate(zip(projs, xs, ws)): for ibin, (proj, x, wl, wh) in enumerate(zip(projs, xs, wls, whs)): c_proj = ap.canvas(batch=not args.show) #rms2sig, err2sig, sigma2_min, sigma2_max = getCoreRMS(proj, sigma=2, fix_mean=0) #rms3sig, err3sig, sigma3_min, sigma3_max = getCoreRMS(proj, sigma=3, fix_mean=0) rms2sig, err2sig, fit2 = getCoreStd(proj, sigma=2, fix_mean=0) rms3sig, err3sig, fit3 = getCoreStd(proj, sigma=3, fix_mean=0) c_proj.hist(proj) c_proj._bare().cd() #fit.SetRange(-3*rms3sig,3*rms3sig) fit2.SetLineColor(ROOT.kBlue) fit3.SetLineColor(ROOT.kGreen) #c_proj.xline(sigma2_min, text='-2#sigma', linecolor=ROOT.kBlue) #c_proj.xline(sigma2_max, text='+2#sigma', linecolor=ROOT.kBlue) #c_proj.xline(sigma3_min, text='-3#sigma', linecolor=ROOT.kGreen) #c_proj.xline(sigma3_max, text='+3#sigma', linecolor=ROOT.kGreen) c_proj.xline(-2 * rms2sig, text='-2#sigma', linecolor=ROOT.kBlue) c_proj.xline(+2 * rms2sig, text='+2#sigma', linecolor=ROOT.kBlue) c_proj.xline(-3 * rms3sig, text='-3#sigma', linecolor=ROOT.kGreen) c_proj.xline(+3 * rms3sig, text='+3#sigma', linecolor=ROOT.kGreen) fit2.Draw('SAME') fit3.Draw('SAME') c_proj.xlabel(displayNameUnit(var)) c_proj.ylabel("Tracks") c_proj.xlim(-8*rms3sig, +8*rms3sig) c_proj.text([displayName('r') + " #in " + group_names[signal][igroup], signal_line(signal) + ' / Combined LRT and standard', ] + ["%s #in [%.1f, %.1f] %s" % (displayName(dep), #x - w/2., #x + w/2., x - wl, x + wh, displayUnit(dep))] + ["Tracks: %d (%d)" % (proj.Integral(), proj.Integral(0, proj.GetXaxis().GetNbins() + 1))], qualifier=qualifier) if args.save: c_proj.save('plots/%s_RobustnessResolution_slice__%s_vs_%s__%s_%d_%d.pdf' % (signal, var, dep, 'Combined', igroup, ibin)) pass ''' pass # Draw figure c = ap.canvas(batch=not args.show, size=(700, 500)) for ihist, (hist, name, col) in enumerate( zip(comb_histograms, group_names[signal], colours)): c.graph(hist, linecolor=col, markercolor=col, linewidth=2, markerstyle=20 + ihist, linestyle=1 + ihist, label=name) pass c.text([signal_line(signal), "Large radius and standard tracks"] + (["%s particles" % t] if t != 'Signal' else []), qualifier=qualifier) c.legend(header=displayName('r') + " in:", width=0.28) if dep == 'pt': c.padding(0.50) else: c.padding(0.60) pass c.xlim(h.GetXaxis().GetBinLowEdge(bin_pairs[dep][0][0][0]), h.GetXaxis().GetBinUpEdge(bin_pairs[dep][0][-1][1])) c.xlabel(displayNameUnit(dep)) c.ylabel(ylabel % displayNameUnit(var)) if dep == 'pt': c.logy() pass # Show/save savename = '_'.join([signal] + histname.format( alg='Combined', var=var, t=t, group='', dep=dep, depdim='').split('/')[2:]) + '.pdf' if args.show: c.show() if args.save: c.save('plots/' + savename) pass # end: loop signals # Profile for STD and LRT combined for both Rhadron and RPV # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - c = ap.canvas(batch=not args.show, size=(700, 500)) for i, signal in enumerate(reversed(signals)): for (name, graph), col in zip(combined_graphs[signal], colours): c.graph(graph, linecolor=col, markercolor=col, linewidth=2, markerstyle=24 - 4 * i, linestyle=2 - i, label=name if i == 1 else None, legend_option='L') pass pass c.text(["Large radius and standard tracks"], qualifier=qualifier) c.legend(header=displayName('r') + " in:", width=0.28, categories=[(signal_line(signal), { 'linestyle': 2 - i, 'markerstyle': 24 - 4 * i, 'option': 'PL' }) for i, signal in enumerate(reversed(signals))]) if dep == 'pt': c.padding(0.35) else: c.padding(0.45) pass if dep == 'pt': c.xlim(1., 400.) else: c.xlim(0., 40.) pass c.xlabel(displayNameUnit(dep)) c.ylabel(ylabel % displayNameUnit(var)) if dep == 'pt': c.logy() c.logx() pass # Show/save savename = '_'.join(['Both'] + histname.format( alg='Combined', var=var, t=t, group='', dep=dep, depdim='').split( '/')[2:]) + '.pdf' if args.show: c.show() if args.save: c.save('plots/' + savename) pass # end: loop basic_vars, types, deps return
def main (): # Parse command-line arguments args = parser.parse_args() # Setup. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Load data files = glob.glob(tf.config['base_path'] + 'objdef_data_*.root') if len(files) == 0: warning("No files found.") return data = loadData(files, tf.config['tree'], prefix=tf.config['prefix']) info = loadData(files, tf.config['outputtree'], stop=1) # Check output. if data.size == 0: warning("No data was loaded. Exiting.") return # Compute new variables data = append_fields(data, 'logpt', np.log(data['pt'])) # Pass/fail masks msk_pass = tf.config['pass'](data) msk_fail = ~msk_pass # Validating transfer factor fit using toys # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #for mass in [85] + list(np.linspace(100, 250, 15 + 1, endpoint=True)): for mass in list(np.linspace(110, 250, 14 + 1, endpoint=True)): print "-------- MASS: %d GeV" % mass # Set up transfer factor calculator instance calc = tf.calculator(data=data, config=tf.config, verbose=False) # Using default configuration calc.mass = mass calc.window = 0.2 if (args.window is None) else args.window # Get nomnial best-fit theta calc.fit() theta = calc.theta() nominal_weights = calc.weights(data[msk_fail], shift=0), \ calc.weights(data[msk_fail], shift=+1), \ calc.weights(data[msk_fail], shift=-1) # "Throw toys" from TF profile, fit N times calc.toysfit(N=args.N, theta=theta) # Get weights for each toys experiment fit toys_weights = calc.toysweights(data[msk_fail]) # Plot variations bins = tf.config['massbins'] c = ap.canvas(num_pads=2, batch=not args.show) # -- Nominal background(s) hist_nom = c.hist(data[msk_fail]['m'], bins=bins, weights=nominal_weights[0], fillcolor=ROOT.kAzure + 7, label='Nominal bkg.') h_sum = c.hist(hist_nom, fillstyle=3245, fillcolor=ROOT.kGray + 2, linecolor=ROOT.kGray + 3, option='E2', label='Stat. uncert.') # -- Toys backgrounds toys_hists = list() for idx, weights in enumerate(toys_weights): h = c.hist(data[msk_fail]['m'], bins=bins, weights=weights[0], fillstyle=0, linecolor=ROOT.kRed + idx % 5, linestyle = 1 + idx // 5, label='Toys %d' % (idx + 1) if idx < 5 else None) toys_hists.append(h) pass # -- Nominal variations hist_up = c.hist(data[msk_fail]['m'], bins=bins, weights=nominal_weights[1], fillstyle=0, linecolor=ROOT.kGreen, linestyle=2, label='Syst. uncert.') hist_down = c.hist(data[msk_fail]['m'], bins=bins, weights=nominal_weights[2], fillstyle=0, linecolor=ROOT.kGreen, linestyle=2) # -- Data hist_data = c.plot(data[msk_pass]['m'], bins=bins, label='Data') # -- Ratio plots c.ratio_plot((h_sum, hist_nom), option='E2') for idx, h in enumerate(toys_hists): c.ratio_plot((h, hist_nom), option='HIST') pass c.ratio_plot((hist_up, hist_nom), option='HIST') c.ratio_plot((hist_down, hist_nom), option='HIST') c.ratio_plot((hist_data, hist_nom), oob=True) # -- Decorations c.xlabel('Large-#it{R} jet mass [GeV]') c.ylabel('Events / 5 GeV') c.pads()[1].ylabel('Ratio wrt. nominal') c.pads()[1].ylim(0.8, 1.2) c.pads()[1].yline(1.) c.text(["#sqrt{s} = 13 TeV, L = 36.1 fb^{-1}", "Photon channel"], qualifier="Internal") c.region("SR", 0.8 * mass, 1.2*mass) c.legend() c.log() if args.show: c.show() if args.save: c.save('plots/validation_%dGeV_N%d.pdf' % (mass, args.N)) pass return
def main(): # Parse command-line arguments args = parser.parse_args() new_pt = 450 # W/Z MC # -------------------------------------------------------------------------- # Setup. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Load data (W/Z MC) files = glob.glob(tf.config['base_path'] + 'objdef_MC_3054*.root') if len(files) == 0: warning("No files found.") return data = loadData(files, tf.config['finaltree'], prefix=tf.config['prefix']) info = loadData(files, tf.config['outputtree'], stop=1) # Check output. if data.size == 0: warning("No data was loaded. Exiting.") return # Scale by cross-section, generator filter efficiency, and luminosity xsec = loadXsec(tf.config['xsec_file']) data = scale_weights(data, info, xsec) # Show normalised W/Z jet mass peak for different pT thresholds # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - msk = data['pt'] > new_pt # Create canvas c = ap.canvas(batch=not args.show) bins = np.linspace(50, 110, 30 + 1, endpoint=True) # Plot histograms h1 = c.hist(data['m'], bins=bins, weights=data['weight'], normalise=True, linecolor=ROOT.kRed - 4, label='p_{T} > 200 GeV') h2 = c.hist(data['m'][msk], bins=bins, weights=data['weight'][msk], normalise=True, linecolor=ROOT.kAzure + 7, label='p_{T} > %d GeV' % new_pt) # Decorations c.legend() c.xlabel("Large-radius jet mass [GeV]") c.ylabel("Jets / {:.0f} GeV (normalised)".format(np.diff(bins)[0])) c.text([ "#sqrt{s} = 13 TeV, L = %s fb^{-1}" % tf.config['lumi'], "Photon channel", "W/Z + #gamma Monte Carlo", ], qualifier='Internal') # Show/Save if args.show: c.show() if args.save: c.save('plots/masspeak_WZ.pdf') # Signal(220) MC # -------------------------------------------------------------------------- # Setup. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Load data (W/Z MC) files = [ sorted(glob.glob(tf.config['base_path'] + 'objdef_MC_30836*.root'))[-1] ] if len(files) == 0: warning("No files found.") return data = loadData(files, tf.config['finaltree'], prefix=tf.config['prefix']) info = loadData(files, tf.config['outputtree'], stop=1) # Check output. if data.size == 0: warning("No data was loaded. Exiting.") return # Scale by cross-section, generator filter efficiency, and luminosity xsec = loadXsec(tf.config['xsec_file']) data = scale_weights(data, info, xsec) # Get jet channel plot path = '/afs/cern.ch/user/l/lkaplan/public/forAndreas/signals_fatjetunc/sig_220.root' f = ROOT.TFile(path, 'READ') t = f.Get('Signal_ISRjet') data_isrjet = tree2array(t) print data_isrjet, data_isrjet.shape # Show normalised signal jet mass peak for different pT thresholds # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - msk = data['pt'] > new_pt # Create canvas c = ap.canvas(batch=not args.show) bins = np.linspace(50, 300, 25 + 1, endpoint=True) # Plot histograms h0 = c.hist(data_isrjet['mJ'], bins=bins, weights=data_isrjet['weight'], normalise=True, linecolor=ROOT.kRed - 4, label='p_{T} > 450 GeV (jet ch.)') h0 = c.hist(h0, option='E2', fillcolor=ROOT.kRed - 4, alpha=0.3) h1 = c.hist(data['m'], bins=bins, weights=data['weight'], normalise=True, linecolor=ROOT.kAzure + 4, label='p_{T} > 200 GeV (#gamma ch.)') h1 = c.hist(h1, option='E2', fillcolor=ROOT.kAzure + 4, alpha=0.3) h2 = c.hist(data['m'][msk], bins=bins, weights=data['weight'][msk], normalise=True, linecolor=ROOT.kAzure + 7, label='p_{T} > %d GeV (#gamma ch.)' % new_pt) h2 = c.hist(h2, option='E2', fillcolor=ROOT.kAzure + 7, alpha=0.3) # Decorations c.legend(xmin=0.5) c.xlabel("Large-radius jet mass [GeV]") c.ylabel("Jets / {:.0f} GeV (normalised)".format(np.diff(bins)[0])) c.text([ "#sqrt{s} = 13 TeV", "Z'(220 GeV) + #gamma/jet", ], qualifier='Internal') # Show/Save if args.show: c.show() if args.save: c.save('plots/masspeak_sig220.pdf') return
def main(): # Parse command-line arguments args = parser.parse_args() # Input paths #base_path = '/afs/cern.ch/user/a/asogaard/Qualification/validation-rel21-2017-01-24/run/' base_path = '/eos/atlas/user/a/asogaard/Qualification/validation/' paths = [ #base_path + '2017-06-30/output_Rhadron0.root', #base_path + '2017-06-30/output_Rhadron4.root', #base_path + '2017-07-04/output_Rhadron4.root', #base_path + '2017-07-05/output_Rhadron0.root', #base_path + 'output_Rhadron0.root', base_path + '2017-06-30/output_Rhadron.root', base_path + '2017-07-05/output_Rhadron.root', ] # Histograms to be compared histogram_names = [ 'IDPerformanceMon/LargeD0/EffPlots/StandardTracks/Signal/trackeff_vs_R', 'IDPerformanceMon/LargeD0/EffPlots/LargeD0Tracks/Signal/trackeff_vs_R', 'IDPerformanceMon/LargeD0/EffPlots/AllTracks/Signal/trackeff_vs_R', ] # Read in histograms histograms = list() for path in paths: histograms.append(list()) f = ROOT.TFile(path, 'READ') for name in histogram_names: h = f.Get(name) h.SetDirectory(0) h.RebinX(2) histograms[-1].append(h) pass f.Close() pass # Definitions names = ['Standard tracks', 'Large radius tracks', 'Combined'] # Draw figure c = ap.canvas(batch=not args.show, size=(700, 500)) for ihist, (hist, name, col) in enumerate(zip(histograms[0], names, colours)): c.plot(hist, linecolor=col, markercolor=col, linestyle=1, markerstyle=20, option='PE', label=name, legend_option='L') pass for ihist, (hist, name, col) in enumerate(zip(histograms[1], names, colours)): c.plot(hist, linecolor=col, markercolor=col, linestyle=2, markerstyle=24, option='PE') pass c.text([signal_line('Rhadron')], qualifier=qualifier) c.ylim(0, 1.6) c.xlabel(displayNameUnit('r')) c.ylabel("Reconstruction effiency") c.legend(width=0.28, categories=[ ('30/06/2017', { 'linestyle': 1, 'markerstyle': 20, 'option': 'PL' }), ('05/07/2017', { 'linestyle': 2, 'markerstyle': 24, 'option': 'PL' }), ]) # Show/save savename = 'comparison.pdf' if args.show: c.show() if args.save: c.save('plots/' + savename) pass return
def main(): # Parse command-line arguments args = parser.parse_args() # Comparing signal track resolution for LRT and STD tracks # -------------------------------------------------------------------------- # Initialise categories for which to plot distinct curves for each histogram algorithms = ['Standard', 'LargeD0'] names = ['Standard', 'Large radius'] signals = ['RPV', 'Rhadron'] rels = ['res', 'resRel', 'pull'] # Initialise variable versus which to plot the physics efficiency basic_vars = ['theta', 'phi', 'd0', 'z0', 'qOverP'] # Initialise list of histograms to be plotted base = 'IDPerformanceMon/LargeD0/' histname = base + 'ResolutionPlots/{alg}Tracks/Signal/{rel}_{var}' # Loop all combinations of track parameter, resolution type, and signal process. for var, rel, signal in itertools.product(basic_vars, rels, signals): # Open file from which to read histograms. f = ROOT.TFile(filename.format(signal=signal), 'READ') ROOT.TH1.AddDirectory(False) # Get list of histograms to plot histograms = list() for alg in algorithms: h = f.Get(histname.format(alg=alg, var=var, rel=rel)) h.SetDirectory(0) # Keep in memory after file is closed. h.GetXaxis().SetNdivisions(507) h.GetYaxis().SetNdivisions(507) ax = h.GetXaxis() rebin = 1 if signal == 'Rhadron': rebin = 2 pass h.Rebin(rebin) ax.SetRangeUser(ax.GetXmin() / 10., ax.GetXmax() / 10.) histograms.append(h) pass # Close file f.Close() # Draw figure c = ap.canvas(batch=not args.show, size=(700, 500)) for ihist, (hist, name, col) in enumerate(zip(histograms, names, colours)): c.hist( hist, linecolor=col, linewidth=3, linestyle=1 + ihist, normalise=True, option='HIST' ) #, label=name + " tracks", normalise=True, option='HIST E2') c.hist(hist, linecolor=col, fillcolor=col, alpha=0.4, normalise=True, option='E2') pass c.text([signal_line(signal)], qualifier=qualifier) c.legend( width=0.28, categories= [(names[0] + " tracks", { 'linecolor': colours[0], 'linewidth': 3, 'linestyle': 1, 'fillcolor': colours[0], 'alpha': 0.4, 'option': 'FL' }), (names[1] + " tracks", { 'linecolor': colours[1], 'linewidth': 3, 'linestyle': 2, 'fillcolor': colours[1], 'alpha': 0.4, 'option': 'FL' }) #('Statistical uncert.', {'fillcolor': ROOT.kGray, 'linecolor': ROOT.kGray + 1, 'option': 'F'}) ]) c.xlabel("%s^{reco.} - %s^{truth} [%s]" % (displayName(var), displayName(var), displayUnit(var))) c.ylabel("Fraction of tracks") # Show/save savename = '_'.join([signal] + histname.format( alg='Combined', var=var, rel=rel).split('/')[2:]) + '.pdf' if args.show: c.show() if args.save: c.save('plots/' + savename) pass # Binned by matching probability # -------------------------------------------------------------------------- # Initialise categories for which to plot distinct curves for each histogram algorithms = ['Standard', 'LargeD0'] names = ['Standard', 'Large radius'] types = ['All', 'Signal'] signals = ['RPV', 'Rhadron'] rels = ['res', 'resRel', 'pull'] groups = [ 'prob_0p40_0p50/', 'prob_0p50_0p60/', 'prob_0p60_0p70/', 'prob_0p70_0p80/', 'prob_0p80_0p90/', 'prob_0p90_1p00/', ] group_names = [ '[%s]' % grp[5:-1].replace('_', ', ').replace('p', '.') for grp in groups ] # Initialise variable versus which to plot the physics efficiency basic_vars = ['theta', 'phi', 'd0', 'z0', 'qOverP'] # Initialise list of histograms to be plotted base = 'IDPerformanceMon/LargeD0/' histname = base + 'ResolutionPlots/{alg}Tracks/{t}/{group}{rel}_{var}' # Loop all combinations of track parameter, tracking algorithm, truth particle type, resolution type, and signal process for var, (alg, name), t, rel, signal in itertools.product( basic_vars, zip(algorithms, names), types, rels, signals): # Open file from which to read histograms. f = ROOT.TFile(filename.format(signal=signal), 'READ') ROOT.TH1.AddDirectory(False) # Get list of histograms tp plot, manually. histograms = list() # Loop probability bins for igroup, group in enumerate(groups): h = f.Get( histname.format(alg=alg, var=var, t=t, rel=rel, group=group)) h.SetDirectory(0) # Keep in memory after file is closed. h.Rebin(10) # 10 histograms.append(h) pass # Close file f.Close() # Draw figure c = ap.canvas(batch=not args.show, size=(700, 500)) for ihist, (hist, grp, col) in enumerate( zip(histograms, group_names, colours_pretty)): c.hist(hist, linecolor=col, linewidth=3, linestyle=1 + ihist, label=grp, normalise=True) pass c.text([signal_line(signal), name + " tracks"] + (["%s particles" % t] if t != 'Signal' else []), qualifier=qualifier) c.legend(header="Match prob. in:", width=0.28, ymax=0.872) c.xlabel("%s^{reco.} - %s^{truth} [%s]" % (displayName(var), displayName(var), displayUnit(var))) c.ylabel("Fraction of tracks") c.padding(0.40) c.log() # Show/save savename = '_'.join([signal] + histname.format( alg=alg, var=var, t=t, rel=rel, group='').split('/')[2:]) + '.pdf' if args.show: c.show() if args.save: c.save('plots/' + savename) pass return
def main (): # Parse command-line arguments args = parser.parse_args() # Setup. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - TF_DSID = int("100%03d" % (args.mass)) signal_DSID = get_signal_DSID(args.mass) signal = bool(signal_DSID) # Load data files = { 'data': glob.glob(tf.config['base_path'] + 'objdef_data_*.root'), 'bkg': glob.glob(tf.config['base_path'] + 'objdef_TF_%d.root' % TF_DSID), 'gbs': glob.glob(tf.config['base_path'] + 'objdef_GBS_400000.root'), 'sig': glob.glob(tf.config['base_path'] + 'objdef_MC_%d.root' % signal_DSID) if signal else [], 'W': glob.glob(tf.config['base_path'] + 'objdef_MC_30543*.root'), 'Z': glob.glob(tf.config['base_path'] + 'objdef_MC_30544*.root'), 'sfl': glob.glob(tf.config['base_path'] + 'objdef_TF_%d_signalfail.root' % TF_DSID), } #for key, arr in files.iteritems(): # print " -- %3s: %3d" % (key, len(arr)) # pass #if 0 in map(len, [files[key] for key in ['bkg', 'sig', 'W', 'Z']]) and signal: # ..., files.values() if 0 in map(len, files.values()) and signal: # ..., files.values() warning("Files not found for all categories in '%s':" % tf.config['base_path']) for key, arr in files.iteritems(): warning("-- %-4s: %3d" % (key, len(arr))) pass return data = {key: loadData(files[key], tf.config['finaltree'], prefix=tf.config['prefix']) for key in files} data_up = {key: loadData(files[key], tf.config['finaltree'].replace('Nominal', 'TF_UP'), prefix=tf.config['prefix']) for key in ['bkg', 'sfl', 'gbs']} data_down = {key: loadData(files[key], tf.config['finaltree'].replace('Nominal', 'TF_DOWN'), prefix=tf.config['prefix']) for key in ['bkg', 'sfl', 'gbs']} info = {key: loadData(files[key], tf.config['outputtree'], stop=1) for key in files} # Scaling by cross section xsec = loadXsec(tf.config['xsec_file']) # Append new DSID field # @TODO: Make more elegant? # Only needs to be done for 'signal', for 'nominal' syst. variation for comp in ['sig', 'W', 'Z']: if comp == 'sig' and not signal: continue data[comp] = scale_weights(data[comp], info[comp], xsec) #data[comp] = append_fields(data[comp], 'DSID', np.zeros((data[comp].size,)), dtypes=int) #for idx in info[comp]['id']: # msk = (data[comp]['id'] == idx) # Get mask of all 'data' entries with same id, i.e. from same file # DSID = info[comp]['DSID'][idx] # Get DSID for this file # data[comp]['weight'][msk] *= xsec[DSID] # Scale by cross section x filter eff. for this DSID # data[comp]['DSID'] [msk] = DSID # Store DSID # pass # @TODO: k-factors? pass # Failing signal component is scaled by cross section, but not luminosity for comp in ['sig', 'W', 'Z']: # 'sfl' if comp in ['sig', 'sfl'] and not signal: continue data[comp]['weight'] *= tf.config['lumi'] # Scale all events (MC) by luminosity pass # Check output. if data['data'].size == 0: warning("No data was loaded.") return # Plotting # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - for mu, gbs, log in itertools.product([None, 0, 0.5, 1.], [False, True], [False, True]): signal = bool(signal_DSID) and bool(mu is not None) c = ap.canvas(num_pads=2, batch=not args.show) p0, p1 = c.pads() # -- Histograms: Main pad bins = tf.config['massbins'] bkgvar = 'gbs' if gbs else 'bkg' h_bkg = c.hist(data [bkgvar]['m'], bins=bins, weights=data [bkgvar]['weight'], display=False) h_bkg_up = c.hist(data_up [bkgvar]['m'], bins=bins, weights=data_up [bkgvar]['weight'], display=False) h_bkg_down = c.hist(data_down[bkgvar]['m'], bins=bins, weights=data_down[bkgvar]['weight'], display=False) # -- Subtract failing signal component (if any) if signal: h_sig = c.hist(data['sig']['m'], bins=bins, weights=data['sig']['weight'], scale=mu, display=False) pass h_Z = c.stack(data['Z']['m'], bins=bins, weights=data['Z']['weight'], fillcolor=ROOT.kAzure + 3, label="Z + #gamma") h_W = c.stack(data['W']['m'], bins=bins, weights=data['W']['weight'], fillcolor=ROOT.kAzure + 2, label="W + #gamma") # --------------------------------------------------------------------- # Saving output for harmonised paper plots # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if (not gbs) and log: outfile = ROOT.TFile('output/hists_isrgamma_datadistributions_%dGeV%s.root' % (args.mass, '' if mu is None else '_mu%d' % (100 * mu)), 'RECREATE') h_bkg.SetName('h_mJ_QCD') h_bkg_up.SetName('h_mJ_QCD_up') h_bkg_down.SetName('h_mJ_QCD_down') h_data.SetName('h_mJ_data') h_W.SetName('h_mJ_WHad') h_Z.SetName('h_mJ_ZHad') if signal: h_sig.SetName('h_mJ_sig') pass for hist in [h_bkg, h_bkg_up, h_bkg_down, h_data, h_W, h_Z] + ([h_sig] if signal else []): hist.Write() pass outfile.Close() pass # --------------------------------------------------------------------- # -- Subtract failing signal component (if any) if signal: h_sfl = c.hist(data ['sfl']['m'], bins=bins, weights=data ['sfl']['weight'], scale=mu, display=False) h_sfl_up = c.hist(data_up ['sfl']['m'], bins=bins, weights=data_up ['sfl']['weight'], scale=mu, display=False) h_sfl_down = c.hist(data_down['sfl']['m'], bins=bins, weights=data_down['sfl']['weight'], scale=mu, display=False) h_bkg .Add(h_sfl, -1) # Subtracting signal h_bkg_up .Add(h_sfl, -1) # -- h_bkg_down.Add(h_sfl, -1) # -- pass h_bkg = c.stack(h_bkg, fillcolor=ROOT.kAzure + 7, label='Bkg. pred. (GBS)' if gbs else 'Background pred.') h_sum = c.getStackSum() if signal: h_sig = c.stack(h_sig, fillcolor=ROOT.kRed - 4, label="Z' (%d GeV) + #gamma" % args.mass)# (#times #mu)") pass h_sum = c.hist(h_sum, fillstyle=3245, fillcolor=ROOT.kGray + 3, option='E2', label='Stat. uncert.') # Add h_sum errors in quadrature to TF systematics (?) if True: for idx in range(1, h_sum.GetXaxis().GetNbins() + 1): nom = h_bkg.GetBinContent(idx) s = h_sum.GetBinContent(idx) stat = h_sum.GetBinError (idx) syst_up = np.abs(h_bkg_up .GetBinContent(idx) - nom) syst_down = np.abs(h_bkg_down.GetBinContent(idx) - nom) h_bkg_up .SetBinContent(idx, s + np.sqrt(np.square(stat) + np.square(syst_up))) h_bkg_down.SetBinContent(idx, s - np.sqrt(np.square(stat) + np.square(syst_down))) pass pass h_bkg_up = c.hist(h_bkg_up, #linecolor=ROOT.kGreen + 1, linestyle=2, option='HIST', linecolor=ROOT.kGray + 3, option='HIST', display=False) #label='TF syst. uncert.') #label='Stat. #oplus syst.') c.hist(h_sum, linecolor=ROOT.kGray + 3, fillstyle=0, option='HIST', label='Stat. #oplus syst.') c.hist(h_sum, linecolor=ROOT.kBlack, fillstyle=0, option='HIST') h_bkg_down = c.hist(h_bkg_down, #linecolor=ROOT.kGreen + 1, linestyle=2, option='HIST') linecolor=ROOT.kGray + 3, option='HIST', display=False) h_data = c.plot(data['data']['m'], bins=bins, weights=data['data']['weight'], label='Data') # -- Axis limits #c.padding(0.40) # 0.45 p1.ylim(0.8, 1.2) # -- Histograms: Ratio pad if signal: c.ratio_plot((h_sig, h_sum), option='HIST', offset=1) pass c.ratio_plot((h_sum, h_sum), option='E2') c.ratio_plot((h_bkg_up, h_sum), option='HIST') c.ratio_plot((h_bkg_down, h_sum), option='HIST') c.ratio_plot((h_data, h_sum), oob=True) # -- Text c.text(["#sqrt{s} = 13 TeV, L = %s fb^{-1}" % tf.config['lumi'], "Trimmed anti-k_{t}^{R=1.0} jets", "ISR #gamma selection", #"Window: %d GeV #pm %d %%" % (args.mass, 20.), ] + (["Pre-fit: #mu = %.2f" % mu] if signal else []), qualifier='Internal') # -- Axis labels c.xlabel('Signal jet mass [GeV]') c.ylabel('Events') p1.ylabel('Data / Est.') # -- Line(s) p1.yline(1.0) # -- Region labels c.region("SR", 0.8 * args.mass, 1.2 * args.mass) c.log(log) c.legend() if args.show: c.show() if args.save: c.save('plots/datadistribution_%dGeV%s%s%s.pdf' % (args.mass, '_mu%d' % (mu * 100)if signal else '', '_gbs' if gbs else '', '_log' if log else '')) pass return
def main(): # Parse command-line arguments args = parser.parse_args() paths = { 'incl': glob.glob(tf.config['base_path'] + 'objdef_MC_3610*.root'), 'W': glob.glob(tf.config['base_path'] + 'objdef_MC_30543*.root'), 'Z': glob.glob(tf.config['base_path'] + 'objdef_MC_30544*.root'), 'sig': glob.glob(tf.config['base_path'] + 'objdef_MC_308365.root'), } # Load data xsec = loadXsec('../AnalysisTools/share/sampleInfo.csv') treenames = [ 'BoostedJet+ISRgamma/Nominal/LargeRadiusJets/Nominal/dPhiPhoton/Postcut', 'BoostedJet+ISRgamma/Nominal/LargeRadiusJets/Nominal/BoostedRegime/Postcut', 'BoostedJet+ISRgamma/Nominal/LargeRadiusJets/Nominal/rhoDDT/Postcut', ] # Standard definitions # -- Data prefix = '' getvars = ['m', 'pt', 'pt_ungroomed', 'tau21', 'tau21_ungroomed'] # , 'tau21DDT'] xvars = ['rho', 'rhoDDT', 'm'] yvars = ['tau21', 'tau21DDT', 'tau21_ungroomed'] axis = { 'rho': (30, -9, 0), 'rhoDDT': (30, -2, 7), 'm': (30, 0, 300), } # -- Plotting colours = [ROOT.kRed + ((i * 2) % 5) for i in range(3)] qualifier = 'Simulation Internal' lines = [ "#sqrt{s} = 13 TeV, Incl. #gamma MC", "Trimmed anti-k_{t}^{R=1.0} jets", ] xmin, xmax, ymax = 0.57, 0.9, 0.85 - ROOT.gROOT.GetStyle( "AStyle").GetTextSize() * 1.45 # 1.4 # Fitting transform # ---------------------------------------------------------------- data = { key: loadDataFast(paths[key], treenames[1], ['m', 'pt', 'tau21'], prefix, xsec, quiet=True) for key in paths } # Compute new variables for key in data: data[key]['rho'] = np.log( np.square(data[key]['m']) / np.square(data[key]['pt'])) data[key]['rhoDDT'] = np.log( np.square(data[key]['m']) / (data[key]['pt'] * 1.)) pass # Compute profile xvar, yvar = 'rhoDDT', 'tau21' profile_before = get_profile(data['incl'], xvar, yvar, bins=(28, 0, axis[xvar][2]), weights=data['incl']['weight']) fit = ROOT.TF1('fit', 'pol1', 1.5, 7) fit.SetLineColor(ROOT.kGray + 3) fit.SetLineWidth(2) profile_before.Fit('fit', 'RQ0') print "Fit:" print " ", fit.GetParameter(0), "+/-", fit.GetParError(0) print " ", fit.GetParameter(1), "+/-", fit.GetParError(1) for key in data: data[key]['tau21DDT'] = data[key]['tau21'] - ( fit.GetParameter(1) * (data[key]['rhoDDT'] - 1.5)) pass xvar, yvar = 'rhoDDT', 'tau21DDT' profile_after = get_profile(data['incl'], xvar, yvar, bins=(28, 0, axis[xvar][2]), weights=data['incl']['weight']) # Draw c = ap.canvas(batch=not args.show) c.plot(profile_before, linecolor=colours[0], markercolor=colours[0], label='Original, #tau_{21}', option='PE', legend_option='LP') c.plot(profile_after, linecolor=colours[1], markercolor=colours[1], label='Transformed, #tau_{21}^{DDT}', option='PE', legend_option='LP') fit.Draw('SAME') c.legend() c.xlabel('Large-#it{R} jet %s' % displayNameUnit(xvar)) c.ylabel("#LT%s#GT" % displayName('tau21') + ", #LT%s#GT" % displayName(yvar)) c.ylim(0, 1.2) c.text(lines + ["Jet p_{T} > 2 #times M, 200 GeV"], qualifier=qualifier) c.latex("Fit: %.3f + %s #times (%.4f)" % (fit.GetParameter(0), displayName(xvar), fit.GetParameter(1)), 0.20, 0.20, NDC=True, align=11) c.line(1.5, fit.Eval(1.5), axis[xvar][2], fit.Eval(1.5), linecolor=ROOT.kGray + 3, linestyle=2, linewidth=2) c.line(1.5, 0.14, 1.5, fit.Eval(1.5), linecolor=ROOT.kGray, linestyle=1, linewidth=1) c.line(1.5, 0.0, 1.5, 0.05, linecolor=ROOT.kGray, linestyle=1, linewidth=1) savename = 'plots/decorrelation_fit_%s_vs_%s.pdf' % ('tau21', xvar) if args.save: c.save(savename) if args.show: c.show() # Performing pT-slicing # -------------------------------------------------------------------------- # Loop trees (succesive cuts) for itreename, treename in enumerate(treenames): data = { key: loadDataFast(paths[key], treename, getvars, prefix, xsec, quiet=True) for key in paths } # Compute new variables for key in data: data[key]['rho'] = np.log( np.square(data[key]['m']) / np.square(data[key]['pt'])) data[key]['rhoDDT'] = np.log( np.square(data[key]['m']) / (data[key]['pt'] * 1.)) data[key]['tau21DDT'] = data[key]['tau21'] - ( fit.GetParameter(1) * (data[key]['rhoDDT'] - 1.5)) pass # Profile settings slices = { 'pt': [ (200, 300), (300, 500), (500, 1000), ], } # Compute profiles profiles = { key: {xvar: {yvar: [] for yvar in yvars} for xvar in xvars} for key in data.keys() } for key, xvar, yvar in itertools.product(data.keys(), xvars, yvars): if key == 'sig': # Restrict signal to +/- 20% of pole mass msk_sig = np.abs(data[key]['m'] - 160.) / 160. < 0.2 else: msk_sig = np.ones_like(data[key][xvar]).astype(bool) pass for sl in slices['pt']: profile = get_profile(data[key], xvar, yvar, bins=axis[xvar], mask=(data[key]['pt'] > sl[0]) & (data[key]['pt'] < sl[1]) & msk_sig, weights=data[key]['weight']) profiles[key][xvar][yvar].append(profile) pass pass # Comparing groomed and un-groomed # ---------------------------------------------------------------------- # Drawing options names = ['[%d, %d] GeV' % (sl[0], sl[1]) for sl in slices['pt']] # Loop xvars for xvar in xvars: yvar1, yvar2 = 'tau21', 'tau21_ungroomed' c = ap.canvas(num_pads=2, batch=not args.show) # -- main pad for i, (col, name, prof) in enumerate( zip(colours, names, profiles['incl'][xvar][yvar1])): c.plot(prof, linecolor=col, markercolor=col, markerstyle=20, option='PE', label=name, legend_option='LP') pass for i, (col, name, prof) in enumerate( zip(colours, names, profiles['incl'][xvar][yvar2])): c.plot(prof, linecolor=col, markercolor=col, markerstyle=24, option='PE') pass # -- ratio pad for prof1, prof2, col in zip(profiles['incl'][xvar][yvar1], profiles['incl'][xvar][yvar2], colours): r = c.ratio_plot((prof1, prof2), option='HIST ][', linecolor=col, default=0) pass # -- decorations c.xlabel('Large-#it{R} jet %s' % displayNameUnit(xvar)) c.ylabel("#LT%s#GT" % displayName(yvar1) + ", #LT%s#GT" % displayName(yvar2)) c.pads()[1].ylabel("#LT%s#GT" % displayName(yvar1) + " / #LT%s#GT" % displayName(yvar2)) c.ylim(0, 1.4) # if xvar == 'm' else 1.6) c.pads()[1].ylim(0, 1.5) c.legend(header='Jet p_{T} in:', categories=[ ('Trimmed %s' % displayName('tau21'), { 'markerstyle': 20, 'markercolor': ROOT.kGray + 3, 'option': 'P' }), ('Un-trimmed %s' % displayName('tau21'), { 'markerstyle': 24, 'markercolor': ROOT.kGray + 3, 'option': 'P' }), ]) c.text(lines + (["Jet p_{T} > 2 #times M"] if itreename > 0 else []) + (["Jet %s > 1.5" % displayName('rhoDDT')] if itreename > 1 else []), qualifier=qualifier) savename = 'plots/decorrelation_trimmed_untrimmed_%s_vs_%s_tree%d.pdf' % ( yvar1, xvar, itreename) if args.save: c.save(savename) if args.show: c.show() pass # Only groomed # ---------------------------------------------------------------------- # Loop xvars for xvar, yvar in itertools.product(xvars, yvars): c = ap.canvas(batch=not args.show) for i, (col, name, prof) in enumerate( zip(colours, names, profiles['incl'][xvar][yvar])): c.plot(prof, linecolor=col, markercolor=col, markerstyle=20, label=name, option='PE', legend_option='LP') pass c.xlabel('Large-#it{R} jet %s' % displayNameUnit(xvar)) c.ylabel("#LT%s#GT" % displayName(yvar)) c.ylim(0, 1.4) # if xvar == 'm' else 1.6) c.legend(header='Jet p_{T} in:') c.text(lines + (["Jet p_{T} > 2 #times M"] if itreename > 0 else []) + (["Jet %s > 1.5" % displayName('rhoDDT')] if itreename > 1 else []), qualifier=qualifier) savename = 'plots/decorrelation_%s_vs_%s_tree%d.pdf' % (yvar, xvar, itreename) if args.save: c.save(savename) if args.show: c.show() pass # end: loop xvars, yvars # Comparing QCD and W/Z # ---------------------------------------------------------------------- # Drawing options names = ['[%d, %d] GeV' % (sl[0], sl[1]) for sl in slices['pt']] # Loop xvars for xvar, yvar in itertools.product(xvars, yvars): # Save outputs for harmonised plotting if itreename == 2 and yvar.startswith('tau21') and xvar in [ 'm', 'rhoDDT' ]: outfile = ROOT.TFile( 'hists_isrgamma_decorrelation_{}_vs_{}.root'.format( yvar, xvar), 'RECREATE') profname = 'profile_{{name:s}}__{yvar:s}_vs_{xvar:s}__pT_{{ptmin:d}}_{{ptmax:d}}_GeV' profname = profname.format(yvar=yvar, xvar=xvar) print ">" * 80 for prof, sl in zip(profiles['incl'][xvar][yvar], slices['pt']): prof.SetName( profname.format(name='inclphoton', ptmin=sl[0], ptmax=sl[1])) print ">>>", prof.GetName() prof.Write() pass for prof, sl in zip(profiles['sig'][xvar][yvar], slices['pt']): prof.SetName( profname.format(name='sig160', ptmin=sl[0], ptmax=sl[1])) print ">>>", prof.GetName() prof.Write() pass print ">" * 80 outfile.Close() pass c = ap.canvas(batch=not args.show) # -- main pad for i, (col, name, prof) in enumerate( zip(colours, names, profiles['incl'][xvar][yvar])): c.plot(prof, linecolor=col, markercolor=col, markerstyle=20, option='PE', label=name, legend_option='LP') pass for i, (col, name, prof) in enumerate( zip(colours, names, profiles['sig'][xvar][yvar])): c.plot(prof, linecolor=col, markercolor=col, markerstyle=24, option='PE') pass # -- decorations c.xlabel('Large-#it{R} jet %s' % displayNameUnit(xvar)) c.ylabel("#LT%s#GT" % displayName(yvar)) c.ylim(0, 1.4) # if xvar == 'm' else 1.6) c.legend(header='Jet p_{T} in:', categories=[ ("Incl. #gamma MC", { 'markerstyle': 20, 'markercolor': ROOT.kGray + 3, 'option': 'P' }), ("Z'(160 GeV) + #gamma", { 'markerstyle': 24, 'markercolor': ROOT.kGray + 3, 'option': 'P' }), ]) c.text(lines[:1] + [lines[1].replace(', Incl. #gamma', '')] + (["Jet p_{T} > 2 #times M"] if itreename > 0 else []) + (["Jet %s > 1.5" % displayName('rhoDDT')] if itreename > 1 else []), qualifier=qualifier) savename = 'plots/decorrelation_inclphoton_sig_%s_vs_%s_tree%d.pdf' % ( yvar, xvar, itreename) if args.save: c.save(savename) if args.show: c.show() pass pass # end: loop treenames return
def main (): # Parse command-line arguments args = parser.parse_args() # Setup. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Input file paths paths = glob.glob(tf.config['base_path'] + 'objdef_MC_3610*.root') # Load data xsec = loadXsec(tf.config['xsec_file']) # Standard definitions axes = { 'tau21': (20, 0.0, 1.0), #'tau21DDT': (20, 0.0, 1.0), # 0.1, 1.1), } # Getting data data = loadData(paths, tf.config['tree'], prefix=tf.config['prefix']) info = loadData(paths, tf.config['outputtree'], stop=1) data = scale_weights(data, info, xsec, lumi=tf.config['lumi']) # Plotting tau21(DDT) profiles # ---------------------------------------------------------------- slices = { 'pt': [ #( 200, 2000), # For inclusive distribution (test) ( 200, 300), ( 300, 500), ( 500, 1000), ], 'm': [ #(0,1000), # For inclusive distribution (test) #( 50, 100), (100, 150), (150, 200), (200, 250), ], #'rhoDDT': [ # (1.5, 2.5), # (2.5, 4.0), # (4.0, 5.5), # ], } colours = [ROOT.kRed + i for i in np.arange(0,5,2)] # Overwriting keys = slices.keys() key = keys[0] category_names = ["[%.0f, %.0f] %s" % (slices[key][i][0], slices[key][i][1], displayUnit(key)) for i in range(len(slices[key]))] for xvar, axis in axes.iteritems(): print "Plotting %s:" % xvar c = ap.canvas(batch=not args.show) bins = np.linspace(axis[1], axis[2], axis[0] + 1, endpoint=True) if xvar in ['tau21DDT', 'tau21'] and args.save: f = ROOT.TFile('output/hists_isrgamma_%sdistributions.root' % xvar, 'RECREATE') else: f = None pass # Fill sliced histograms histograms = list() for i, sl in enumerate(dict_product(slices)): print " %d:" % i, sl # Create full mask for current slice msk = np.ones_like(data['weight']).astype(bool) for key, limits in sl.iteritems(): msk &= (data[key] >= limits[0]) & (data[key] < limits[1]) pass # Create distribution for current slice key = keys[1] label = "[%.0f, %.0f] %s" % (sl[key][0], sl[key][1], displayUnit(key)) hist = c.hist(data[xvar][msk], bins=bins, weights=data['weight'][msk], linecolor=colours[i % 3], linestyle=1 + (i//3), label=label if i < 3 else None, normalise=True) if f: f.cd() hist.SetName('h_%s_%d_%d_%s_%d_%s' % (keys[0], sl[keys[0]][0], sl[keys[0]][1], keys[1], sl[keys[1]][0], sl[keys[1]][1])) #hist.Write() pass pass if f: f.Write() f.Close() pass # Decorations c.xlabel('Signal jet %s' % displayNameUnit(xvar)) c.ylabel('Jets (a.u.)') c.text(["#sqrt{s} = 13 TeV", "ISR #gamma selection"], qualifier="Simulation Internal") c.padding(0.50) ymax = 0.735 c.legend(header='Jet %s in:' % displayName(keys[1]), ymax=ymax) c.legend(header='Jet %s in:' % displayName(keys[0]), categories=[ (category_names[idx], {'linestyle': 1 + idx}) for idx in range(len(category_names)) ], ymax=ymax, xmin=0.19) if args.show: c.show() if args.save: c.save('plots/tau21distributions_%s__%s_x_%s.pdf' % (xvar, keys[0], keys[1])) pass return
def main (): # Parse command-line arguments args = parser.parse_args() # Setup. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Input file paths paths = glob.glob('/afs/cern.ch/user/a/asogaard/Analysis/2016/BoostedJetISR/AnalysisTools/outputObjdef/objdef_MC_30836*.root') base = 'BoostedJet+ISRgamma/Nominal/' trees = [ 'PreSelection/Nominal/GRL/Postcut', 'PreSelection/Nominal/HLT_g140_loose/Postcut', 'LargeRadiusJets/Nominal/eta/Postcut', 'LargeRadiusJets/Nominal/pt/Postcut', 'LargeRadiusJets/Nominal/dPhiPhoton/Postcut', 'LargeRadiusJets/Nominal/BoostedRegime/Postcut', 'LargeRadiusJets/Nominal/rhoDDT/Postcut', 'EventSelection/Pass/NumPhotons/Postcut', 'EventSelection/Pass/NumLargeRadiusJets/Postcut', 'EventSelection/Pass/Jet_tau21DDT/Postcut' ] trees = [base + tree for tree in trees] names = [ "GRL", "Trigger", "Jet eta", "Jet pT", "Jet dPhi(y)", "Jet pT > 2m", "Jet rhoDDT", "Photon pT", "(Jet count)", "Jet tau21DDT" ] DSIDs = [308363, 308364, 308365, 308366, 308367] basecounts = base = { 308363: 50000, 308364: 48000, 308365: 50000, 308366: 50000, 308367: 48000, } genfilteff = { 308363: 2.9141e-01, 308364: 1.3795e-01, 308365: 7.5920e-02, 308366: 4.5985e-02, 308367: 2.9914e-02, } for idx, DSID in enumerate(DSIDs): print "\nDSID: {}".format(DSID) passingEvents = None for name, tree in zip(names,trees): # Getting data data = loadData([paths[idx]], tree) if passingEvents is None: passingEvents = sorted(list(set(data['eventNumber']))) assert len(passingEvents) == data.shape[0] else: passingEvents = sorted(list(set(passingEvents) & set(data['eventNumber']))) pass print " {:15s}: {:6d} | {:4.1f}% | {:4.1f}%".format( name, len(passingEvents), len(passingEvents) / float(basecounts[DSID]) * 100., len(passingEvents) / float(basecounts[DSID]) * genfilteff[DSID] * 100., ) pass pass return # Plotting tau21(DDT) profiles # ---------------------------------------------------------------- slices = { 'pt': [ #( 200, 2000), # For inclusive distribution (test) ( 200, 300), ( 300, 500), ( 500, 1000), ], 'm': [ #(0,1000), # For inclusive distribution (test) #( 50, 100), (100, 150), (150, 200), (200, 250), ], #'rhoDDT': [ # (1.5, 2.5), # (2.5, 4.0), # (4.0, 5.5), # ], } colours = [ROOT.kRed + i for i in np.arange(0,5,2)] # Overwriting keys = slices.keys() key = keys[0] category_names = ["[%.0f, %.0f] %s" % (slices[key][i][0], slices[key][i][1], displayUnit(key)) for i in range(len(slices[key]))] for xvar, axis in axes.iteritems(): print "Plotting %s:" % xvar c = ap.canvas(batch=not args.show) bins = np.linspace(axis[1], axis[2], axis[0] + 1, endpoint=True) if xvar in ['tau21DDT/Postcut', 'tau21'] and args.save: f = ROOT.TFile('output/hists_isrgamma_%sdistributions.root' % xvar, 'RECREATE') else: f = None pass # Fill sliced histograms histograms = list() for i, sl in enumerate(dict_product(slices)): print " %d:" % i, sl # Create full mask for current slice msk = np.ones_like(data['weight']).astype(bool) for key, limits in sl.iteritems(): msk &= (data[key] >= limits[0]) & (data[key] < limits[1]) pass # Create distribution for current slice key = keys[1] label = "[%.0f, %.0f] %s" % (sl[key][0], sl[key][1], displayUnit(key)) hist = c.hist(data[xvar][msk], bins=bins, weights=data['weight'][msk], linecolor=colours[i % 3], linestyle=1 + (i//3), label=label if i < 3 else None, normalise=True) if f: f.cd() hist.SetName('h_%s_%d_%d_%s_%d_%s' % (keys[0], sl[keys[0]][0], sl[keys[0]][1], keys[1], sl[keys[1]][0], sl[keys[1]][1])) #hist.Write() pass pass if f: f.Write() f.Close() pass # Decorations c.xlabel('Signal jet %s' % displayNameUnit(xvar)) c.ylabel('Jets (a.u.)') c.text(["#sqrt{s} = 13 TeV", "ISR #gamma selection"], qualifier="Simulation Internal") c.padding(0.50) ymax = 0.735 c.legend(header='Jet %s in:' % displayName(keys[1]), ymax=ymax) c.legend(header='Jet %s in:' % displayName(keys[0]), categories=[ (category_names[idx], {'linestyle': 1 + idx}) for idx in range(len(category_names)) ], ymax=ymax, xmin=0.19) if args.show: c.show() if args.save: c.save('plots/tau21distributions_%s__%s_x_%s.pdf' % (xvar, keys[0], keys[1])) pass return
def main(): # Parse command-line arguments args = parser.parse_args() # Setup. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Load data files = glob.glob(tf.config['base_path'] + 'objdef_MC_3610*.root') if len(files) == 0: warning("No files found.") return data = loadData(files, tf.config['tree'], prefix=tf.config['prefix']) info = loadData(files, tf.config['outputtree'], stop=1) # Scaling by cross section xsec = loadXsec(tf.config['xsec_file']) # Append new DSID field # @TODO: Make more elegant? data = append_fields(data, 'DSID', np.zeros((data.size, )), dtypes=int) for idx in info['id']: msk = ( data['id'] == idx ) # Get mask of all 'data' entries with same id, i.e. from same file DSID = info['DSID'][idx] # Get DSID for this file data['weight'][msk] *= xsec[ DSID] # Scale by cross section x filter eff. for this DSID data['DSID'][msk] = DSID # Store DSID pass data['weight'] *= tf.config['lumi'] # Scale all events (MC) by luminosity # Check output. if data.size == 0: warning("No data was loaded.") return # Compute new variables data = append_fields(data, 'logpt', np.log(data['pt'])) # Transfer factor # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Pass/fail masks msk_pass = tf.config['pass'](data) msk_fail = ~msk_pass # Transfer factor calculator instance calc = tf.calculator(data=data, config=tf.config) # Using default configuration calc.mass = args.mass calc.window = args.window # ... calc.partialbins, calc.emptybins, ... calc.fit() # ...(theta=0.5) w_nom = calc.weights(data[msk_fail]) w_up = calc.weights(data[msk_fail], shift=+1) w_down = calc.weights(data[msk_fail], shift=-1) if args.show or args.save: calc.plot(show=args.show, save=args.save, prefix='plots/new_closure_') # Comparing jet mass distrbutions (closure) # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if args.show or args.save: c = ap.canvas(num_pads=2, batch=not args.show) p0, p1 = c.pads() bins = tf.config['massbins'] h_bkg = c.hist(data['m'][msk_fail], bins=bins, weights=data['weight'][msk_fail] * w_nom, display=False) h_up = c.hist(data['m'][msk_fail], bins=bins, weights=data['weight'][msk_fail] * w_up, display=False) h_down = c.hist(data['m'][msk_fail], bins=bins, weights=data['weight'][msk_fail] * w_down, display=False) h_data = c.plot(data['m'][msk_pass], bins=bins, weights=data['weight'][msk_pass], display=False) for bin in range(1, h_bkg.GetXaxis().GetNbins() + 1): width = float(h_bkg.GetBinWidth(bin)) h_bkg.SetBinContent(bin, h_bkg.GetBinContent(bin) / width) h_bkg.SetBinError(bin, h_bkg.GetBinError(bin) / width) h_up.SetBinContent(bin, h_up.GetBinContent(bin) / width) h_up.SetBinError(bin, h_up.GetBinError(bin) / width) h_down.SetBinContent(bin, h_down.GetBinContent(bin) / width) h_down.SetBinError(bin, h_down.GetBinError(bin) / width) h_data.SetBinContent(bin, h_data.GetBinContent(bin) / width) h_data.SetBinError(bin, h_data.GetBinError(bin) / width) pass h_bkg = c.hist(h_bkg, fillcolor=ROOT.kAzure + 7, label='Background est.') h_err = c.hist(h_bkg, fillstyle=3245, fillcolor=ROOT.kGray + 2, linecolor=ROOT.kGray + 3, label='Stat. uncert.', option='E2') h_up = c.hist(h_up, linecolor=ROOT.kGreen + 1, linestyle=2, option='HIST', label='Syst. uncert.') h_down = c.hist(h_down, linecolor=ROOT.kGreen + 1, linestyle=2, option='HIST') h_data = c.plot(h_data, label='Pseudo-data') c.ratio_plot((h_err, h_bkg), option='E2') c.ratio_plot((h_up, h_bkg), option='HIST') c.ratio_plot((h_down, h_bkg), option='HIST') c.ratio_plot((h_data, h_bkg)) c.xlabel('Large-#it{R} jet mass [GeV]') c.ylabel('Events / GeV') p1.ylabel('Data / Est.') c.ylim(1E+00, 1E+06) p1.ylim(0.80, 1.20) p1.yline(1.0) c.region("SR", 0.8 * args.mass, 1.2 * args.mass) #for x in [args.mass * (1 - args.window), args.mass * (1 + args.window)]: # p0.line(x, 1E+01, x, 2E+04) # pass #p1.xlines([args.mass * (1 - args.window), args.mass * (1 + args.window)]) c.text([ "#sqrt{s} = 13 TeV, %s fb^{-1}" % tf.config['lumi'], "Incl. #gamma Monte Carlo", "Photon channel", ], qualifier='Simulation Internal') c.log() c.legend() if args.save: c.save('plots/new_closure_%dGeV_pm%d.pdf' % (args.mass, args.window * 100.)) if args.show: c.show() pass return
def main(): # Parse command-line arguments args = parser.parse_args() # Setup. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Input file paths paths = { 'bkg': glob.glob(tf.config['base_path'] + 'objdef_MC_3610*.root'), 'sig': glob.glob(tf.config['base_path'] + 'objdef_MC_30836*.root'), } sig_names = [ "Z' + #gamma (100 GeV)", "Z' + #gamma (130 GeV)", "Z' + #gamma (160 GeV)", "Z' + #gamma (190 GeV)", "Z' + #gamma (220 GeV)", ] # Load data treename = 'BoostedJet+ISRgamma/Nominal/EventSelection/Pass/NumLargeRadiusJets/Postcut' # Standard definitions # -- Data prefix = 'Jet_' xvars = ['m'] axis = { 'm': (40, 50, 250), 'tau21DDT': (20, 0, 1), } # Getting data bkg = loadData(paths['bkg'], treename, prefix=prefix) sig = loadData(paths['sig'], treename, prefix=prefix) info_bkg = loadData(paths['bkg'], tf.config['outputtree'], stop=1) info_sig = loadData(paths['sig'], tf.config['outputtree'], stop=1) # Check output if bkg.size == 0 or sig.size == 0: warning("No data was loaded.") return # Scaling by cross-section xsec = loadXsec(tf.config['xsec_file']) bkg = scale_weights(bkg, info_bkg, xsec, lumi=tf.config['lumi']) sig = scale_weights(sig, info_sig, xsec, lumi=tf.config['lumi'], verbose=True) # Compute significance improvements # -------------------------------------------------------------------------- # Loop tau21DDT bins cuts = np.linspace(axis['tau21DDT'][1], axis['tau21DDT'][2], 10 * axis['tau21DDT'][0] + 1, endpoint=True) bins = np.linspace(axis['m'][1], axis['m'][2], axis['m'][0] + 1, endpoint=True) significances = [list() for _ in range(len(set(sig['DSID'])))] plot_cut = 36 c_temp = ap.canvas(batch=True) for icut, cut in enumerate(cuts): print "Bin %2d: tau21DDT < %.3f" % (icut, cut) # Create histograms msk = bkg['tau21DDT'] < cut h_bkg = c_temp.hist(bkg['m'][msk], bins=bins, weights=bkg['weight'][msk], fillcolor=ROOT.kAzure + 7, name='Background', display=(icut == plot_cut)) h_sigs = list() for idx, (DSID, name) in enumerate( zip(sorted(list(set(sig['DSID']))), sig_names)): msk = (sig['DSID'] == DSID) & (sig['tau21DDT'] < cut) h_sigs.append( c_temp.hist(sig['m'][msk], bins=bins, weights=sig['weight'][msk], linestyle=1 + idx, label=name, display=(icut == plot_cut))) pass # Fill histograms for i, h_sig in enumerate(h_sigs): print " -- Signal %d |" % (i + 1), sign = 0. for bin in range(1, h_bkg.GetXaxis().GetNbins() + 1): s = h_sig.GetBinContent(bin) b = max(h_bkg.GetBinContent(bin), 0.5) if icut == 35: print s, b pass #sign += np.square(s/np.sqrt(b)) sign += 2 * ( (s + b) * np.log(1 + s / b) - s ) # Slide 29 in [http://www2.warwick.ac.uk/fac/sci/physics/research/epp/events/seminars/cowan_warwick_2011.pdf] -- here using the _square_ of the per-bin significance, in order to add them in quadrature pass print "Significance:", sign sign = np.sqrt(sign) significances[i].append(sign) pass pass # Fix for icut == 35, isig == 2 # @TEMP!!! significances[2][35] = 0.5 * (significances[2][34] + significances[2][36]) c_temp.xlabel("Large-#it{R} jet mass [GeV]") c_temp.logy() c_temp.legend() if args.save: c_temp.save("plots/cutoptimisation_massspectrum.pdf") # Compute significance improvements improvements = [np.array(sign) / sign[-1] for sign in significances] improvements_avg = np.mean(improvements, axis=0) idx_max = np.argmax(improvements_avg) print "Optimal cut: %.2f (avg. improvement: %.3f)" % ( cuts[idx_max], improvements_avg[idx_max]) msk = bkg['tau21DDT'] < cuts[idx_max] print "Background efficiency: %.2f%%" % (np.sum(bkg['weight'][msk]) / np.sum(bkg['weight']) * 100.) print "Signal efficiencies:" for name, DSID in zip(sig_names, sorted(list(set(sig['DSID'])))): msk_num = (sig['DSID'] == DSID) & (sig['tau21DDT'] < cuts[idx_max]) msk_den = (sig['DSID'] == DSID) print " %s: %.2f%%" % (name, np.sum(sig['weight'][msk_num]) / np.sum(sig['weight'][msk_den]) * 100.) pass print "" cut_man = 0.50 print "For manual cut: %.2f (avg. improvement: %.3f)" % ( cut_man, float(improvements_avg[np.where(cuts == cut_man)])) msk = bkg['tau21DDT'] < cut_man print "Background efficiency: %.2f%%" % (np.sum(bkg['weight'][msk]) / np.sum(bkg['weight']) * 100.) print "Signal efficiencies:" for name, DSID in zip(sig_names, sorted(list(set(sig['DSID'])))): msk_num = (sig['DSID'] == DSID) & (sig['tau21DDT'] < cut_man) msk_den = (sig['DSID'] == DSID) print " %s: %.2f%%" % (name, np.sum(sig['weight'][msk_num]) / np.sum(sig['weight'][msk_den]) * 100.) pass # Plot improvements # -------------------------------------------------------------------------- # Create canvas c = ap.canvas(batch=not args.show) bins = np.linspace(axis['tau21DDT'][1], axis['tau21DDT'][2], axis['tau21DDT'][0] + 1, endpoint=True) # Draw histograms msk = bkg['tau21DDT'] < cut h_bkg = c.hist(bkg['tau21DDT'][msk], bins=bins, weights=bkg['weight'][msk], fillcolor=ROOT.kAzure + 7, name='Incl. #gamma MC', normalise=True) h_sigs = list() for idx, (DSID, name) in enumerate(zip(sorted(list(set(sig['DSID']))), sig_names)): msk = (sig['DSID'] == DSID) & (sig['tau21DDT'] < cut) h_sigs.append( c.hist(sig['tau21DDT'][msk], bins=bins, weights=sig['weight'][msk], linecolor=ROOT.kRed + idx, label=name, normalise=True)) pass # Overlay o = ap.overlay(c, color=ROOT.kViolet) graphs = list() for idx, impr in enumerate(improvements): graphs.append(o.graph(impr, bins=cuts, display=None)) pass gr = o.graph(improvements_avg, bins=cuts, linecolor=ROOT.kViolet, linewidth=2, markerstyle=0, option='L') o.padding(0.50) o.label("Average significance improvement") # Text c.padding(0.40) c.text([ "#sqrt{s} = 13 TeV, L = 36.1 fb^{-1}", "ISR #gamma selection", ], qualifier='Simulation Internal') c.xlabel('Signal jet #tau_{21}^{DDT}') c.ylabel('Events (normalised)') # Lines o.line(cuts[idx_max], 0, cuts[idx_max], improvements_avg[idx_max]) # Legend c.legend(width=0.28) # Show if args.save: c.save('plots/cutoptimisation.pdf') if args.show: c.show() # Write output to file f = ROOT.TFile('output/hists_isrgamma_cutoptimisation.root', 'RECREATE') h_bkg.SetName('h_tau21DDT_bkg') h_bkg.Write() for idx in range(len(h_sigs)): name = sig_names[idx] m = re.search('\(([0-9]+) GeV\)', name) h_sigs[idx].SetName('h_tau21DDT_sig%s' % m.group(1)) h_sigs[idx].Write() graphs[idx].SetName('gr_improvements_sig%s' % m.group(1)) graphs[idx].Write() pass gr.SetName('gr_improvements_avg') gr.Write() f.Write() f.Close() return
def main (): # Parse command-line arguments args = parser.parse_args() # Setup. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Get files if not args.isrjet: files = glob.glob(tf.config['base_path'] + 'objdef_MC_30836*.root') else: #files = glob.glob('/afs/cern.ch/user/l/lkaplan/public/forAndreas/signals/sig_*.root') files = glob.glob('/afs/cern.ch/user/l/lkaplan/public/forAndreas/signals_fatjetunc/sig_*.root') pass if len(files) == 0: warning("No files found.") return # Get names of all available systematic variations f = ROOT.TFile(files[0], 'READ') if not args.isrjet: f.cd("BoostedJet+ISRgamma") pass variations = [key.GetName() for key in ROOT.gDirectory.GetListOfKeys()] f.Close() colours = [ROOT.kViolet + 7, ROOT.kAzure + 7, ROOT.kTeal, ROOT.kSpring - 2, ROOT.kOrange - 3, ROOT.kPink] # Load data if not args.isrjet: data = {var: loadData(files, tf.config['finaltree'] .replace('Nominal', var), prefix=tf.config['prefix']) for var in variations} info = {var: loadData(files, tf.config['outputtree'].replace('Nominal', var), stop=1) for var in variations} # Scaling by cross section xsec = loadXsec(tf.config['xsec_file']) # Append new DSID field # @TODO: Make more elegant? for var in variations: data[var] = append_fields(data[var], 'DSID', np.zeros((data[var].size,)), dtypes=int) for idx in info[var]['id']: msk = (data[var]['id'] == idx) # Get mask of all 'data' entries with same id, i.e. from same file DSID = info[var]['DSID'][idx] # Get DSID for this file data[var]['weight'][msk] *= xsec[DSID] # Scale by cross section x filter eff. for this DSID data[var]['DSID'] [msk] = DSID # Store DSID pass data[var]['weight'] *= tf.config['lumi'] # Scale all events (MC) by luminosity pass else: data = {var: loadData(files, var) for var in variations} for var in variations: data[var] = append_fields(data[var], 'DSID', np.zeros((data[var].size,)), dtypes=int) for idx in list(set(data[var]['id'])): msk = (data[var]['id'] == idx) # Get mask of all 'data' entries with same id, i.e. from same file data[var]['DSID'][msk] = idx pass pass pass # Check output. if not args.isrjet and data['Nominal'].size == 0: warning("No data was loaded.") return nominal = 'Nominal' if not args.isrjet else 'Signal_ISRjet' m = 'm' if not args.isrjet else 'mJ' # Perform interpolation of signal mass peaks # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - closure = True # Store mass points if closure: massPoints = np.array([100, 130, 190, 220]) else: massPoints = np.array([100, 130, 160, 190, 220]) pass massVec = ROOT.TVectorD(len(massPoints)) for i in range(len(massPoints)): massVec[i] = massPoints[i] pass # Prepare workspaces workspace = ROOT.RooWorkspace() # Create variables mZ = workspace.factory('mZ[50,300]') # this is our continuous interpolation parameter mJ = workspace.factory('mJ[50,300]') mJ.setBins(50) frame = mJ.frame() # Create fit model for i in range(len(massPoints)): workspace.factory('Gaussian::signal{i}(mJ, mu_sig_{i}[{mean},0,300], sigma_sig_{i}[{width},0,50])'.format(i=i, mean=massPoints[i], width=massPoints[i]*0.1)) if not args.isrjet: workspace.factory('Gaussian::background{i}(mJ, mu_bkg_{i}[0,0,0], sigma_bkg_{i}[100,50,500])'.format(i=i)) else: workspace.factory('Exponential::background{i}(mJ, tau_bkg_{i}[-0.01,-100,0])'.format(i=i)) pass workspace.factory('SUM::model{i}(norm_sig_{i}[2,0,10]*signal{i}, norm_bkg_{i}[1,1,1]*background{i})'.format(i=i)) pass # Create p.d.f.s c = ap.canvas(batch=not args.show) bins = np.linspace(50, 300, 50 + 1, endpoint=True) pdfs = ROOT.RooArgList(*[workspace.pdf('model{i}'.format(i=i)) for i in range(len(massPoints))]) integrals = np.zeros((len(massPoints),), dtype=float) events = np.zeros((len(massPoints),), dtype=float) #for idx in range(len(massPoints)): for idx, mass in enumerate(massPoints): #DSID = idx + (308363 if not args.isrjet else 0) DSID = (mass - 100) / 30 + (308363 if not args.isrjet else 0) print "==> %d" % DSID # Create data histogram for fitting msk = (data[nominal]['DSID'] == DSID) hist = c.hist(data[nominal][m][msk], bins=bins, weights=data[nominal]['weight'][msk], normalise=True, display=False) integrals[idx] = np.sum(data[nominal]['weight'][msk]) events [idx] = np.sum(msk) # Add minimal error to empty bins emax = np.finfo(float).max for bin in range(1, hist.GetXaxis().GetNbins() + 1): if hist.GetBinError(bin) == 0: hist.SetBinError(bin, emax) pass # Create RooFit histogram and p.d.f. rdh = ROOT.RooDataHist('rdh', 'rdh', ROOT.RooArgList(mJ), hist) rhp = ROOT.RooHistPdf ('rhp', 'rhp', ROOT.RooArgSet (mJ), rdh) # Fit data with model pdfs[idx].chi2FitTo(rdh, ROOT.RooLinkedList()) # Plot data histogram and fit rhp .plotOn(frame, ROOT.RooFit.LineColor(colours[idx]), ROOT.RooFit.LineStyle(1), ROOT.RooFit.LineWidth(3)) pdfs[idx].plotOn(frame, ROOT.RooFit.LineColor(ROOT.kBlack), ROOT.RooFit.LineStyle(2)) pass setting = ROOT.RooMomentMorph.Linear morph = ROOT.RooMomentMorph('morph', 'morph', mZ, ROOT.RooArgList(mJ), pdfs, massVec, setting) getattr(workspace,'import')(morph) # work around for morph = w.import(morph) morph.Print('v') # Make plots of interpolated p.d.f.s #interpolationMassPoints = np.linspace(100, 220, (220 - 100) / 10 + 1, endpoint=True) interpolationMassPoints = np.linspace(100, 220, (220 - 100) / 5 + 1, endpoint=True) interpolationMassPoints = sorted(list(set(interpolationMassPoints) - set(massPoints))) """ @TEMP: BEGIN """ # -- Interpolate logarithmically between integrals interpolationIntegrals = np.exp(np.interp(interpolationMassPoints, massPoints, np.log(integrals))) # -- Interpolate linearly between event counts interpolationEvents = np.interp(interpolationMassPoints, massPoints, events).astype(int) """ @TEMP: END """ #for i, (n, integral, mass) in enumerate(zip(interpolationEvents, interpolationIntegrals, interpolationMassPoints)): for i, mass in enumerate(interpolationMassPoints): print "=" * 80 mZ.setVal(mass) mZ.Print() morph.Print() morph.plotOn(frame, ROOT.RooFit.LineColor(ROOT.kRed - 4), ROOT.RooFit.LineStyle(2), ROOT.RooFit.LineWidth(1)) pass # Interpolation closure if closure: # Get mass and DSID idx = 2 mass = 100 + idx * 30 DSID = idx + (308363 if not args.isrjet else 0) print "Performing closure test with %d" % DSID # Create data histogram for fitting msk = (data[nominal]['DSID'] == DSID) hist = c.hist(data[nominal][m][msk], bins=bins, weights=data[nominal]['weight'][msk], normalise=True, display=False) # Add minimal error to empty bins emax = np.finfo(float).max for bin in range(1, hist.GetXaxis().GetNbins() + 1): if hist.GetBinError(bin) == 0: hist.SetBinError(bin, emax) pass # Create RooFit histogram and p.d.f. rdh = ROOT.RooDataHist('rdh', 'rdh', ROOT.RooArgList(mJ), hist) rhp = ROOT.RooHistPdf ('rhp', 'rhp', ROOT.RooArgSet (mJ), rdh) # Plot data histogram and fit rhp .plotOn(frame, ROOT.RooFit.LineColor(1), ROOT.RooFit.LineStyle(1)) # Plot morphed distribution mZ.setVal(mass) morph.plotOn(frame, ROOT.RooFit.LineColor(ROOT.kBlue - 4), ROOT.RooFit.LineStyle(2), ROOT.RooFit.LineWidth(3)) # Get RooFit chi2 value workspace.factory('Gaussian::signal{i}(mJ, mu_sig_{i}[{mean},0,300], sigma_sig_{i}[{width},0,50])'.format(i=4, mean=mass, width=mass*0.1)) if not args.isrjet: workspace.factory('Gaussian::background{i}(mJ, mu_bkg_{i}[0,0,0], sigma_bkg_{i}[100,50,500])'.format(i=4)) else: workspace.factory('Exponential::background{i}(mJ, tau_bkg_{i}[-0.01,-100,0])'.format(i=4)) pass workspace.factory('SUM::model{i}(norm_sig_{i}[2,0,10]*signal{i}, norm_bkg_{i}[1,1,1]*background{i})'.format(i=4)) interp_pdf = workspace.pdf('model{i}'.format(i=4)) interp_pdf.chi2FitTo(rdh, ROOT.RooLinkedList()) # Compute chi2 for data histogram vs. interpolated one h_data = rhp .createHistogram("h_data", mJ) h_est = morph .createHistogram("h_est", mJ) h_fit = interp_pdf.createHistogram("h_fit", mJ) h_fit.Scale(1./h_fit.Integral()) chi2 = 0. for bin in range(1, h_est.GetXaxis().GetNbins() + 1): o = h_fit.GetBinContent(bin) # Fitted p = h_est.GetBinContent(bin) # Interpolated chi2 += np.square(o - p) / o pass N = np.sum(msk) chi2 *= N ndf = h_est.GetXaxis().GetNbins() - 1 prob = ROOT.TMath.Prob(chi2, ndf) print "Chi2 = %.2f | Ndf = %d | p = %.3f" % (chi2, ndf, prob) #for h in [h_data, h_est, h_fit]: # print ">> %.3f" % h.Integral() # pass #hist.Scale(h_est.Integral()/hist.Integral()) #h_est.Scale(hist.Integral()/h_est.Integral()) #h_fit.Scale(hist.Integral()/h_fit.Integral()) #for bin in range(1, h_est.GetXaxis().GetNbins() + 1): # h_est.SetBinError(bin, 0) # pass h_fit.Scale(N) h_est.Scale(N) for bin in range(1, h_est.GetXaxis().GetNbins() + 1): h_fit.SetBinError(bin, np.sqrt(h_fit.GetBinContent(bin))) h_est.SetBinError(bin, np.sqrt(h_est.GetBinContent(bin))) pass print "Chi2 prob. = %.3f" % h_fit.Chi2Test(h_est, "WW P") print "KS prob. = %.3e / %.3e" % (h_fit.KolmogorovTest(h_est), h_est.KolmogorovTest(h_fit)) c = ap.canvas() c.hist(h_data, label='Data', linecolor=ROOT.kBlack) c.hist(h_est, label='Interpolation', linecolor=ROOT.kRed, linestyle=2) c.hist(h_fit, label='Fit', linecolor=ROOT.kBlue, linestyle=2) c.legend() c.show() return chi2var = ROOT.RooChi2Var("chi2", "chi2", morph, rdh) chi2 = chi2var.getVal() print "(1) CHI2:", chi2 chi2var = ROOT.RooChi2Var("chi2", "chi2", morph, interp_pdf) chi2 = chi2var.getVal() print "(2) CHI2:", chi2 #for bin in range(1, hist.GetXaxis().GetNbins() + 1): # print "-- %.1f :%.5f vs. %.5f / %.3f" % (hist.GetBinCenter(bin), hist.GetBinContent(bin), h_est.GetBinContent(bin), hist.GetBinContent(bin) / h_est.GetBinContent(bin)) # pass #hist.scale(1./hist.Integral()) ''' chi2 = 0. N = np.sum(msk) print "N = %.1f" % N O = 0 P = 0 for bin in range(1, hist.GetXaxis().GetNbins() + 1): mJ.setVal(hist.GetXaxis().GetBinCenter(bin)) o = rhp .getVal(ROOT.RooArgSet(mJ)) p = morph.getVal(ROOT.RooArgSet(mJ)) #print " morph(%.1f) = %.5f vs. %.5f / %.3f" % (mJ.getVal(), o, p, o/p) chi2 += np.square(o * 5. - p * 5.) / (p * 5.) O += o P += p pass chi2 *= N ndf = hist.GetXaxis().GetNbins() - 1 prob = ROOT.TMath.Prob(chi2, ndf) #print "O =", O #print "P =", P #print "Chi2 = %.2f | Ndf = %d | p = %.3f" % (chi2, ndf, prob) ''' #for massval in np.linspace(135, 185, 9, endpoint=True): # mZ.setVal(massval) # morph.plotOn(frame, ROOT.RooFit.LineColor(ROOT.kBlue), ROOT.RooFit.LineStyle(2), ROOT.RooFit.LineWidth(3 if massval == 160 else 1)) # pass # Compute chi2 value # ... pass # Draw frame if args.show or args.save: c = ap.canvas(batch=not args.show) c.pads()[0]._bare().cd() frame.Draw() frame.GetXaxis().SetTitle("Large-#it{R} jet mass [GeV]") frame.GetYaxis().SetTitle("Signal p.d.f.") frame.GetYaxis().SetRangeUser(0, 0.22) c.text(["#sqrt{s} = 13 TeV", "ISR %s channel" % ('#gamma' if not args.isrjet else 'jet')], qualifier="Simulation Internal") c.legend(header="Signal MC shape:", categories= [ ("Z' (%d GeV)" % mass, {'linecolor': colours[idx], 'linewidth': 3, 'option': 'L'}) for idx, mass in enumerate(massPoints)] + [ ("Fitted shape", {'linecolor': ROOT.kBlack, 'linestyle': 2, 'linewidth': 2, 'option': 'L'}), ("Interpolated shape", {'linecolor': ROOT.kRed - 4, 'linestyle': 2, 'linewidth': 1, 'option': 'L'}), ]) c.ylim(0, 0.22) if args.save: c.save('plots/interpolation_frame_isr%s.pdf' % ('gamma' if not args.isrjet else 'jet')) if args.show: c.show() pass # Write outputs if args.save: check_make_dir('output') # -- Interpolate logarithmically between integrals interpolationIntegrals = np.exp(np.interp(interpolationMassPoints, massPoints, np.log(integrals))) # -- Interpolate linearly between event counts interpolationEvents = np.interp(interpolationMassPoints, massPoints, events).astype(int) mult = 100 for n, integral, mass in zip(mult * interpolationEvents, interpolationIntegrals, interpolationMassPoints): # Generate mass- and weight dataset for interpolated mass point weight = integral / float(n) mZ.setVal(mass) dataset = morph.generate(ROOT.RooArgSet(mJ), n) vector_m = np.array([dataset.get(idx).getRealValue('mJ') for idx in range(n)]) # Get closest mass points on either side idx1 = np.where(massPoints - mass < 0)[0][-1] idx2 = np.where(massPoints - mass > 0)[0][0] DSID1 = idx1 + (308363 if not args.isrjet else 0) DSID2 = idx2 + (308363 if not args.isrjet else 0) # Open output file DSID = int("200%03d" % mass) if not args.isrjet: filename = 'objdef_MC_{DSID:6d}.root'.format(DSID=DSID) else: filename = 'sig_%d.root' % mass pass outputdir = '/eos/atlas/user/a/asogaard/Analysis/2016/BoostedJetISR/StatsInputs/2017-08-06/' #output = ROOT.TFile('output/' + filename, 'RECREATE') output = ROOT.TFile(outputdir + filename, 'RECREATE') print "Writing %d events to file '%s'" % (n, output.GetName()) ROOT.TH1.AddDirectory(0) # Get nominal histograms for each of the two closes mass points msk1 = data[nominal]['DSID'] == DSID1 msk2 = data[nominal]['DSID'] == DSID2 hist_nom1 = c.hist(data[nominal][m][msk1], bins=bins, weights=data[nominal]['weight'][msk1], display=False) hist_nom2 = c.hist(data[nominal][m][msk2], bins=bins, weights=data[nominal]['weight'][msk2], display=False) # Loop systematic variations for var in variations: # Reset for each variation! vector_w = np.ones((n,)) * weight print " Writing arrays to file: %s" % var msk1 = data[var]['DSID'] == DSID1 msk2 = data[var]['DSID'] == DSID2 hist_var1 = c.hist(data[var][m][msk1], bins=bins, weights=data[var]['weight'][msk1], display=False) hist_var2 = c.hist(data[var][m][msk2], bins=bins, weights=data[var]['weight'][msk2], display=False) # Plot variations c2 = ap.canvas(num_pads=2, batch=not args.show) p0, p1 = c2.pads() c2.hist(vector_m, bins=bins, weights=vector_w, label="%d GeV (interp.)" % mass) c2.hist(hist_nom1, linecolor=ROOT.kRed, label="%d GeV" % massPoints[idx1]) c2.hist(hist_nom2, linecolor=ROOT.kBlue, label="%d GeV" % massPoints[idx2]) c2.hist(hist_var1, linecolor=ROOT.kRed, linestyle=2) c2.hist(hist_var2, linecolor=ROOT.kBlue, linestyle=2) # -- Get the variation/nominal ratio for each closest masspoint hist_rel1 = c2.ratio_plot((hist_var1, hist_nom1), linecolor=ROOT.kRed, linestyle=1, option='HIST') hist_rel2 = c2.ratio_plot((hist_var2, hist_nom2), linecolor=ROOT.kBlue, linestyle=1, option='HIST') # -- Get the number of bins to shift the systematic variation of each closest masspoint binwidth = bins[1] - bins[0] shift1 = int(np.round((mass - massPoints[idx1]) / binwidth)) shift2 = int(np.round((mass - massPoints[idx2]) / binwidth)) arr_rel1 = hist2array(hist_rel1) arr_rel2 = hist2array(hist_rel2) # -- Shift the variations by the appropriate number of bins by _rolling_ arr_relshift1 = np.roll(arr_rel1, shift1) arr_relshift2 = np.roll(arr_rel2, shift2) arr_relshift1[:shift1] = 1 arr_relshift2[shift2:] = 1 # -- Get weights for the averaging of the two shifted systematics w1 = 1. / float(abs(shift1)) w2 = 1. / float(abs(shift2)) # -- Take the weighted average arr_relshift = (w1 * arr_relshift1 + w2 * arr_relshift2) / (w1 + w2) hist_relshift1 = array2hist(arr_relshift1, hist_rel1.Clone("hist_relshift1")) hist_relshift2 = array2hist(arr_relshift2, hist_rel2.Clone("hist_relshift2")) hist_relshift = array2hist(arr_relshift, hist_rel1.Clone("hist_relshift")) p1.hist(hist_relshift1, linecolor=ROOT.kRed, linestyle=2) p1.hist(hist_relshift2, linecolor=ROOT.kBlue, linestyle=2) p1.hist(hist_relshift, linecolor=ROOT.kBlack, linestyle=1) # -- Compute the jet-by-jet weights for the current variation vector_w_var = vector_w for bin in range(1, hist_relshift.GetXaxis().GetNbins() + 1): # Get mask for all jet in 'bin' msk = (vector_m >= hist_relshift.GetXaxis().GetBinLowEdge(bin)) & (vector_m < hist_relshift.GetXaxis().GetBinUpEdge(bin)) vector_w_var[msk] *= hist_relshift.GetBinContent(bin) pass c2.hist(vector_m, bins=bins, weights=vector_w_var, linecolor=ROOT.kBlack, linestyle=2) p1.ylim(0.5, 1.5) p1.yline(1) c2.xlabel('Large-#it{R} jet mass [GeV]') c2.ylabel('Events / 5 GeV') p1.ylabel('Variation / nominal') c2.legend(header="Signal mass point:", categories=[ ('Nominal', {}), (var, {'linestyle': 2}), ]) c2.text(["#sqrt{s} = 13 TeV", "ISR %s channel" % ('gamma' if not args.isrjet else 'jet'), ], qualifier='Simulation Internal') if args.save: c2.save('plots/interpolation_shift_isr%s_%s_%dGeV.pdf' % ('gamma' if not args.isrjet else 'jet', var, mass)) if args.show: c2.show() # Prepare DISD and isMC vectors vector_DSID = np.ones_like(vector_w_var) * DSID vector_isMC = np.ones_like(vector_w_var).astype(bool) jetmassname = (tf.config['prefix'] + 'm' if not args.isrjet else 'mJ') array1 = np.array(zip(vector_m, vector_w_var), dtype = [(jetmassname, np.float64), ('weight', np.float64)]) array2 = np.array(zip(vector_DSID, vector_isMC), dtype = [('DSID', np.uint32), ('isMC', np.bool_)]) # Mass and weight branch if not args.isrjet: treename1 = tf.config['finaltree'].replace('Nominal', var) else: treename1 = var.replace('ISRjet', 'ISRjet_%d' % mass) pass make_directories('/'.join(treename1.split('/')[:-1]), fromDir=output) tree1 = ROOT.TTree(treename1.split('/')[-1], "") array2tree(array1, tree=tree1) # outputTree if not args.isrjet: treename2 = tf.config['outputtree'].replace('Nominal', var) make_directories('/'.join(treename2.split('/')[:-1]), fromDir=output) tree2 = ROOT.TTree(treename2.split('/')[-1], "") array2tree(array2, tree=tree2) pass output.Write() pass output.Close() pass pass return
def main(): # Parse command-line arguments args = parser.parse_args() # Set correct weighting ROOT.TH1.SetDefaultSumw2(True) # Setup. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - signal_DSID = int("1%02d085" % (0 if args.window is None else args.window * 100)) # Load data files = { 'data': glob.glob(tf.config['base_path'] + 'objdef_data_*.root'), 'bkg': glob.glob(tf.config['base_path'] + 'objdef_TF_%d.root' % signal_DSID), #'sig': glob.glob(tf.config['base_path'] + 'objdef_MC_3054*.root'), 'W': glob.glob(tf.config['base_path'] + 'objdef_MC_30543*.root'), 'Z': glob.glob(tf.config['base_path'] + 'objdef_MC_30544*.root'), 'sfl': glob.glob(tf.config['base_path'] + 'objdef_TF_%d_signalfail.root' % signal_DSID), } if 0 in map(len, files.values()): warning("No files found.") return data = { key: loadData(files[key], tf.config['finaltree'], prefix=tf.config['prefix']) for key in files } data_up = { key: loadData(files[key], tf.config['finaltree'].replace('Nominal', 'TF_UP'), prefix=tf.config['prefix']) for key in ['bkg', 'sfl'] } data_down = { key: loadData(files[key], tf.config['finaltree'].replace('Nominal', 'TF_DOWN'), prefix=tf.config['prefix']) for key in ['bkg', 'sfl'] } info = { key: loadData(files[key], tf.config['outputtree'], stop=1) for key in files } # Scaling by cross section xsec = loadXsec(tf.config['xsec_file']) # Append new DSID field # @TODO: Make more elegant? # Only needs to be done for 'signal', for 'nominal' syst. variation for comp in ['W', 'Z']: # 'sig' data[comp] = append_fields(data[comp], 'DSID', np.zeros((data[comp].size, )), dtypes=int) for idx in info[comp]['id']: msk = ( data[comp]['id'] == idx ) # Get mask of all 'data' entries with same id, i.e. from same file DSID = info[comp]['DSID'][idx] # Get DSID for this file data[comp]['weight'][msk] *= xsec[ DSID] # Scale by cross section x filter eff. for this DSID data[comp]['DSID'][msk] = DSID # Store DSID pass data[comp]['weight'] *= tf.config[ 'lumi'] # Scale all events (MC) by luminosity pass # Check output. if data['data'].size == 0: warning("No data was loaded.") return # k-factors kfactor = {'W': 0.7838, 'Z': 1.3160} data['W']['weight'] *= kfactor['W'] data['Z']['weight'] *= kfactor['Z'] data['sig'] = np.concatenate((data['W'], data['Z'])) # Plotting pre-fit jet mass spectrum # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bestfit_mu = None for mu, fit, prefit, subtract in zip([0, 1, 1, None], [False, False, True, False], [True, True, True, False], [True, True, False, True]): if not prefit: print "#" * 20, bestfit_mu, "#" * 20 mu = bestfit_mu[0] pass # Plotting # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - c = ap.canvas(num_pads=2, batch=not args.show) p0, p1 = c.pads() # -- Histograms: Main pad bins = np.linspace(50, 112, 31 + 1, endpoint=True) # tf.config['massbins'] h_bkg = c.hist(data['bkg']['m'], bins=bins, weights=data['bkg']['weight'], display=False) h_bkg_up = c.hist(data_up['bkg']['m'], bins=bins, weights=data_up['bkg']['weight'], display=False) h_bkg_down = c.hist(data_down['bkg']['m'], bins=bins, weights=data_down['bkg']['weight'], display=False) h_sig = c.hist(data['sig']['m'], bins=bins, weights=data['sig']['weight'], scale=mu, display=False) h_sfl = c.hist(data['sfl']['m'], bins=bins, weights=data['sfl']['weight'], scale=mu, display=False) h_sfl_up = c.hist(data_up['sfl']['m'], bins=bins, weights=data_up['sfl']['weight'], scale=mu, display=False) h_sfl_down = c.hist(data_down['sfl']['m'], bins=bins, weights=data_down['sfl']['weight'], scale=mu, display=False) if not fit: h_bkg.Add(h_sfl, -1) # Subtracting signal h_bkg_up.Add(h_sfl_up, -1) # -- h_bkg_down.Add(h_sfl_down, -1) # -- pass h_bkg = c.stack(h_bkg, fillcolor=ROOT.kAzure + 7, label='Background pred.') h_Z = c.stack(data['Z']['m'], bins=bins, weights=data['Z']['weight'], scale=mu, fillcolor=ROOT.kAzure + 3, label="Z + #gamma (#times #mu)") h_W = c.stack(data['W']['m'], bins=bins, weights=data['W']['weight'], scale=mu, fillcolor=ROOT.kAzure + 2, label="W + #gamma (#times #mu)") h_sum = h_bkg h_sum = c.hist(h_sum, fillstyle=3245, fillcolor=ROOT.kGray + 2, linecolor=ROOT.kGray + 3, option='E2', label='Stat. uncert.') h_bkg_up = c.hist(h_bkg_up, linecolor=ROOT.kRed + 1, linestyle=2, option='HIST', label='TF syst. uncert.') h_bkg_down = c.hist(h_bkg_down, linecolor=ROOT.kGreen + 1, linestyle=2, option='HIST') h_data = c.plot(data['data']['m'], bins=bins, weights=data['data']['weight'], label='Data') # Manuall check chi2 for nominal systematic """ if not fit: chi2 = 0. chi2_data = 0. for ibin in range(1, h_data.GetXaxis().GetNbins() + 1): stat_data = h_data.GetBinError(ibin) cont_data = h_data.GetBinContent(ibin) cont_bkg = h_bkg .GetBinContent(ibin) cont_sig = h_sig .GetBinContent(ibin) diff = np.abs(cont_data - (cont_bkg + cont_sig)) chi2 += np.square(diff / stat_data) pass print "[INFO] mu = {:.2f} | chi2 = {:.1f}".format(mu, chi2) pass """ # -- Histograms: Ratio pad c.ratio_plot((h_sig, h_sum), option='HIST', offset=1, fillcolor=ROOT.kAzure + 2) c.ratio_plot((h_Z, h_sum), option='HIST', offset=1, fillcolor=ROOT.kAzure + 3) c.ratio_plot((h_sum, h_sum), option='E2') c.ratio_plot((h_bkg_up, h_sum), option='HIST') c.ratio_plot((h_bkg_down, h_sum), option='HIST') c.ratio_plot((h_data, h_sum)) # -- Text c.text( [ "#sqrt{s} = 13 TeV, L = %s fb^{-1}" % tf.config['lumi'], "Trimmed anti-k_{t}^{R=1.0} jets", "ISR #gamma selection", "Window: %d GeV #pm %d %%" % (85, (0.2 if args.window is None else args.window) * 100.), # @TODO: Improve "%s-fit: #mu = %s" % ("Pre" if prefit else "Post", "%.0f" % mu if prefit else "%.2f #pm %.2f" % (mu, bestfit_mu[1])), ], qualifier='Internal') # -- Axis labels c.xlabel('Signal jet mass [GeV]') c.ylabel('Events') p1.ylabel('Data / Est.') # -- Axis limits c.padding(0.45) p1.ylim(0.95, 1.15) # -- Line(s) p1.yline(1.0) # -- Region labels c.region("SR", 0.8 * 85, 1.2 * 85) if args.window is not None: c.region("VR", (1 - args.window) * 85, 0.8 * 85) c.region("VR", 1.2 * 85, (1 + args.window) * 85) pass #c.log() c.legend() if args.show and not fit: c.show() if args.save and not fit: c.save('plots/wzsearch_%s%s.pdf' % ('' if args.window is None else 'pm%d_' % (args.window * 100), 'prefit_mu%d' % mu if prefit else 'postfit')) # Saving output for harmonised paper plots # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if not prefit: outfile = ROOT.TFile('output/hists_isrgamma_WZ.root', 'RECREATE') h_bkg.SetName('h_mJ_QCD') h_bkg_up.SetName('h_mJ_QCD_up') h_bkg_down.SetName('h_mJ_QCD_down') h_data.SetName('h_mJ_data') h_W.SetName('h_mJ_WHad') h_Z.SetName('h_mJ_ZHad') for hist in [h_bkg, h_bkg_up, h_bkg_down, h_data, h_W, h_Z]: hist.Write() pass outfile.Close() pass # Fitting # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if fit: hs_save = [ h_bkg_down.Clone('h_save_down'), h_bkg.Clone('h_save_nom'), h_bkg_up.Clone('h_save_up'), ] hdata_save = [ h_data.Clone('hdata_save_down'), h_data.Clone('hdata_save_nom'), h_data.Clone('hdata_save_up'), ] hsfl_save = [ h_sfl_down.Clone('h_sfl_down'), h_sfl.Clone('h_sfl_nom'), h_sfl_up.Clone('h_sfl_up'), ] bestfit_mu = list() for variation in range(3): print "Variation: " + ("Nominal" if variation == 1 else ("Down" if variation == 0 else "Up")) # Get correct histogram for this variation h_bkg_use = hs_save[variation] h_sfl_use = hsfl_save[variation] h_data_use = hdata_save[variation] # -- Define jet mass variable mJ = ROOT.RooRealVar('mJ', 'mJ', 50, 112) mJ.setBins(31) # -- Define histograms rdh_bkg = ROOT.RooDataHist('rdh_bkg', 'rdh_bkg', ROOT.RooArgList(mJ), h_bkg_use) rdh_sig = ROOT.RooDataHist('rdh_sig', 'rdh_sig', ROOT.RooArgList(mJ), h_sig) rdh_sfl = ROOT.RooDataHist('rdh_sfl', 'rdh_sfl', ROOT.RooArgList(mJ), h_sfl_use) # -- Turn histograms into pdf's rhp_bkg = ROOT.RooHistPdf('rhp_bkg', 'rhp_bkg', ROOT.RooArgSet(mJ), rdh_bkg) rhp_sig = ROOT.RooHistPdf('rhp_sig', 'rhp_sig', ROOT.RooArgSet(mJ), rdh_sig) rhp_sfl = ROOT.RooHistPdf('rhp_sfl', 'rhp_sfl', ROOT.RooArgSet(mJ), rdh_sfl) # -- Define integrals as constants n_bkg = ROOT.RooRealVar('n_bkg', 'n_bkg', h_bkg_use.Integral()) n_sig = ROOT.RooRealVar('n_sig', 'n_sig', h_sig.Integral()) n_sfl = ROOT.RooRealVar('n_sfl', 'n_sfl', h_sfl_use.Integral()) # -- Define signal strength and constant(s) v_mu = ROOT.RooRealVar('v_mu', 'v_mu', 1, 0, 5) v_neg1 = ROOT.RooRealVar('v_neg1', 'v_neg1', -1) # -- Define fittable normalisation factors c_bkg = ROOT.RooFormulaVar('c_bkg', 'c_bkg', '@0', ROOT.RooArgList(n_bkg)) c_sig = ROOT.RooFormulaVar('c_sig', 'c_sig', '@0 * @1', ROOT.RooArgList(v_mu, n_sig)) c_sfl = ROOT.RooFormulaVar( 'c_sfl', 'c_sfl', '@0 * @1 * @2', ROOT.RooArgList(v_neg1, v_mu, n_sfl)) # -- Construct combined pdf pdf = ROOT.RooAddPdf( 'pdf', 'pdf', ROOT.RooArgList(rhp_bkg, rhp_sig, rhp_sfl), ROOT.RooArgList(c_bkg, c_sig, c_sfl)) # -- Construct data histogram rdh_data = ROOT.RooDataHist('rdh_data', 'rdh_data', ROOT.RooArgList(mJ), h_data_use) # -- Fit pdf to data histogram #pdf.fitTo(rdh_data) chi2Fit = ROOT.RooChi2Var("chi2", "chi2", pdf, rdh_data, ROOT.RooFit.Extended()) minuit = ROOT.RooMinuit(chi2Fit) minuit.migrad() minuit.hesse() r2 = minuit.save() bestfit_mu.append((v_mu.getValV(), v_mu.getError())) pass print "(" * 10, bestfit_mu, ")" * 10 bestfit_mu = bestfit_mu[1][0], np.sqrt( np.power(abs(bestfit_mu[0][0] - bestfit_mu[2][0]) / 2., 2.) + np.power(bestfit_mu[1][1], 2.)) pass pass return
def main(): # Parse command-line arguments args = parser.parse_args() DSID = int("100%03d" % args.mass) # Setup. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Get signal file sig_DSID = get_signal_DSID(args.mass, tolerance=10) if sig_DSID is None: warning("No signal file was found") return sig_file = 'objdef_MC_{DSID:6d}.root'.format(DSID=sig_DSID) # Load data files = { 'data': glob.glob(tf.config['base_path'] + 'objdef_MC_3610*.root'), 'gbs': glob.glob(tf.config['base_path'] + 'objdef_GBSMC_400001.root'), 'WZ': glob.glob(tf.config['base_path'] + 'objdef_MC_3054*.root') } if args.inject: files['sig'] = glob.glob(tf.config['base_path'] + sig_file) pass if len(files) == 0: warning("No files found. Try to run:") warning(" $ source getSomeData.sh") return data = loadData(files['data'], tf.config['tree'], prefix=tf.config['prefix']) gbs = loadData(files['gbs'], tf.config['finaltree'], prefix=tf.config['prefix']) WZ = loadData(files['WZ'], tf.config['tree'], prefix=tf.config['prefix']) if args.inject: signal = loadData(files['sig'], tf.config['tree'], prefix=tf.config['prefix']) else: signal = None pass info = { key: loadData(files[key], tf.config['outputtree'], stop=1) for key in files } # Scaling by cross section xsec = loadXsec(tf.config['xsec_file']) # Append new DSID field if args.inject: signal = append_fields(signal, 'DSID', np.zeros((signal.size, )), dtypes=int) for idx, id in enumerate(info['sig']['id']): msk = ( signal['id'] == id ) # Get mask of all 'signal' entries with same id, i.e. from same file DSID = info['sig']['DSID'][idx] # Get DSID for this file signal['weight'][msk] *= xsec[ DSID] # Scale by cross section x filter eff. for this DSID signal['DSID'][msk] = DSID # Store DSID pass signal['weight'] *= tf.config['lumi'] pass WZ = append_fields(WZ, 'DSID', np.zeros((WZ.size, )), dtypes=int) for idx, id in enumerate(info['WZ']['id']): msk = ( WZ['id'] == id ) # Get mask of all 'WZ' entries with same id, i.e. from same file DSID = info['WZ']['DSID'][idx] # Get DSID for this file WZ['weight'][msk] *= xsec[ DSID] # Scale by cross section x filter eff. for this DSID WZ['DSID'][msk] = DSID # Store DSID pass WZ['weight'] *= tf.config['lumi'] #if not args.data: data = append_fields(data, 'DSID', np.zeros((data.size, )), dtypes=int) for idx, id in enumerate(info['data']['id']): msk = ( data['id'] == id ) # Get mask of all 'data' entries with same id, i.e. from same file DSID = info['data']['DSID'][idx] # Get DSID for this file data['weight'][msk] *= xsec[ DSID] # Scale by cross section x filter eff. for this DSID data['DSID'][msk] = DSID # Store DSID pass data['weight'] *= tf.config['lumi'] #pass # Compute new variables data = append_fields(data, 'logpt', np.log(data['pt'])) WZ = append_fields(WZ, 'logpt', np.log(WZ['pt'])) if signal is not None: signal = append_fields(signal, 'logpt', np.log(signal['pt'])) pass # Inject signal into data if args.inject: data = np.array(np.concatenate((data, signal)), dtype=data.dtype) pass #if not args.data: data = np.array(np.concatenate((data, WZ)), dtype=data.dtype) #pass """ @TODO: Not sure this script works for data input... But it's not used anyway. """ # Transfer factor # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Pass/fail masks # -- Data (incl. signal) msk_pass = tf.config['pass'](data) msk_fail = ~msk_pass # -- W/Z msk_WZ_pass = tf.config['pass'](WZ) msk_WZ_fail = ~msk_WZ_pass # -- Signal if args.inject: msk_sig_pass = tf.config['pass'](signal) msk_sig_fail = ~msk_sig_pass pass # Transfer factor calculator instance calc = tf.calculator(data=data, config=tf.config, subtract=WZ) # Nominal fit calc.fit() w_nom = calc.weights(data[msk_fail]) w_nom_WZ = calc.weights(WZ[msk_WZ_fail]) if args.show or args.save: calc.plot(show=args.show, save=args.save, prefix='plots/globalbackground_%s_%s_' % ('injected' if args.inject else 'notinjected', 'data' if args.data else 'MC')) # mass +/- 20% stripe fit calc.mass = args.mass calc.window = 0.2 calc.fit() w_stripe = calc.weights(data[msk_fail]) w_stripe_WZ = calc.weights(WZ[msk_WZ_fail]) if args.inject: w_stripe_sig = calc.weights(signal[msk_sig_fail]) pass # Plotting # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bins = np.linspace(100, 250, 30 + 1, endpoint=True) # Setup canvas c = ap.canvas(num_pads=2, batch=not args.show) p0, p1 = c.pads() # Add stacked backgrounds h_bkg_nom = c.hist(data['m'][msk_fail], bins=bins, weights=data['weight'][msk_fail] * w_nom, display=False) h_bkg_stripe = c.hist(data['m'][msk_fail], bins=bins, weights=data['weight'][msk_fail] * w_stripe, display=False) h_WZfl_nom = c.hist(WZ['m'][msk_WZ_fail], bins=bins, weights=WZ['weight'][msk_WZ_fail] * w_nom_WZ, display=False) h_WZfl_stripe = c.hist(WZ['m'][msk_WZ_fail], bins=bins, weights=WZ['weight'][msk_WZ_fail] * w_stripe_WZ, display=False) if args.inject: h_sig = c.hist(signal['m'][msk_sig_pass], bins=bins, weights=signal['weight'][msk_sig_pass], display=False) h_sfl = c.hist(signal['m'][msk_sig_fail], bins=bins, weights=signal['weight'][msk_sig_fail] * w_stripe_sig, display=False) pass h_gbs = c.hist(gbs['m'], bins=bins, weights=gbs['weight'], display=False) # -- Subtract (opt.) if args.inject: h_bkg_stripe.Add(h_sfl, -1) h_gbs.Add(h_sfl, -1) pass h_bkg_nom.Add(h_WZfl_nom, -1) h_bkg_stripe.Add(h_WZfl_stripe, -1) # -- Actually draw #if not args.data: h_WZ = c.stack(WZ['m'][msk_WZ_pass], bins=bins, weights=WZ['weight'][msk_WZ_pass], fillcolor=ROOT.kRed - 4, label='W/Z + #gamma') #pass h_bkg_nom = c.stack(h_bkg_nom, fillcolor=ROOT.kAzure + 7, label="Bkg. (full)") h_sum = c.getStackSum() h_bkg_stripe.Add(h_WZ) h_gbs.Add(h_WZ) if args.inject: h_sig = c.stack(h_sig, fillcolor=ROOT.kViolet - 4, label="Z' (%d GeV)" % args.mass) pass h_bkg_stripe = c.hist(h_bkg_stripe, linecolor=ROOT.kGreen + 1, label="Bkg. (window)") # % args.mass) h_gbs = c.hist(h_gbs, linecolor=ROOT.kViolet + 1, label="Bkg. (GBS)") # Draw stats. error of stacked sum h_sum = c.hist(h_sum, fillstyle=3245, fillcolor=ROOT.kGray + 2, linecolor=ROOT.kGray + 3, label='Stats. uncert.', option='E2') # Add (pseudo-) data h_data = c.plot(data['m'][msk_pass], bins=bins, weights=data['weight'][msk_pass], markersize=0.8, label='Data' if args.data else 'Pseudo-data') # Axis limits p1.ylim(0.8, 1.2) c.padding(0.45) c.log(True) # Draw error- and ratio plots if args.inject: hr_sig = c.ratio_plot((h_sig, h_sum), option='HIST', offset=1) pass h_err = c.ratio_plot((h_sum, h_sum), option='E2') h_ratio = c.ratio_plot((h_data, h_sum), oob=True) h_rgbs = c.ratio_plot((h_gbs, h_sum), linecolor=ROOT.kViolet + 1, option='HIST ][') h_rgbs = c.ratio_plot((h_bkg_stripe, h_sum), linecolor=ROOT.kGreen + 1, option='HIST ][') # Add labels and text c.xlabel('Signal jet mass [GeV]') c.ylabel('Events') p1.ylabel('Data / Nom.') c.text([ "#sqrt{s} = 13 TeV, L = 36.1 fb^{-1}", ] + ([ "Sherpa incl. #gamma MC", ] if not args.data else []) + [ "Trimmed anti-k_{t}^{R=1.0} jets", "ISR #gamma selection", ] + (["Signal injected"] if args.inject else []), qualifier='%sInternal' % ("Simulation " if not args.data else "")) # Add line(s) p1.yline(1.0) # Draw legend c.legend() c.region("SR", 0.8 * args.mass, 1.2 * args.mass) # Save and show plot if args.save: c.save('plots/globalbackground_spectrum_%dGeV_%s_%s.pdf' % (args.mass, 'injected' if args.inject else 'notinjected', 'data' if args.data else 'MC')) if args.show: c.show() # p0-plot # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Setup canvas c2 = ap.canvas(batch=not args.show) p_local = h_data.Clone('p_local') p_global = h_data.Clone('p_global') for bin in range(1, h_data.GetXaxis().GetNbins() + 1): c_data = h_data.GetBinContent(bin) e_data = h_data.GetBinError(bin) c_loc = h_bkg_stripe.GetBinContent(bin) e_loc = h_bkg_stripe.GetBinError(bin) c_glb = h_gbs.GetBinContent(bin) e_glb = e_loc # h_gbs .GetBinError (bin) z_loc = (c_data - c_loc) / np.sqrt(np.square(e_data) + np.square(e_loc)) z_glb = (c_data - c_glb) / np.sqrt(np.square(e_data) + np.square(e_glb)) if c_glb > 0 else 0 p_loc = min(ROOT.TMath.Erfc(z_loc / np.sqrt(2)), 1) p_glb = min(ROOT.TMath.Erfc(z_glb / np.sqrt(2)), 1) p_local.SetBinContent(bin, p_loc) p_global.SetBinContent(bin, p_glb) p_local.SetBinError(bin, 0) p_global.SetBinError(bin, 0) pass c2.plot(p_local, markercolor=ROOT.kGreen + 1, linecolor=ROOT.kGreen + 1, option='PL', label="Local (20% window)") c2.plot(p_global, markercolor=ROOT.kViolet + 1, linecolor=ROOT.kViolet + 1, option='PL', label="Global (GBS)") c2.xlabel("Signal jet mass [GeV]") c2.ylabel("p_{0}") c2.log() c2.ylim(1E-04, 1E+04) for sigma in range(4): c2.yline(ROOT.TMath.Erfc(sigma / np.sqrt(2))) pass c2.text([ "#sqrt{s} = 13 TeV, L = 36.1 fb^{-1}", ] + ([ "Sherpa incl. #gamma MC", ] if not args.data else []) + [ "Trimmed anti-k_{t}^{R=1.0} jets", "ISR #gamma selection", ("Signal" if args.inject else "No signal") + " injected" + (" at m = %d GeV" % args.mass if args.inject else ""), ], qualifier='Simulation Internal') c2.region("SR", 0.8 * args.mass, 1.2 * args.mass) c2.legend() if args.save: c2.save('plots/globalbackground_p0_%dGeV_%s_%s.pdf' % (args.mass, 'injected' if args.inject else 'notinjected', 'data' if args.data else 'MC')) if args.show: c2.show() return