def draw_channel(channel, fit=None, no_data=False, ypadding=None, log_ypadding=None, **kwargs): """ Draw a HistFactory::Channel only include OverallSys systematics in resulting band as an illustration of the level of uncertainty since correlations of the NPs are not known and it is not possible to draw the statistically correct error band. """ if fit is not None: log.warning("applying snapshot on channel {0}".format(channel.name)) channel = channel.apply_snapshot(fit) if channel.data and channel.data.hist and not no_data: data_hist = channel.data.hist else: data_hist = None model_hists = [] signal_hists = [] systematics_terms = {} for sample in channel.samples: nominal_hist = sample.hist.Clone(shallow=True) _systematics = {} for sys_name, osys, hsys in sample.iter_sys(): systematics_terms[sys_name] = (sys_name + '_UP', sys_name + '_DOWN') if hsys is not None: # include only overallsys component norm, shape = split_norm_shape(hsys, nominal_hist) if osys is not None: osys.low *= norm.low osys.high *= norm.high else: osys = norm _systematics[sys_name + '_DOWN'] = nominal_hist * osys.low _systematics[sys_name + '_UP'] = nominal_hist * osys.high log.debug("sample: {0} overallsys: {1} high: {2} low: {3}".format( sample.name, sys_name, osys.high, osys.low)) nominal_hist.systematics = _systematics if sample.GetNormFactor('SigXsecOverSM') is not None: signal_hists.append(nominal_hist) else: model_hists.append(nominal_hist) if 'systematics' in kwargs: del kwargs['systematics'] figs = [] for logy in (False, True): figs.append( draw(data=data_hist, model=model_hists or None, signal=signal_hists or None, systematics=systematics_terms, logy=logy, ypadding=(log_ypadding or ypadding) if logy else ypadding, **kwargs)) return figs
def draw_channel(channel, fit=None, no_data=False, ypadding=None, log_ypadding=None, **kwargs): """ Draw a HistFactory::Channel only include OverallSys systematics in resulting band as an illustration of the level of uncertainty since correlations of the NPs are not known and it is not possible to draw the statistically correct error band. """ if fit is not None: log.warning("applying snapshot on channel {0}".format(channel.name)) channel = channel.apply_snapshot(fit) if channel.data and channel.data.hist and not no_data: data_hist = channel.data.hist else: data_hist = None model_hists = [] signal_hists = [] systematics_terms = {} for sample in channel.samples: nominal_hist = sample.hist.Clone(shallow=True) _systematics = {} for sys_name, osys, hsys in sample.iter_sys(): systematics_terms[sys_name] = ( sys_name + '_UP', sys_name + '_DOWN') if hsys is not None: # include only overallsys component norm, shape = split_norm_shape(hsys, nominal_hist) if osys is not None: osys.low *= norm.low osys.high *= norm.high else: osys = norm _systematics[sys_name + '_DOWN'] = nominal_hist * osys.low _systematics[sys_name + '_UP'] = nominal_hist * osys.high log.debug("sample: {0} overallsys: {1} high: {2} low: {3}".format( sample.name, sys_name, osys.high, osys.low)) nominal_hist.systematics = _systematics if sample.GetNormFactor('SigXsecOverSM') is not None: signal_hists.append(nominal_hist) else: model_hists.append(nominal_hist) if 'systematics' in kwargs: del kwargs['systematics'] figs = [] for logy in (False, True): figs.append(draw( data=data_hist, model=model_hists or None, signal=signal_hists or None, systematics=systematics_terms, logy=logy, ypadding=(log_ypadding or ypadding) if logy else ypadding, **kwargs)) return figs
def apply_split_norm_shape(s): from rootpy.stats.histfactory import split_norm_shape for histosys in s.histo_sys: # skip histosys for which overallsys already exist if s.GetOverallSys(histosys.name) is not None: continue log.info("splitting HistoSys `{0}` in sample `{1}`".format( histosys.name, s.name)) norm, shape = split_norm_shape(histosys, s.hist) histosys.high = shape.high histosys.low = shape.low s.AddOverallSys(norm)
def histfactory(self, sample, category, systematics=False, rec=None, weights=None, mva=False, uniform=False, nominal=None): if not systematics: return if len(self.modes) != 1: raise TypeError( 'histfactory sample only valid for single production mode') if len(self.masses) != 1: raise TypeError( 'histfactory sample only valid for single mass point') # isolation systematic sample.AddOverallSys( 'ATLAS_ANA_HH_{0:d}_Isolation'.format(self.year), 1. - 0.06, 1. + 0.06) mode = self.modes[0] if mode in ('Z', 'W'): _uncert_mode = 'VH' else: _uncert_mode = self.MODES_WORKSPACE[mode] if self.year == 2011: energy = 7 elif self.year == 2012: energy = 8 else: raise ValueError( "collision energy is unknown for year {0:d}".format(self.year)) # QCD_SCALE for qcd_scale_term, qcd_scale_mode, qcd_scale_category, values in self.QCD_SCALE: if qcd_scale_mode == _uncert_mode and qcd_scale_category == category.name: high, low = map(float, values.split('/')) sample.AddOverallSys(qcd_scale_term, low, high) # UE UNCERTAINTY for ue_term, ue_mode, ue_category, values in self.UE_UNCERT: if ue_mode == _uncert_mode and ue_category == category.name: high, low = map(float, values.split('/')) sample.AddOverallSys(ue_term, low, high) # PDF ACCEPTANCE UNCERTAINTY (OverallSys) for pdf_term, pdf_mode, pdf_category, values in self.PDF_ACCEPT_NORM_UNCERT: if pdf_mode == _uncert_mode and pdf_category == category.name: high, low = map(float, values.split('/')) sample.AddOverallSys(pdf_term, low, high) sample_nom = sample.hist # PDF ACCEPTANCE UNCERTAINTY (HistoSys) ONLY FOR MVA if mva: for pdf_term, pdf_mode, pdf_category, hist_names in self.PDF_ACCEPT_SHAPE_UNCERT: if pdf_mode == _uncert_mode and pdf_category == category.name: high_name, low_name = hist_names.format(energy).split('/') high_shape, low_shape = self.PDF_ACCEPT_file[high_name], self.PDF_ACCEPT_file[low_name] if len(high_shape) != len(sample.hist): log.warning("skipping pdf acceptance shape systematic " "since histograms are not compatible") continue high = sample_nom.Clone(shallow=True, name=sample_nom.name + '_{0}_UP'.format(pdf_term)) low = sample_nom.Clone(shallow=True, name=sample_nom.name + '_{0}_DOWN'.format(pdf_term)) high *= high_shape low *= low_shape histsys = histfactory.HistoSys( pdf_term, low=low, high=high) sample.AddHistoSys(histsys) #mixing Norms if self.SM: log.info('adding norm factor') sample.AddNormFactor('ATLAS_epsilon', 1., -200., 200., False) elif self.BSM: log.info('adding norm factor') sample.AddNormFactor('ATLAS_epsilon_rejected', 1., -200., 200., False) else: log.info('no norms for {0}'.format(self.name)) # BR_tautau _, (br_up, br_down) = yellowhiggs.br( self.mass, 'tautau', error_type='factor') sample.AddOverallSys('ATLAS_BR_tautau', br_down, br_up) # <NormFactor Name="mu_BR_tautau" Val="1" Low="0" High="200" /> sample.AddNormFactor('mu_BR_tautau', 1., 0., 200., True) #mu_XS[energy]_[mode] #_, (xs_up, xs_down) = yellowhiggs.xs( # energy, self.mass, self.MODES_DICT[self.mode][0], # error_type='factor') #sample.AddOverallSys( # 'mu_XS{0:d}_{1}'.format(energy, self.MODES_WORKSPACE[self.mode]), # xs_down, xs_up) sample.AddNormFactor( 'mu_XS{0:d}_{1}'.format(energy, self.MODES_WORKSPACE[self.mode]), 1., 0., 200., True) # https://twiki.cern.ch/twiki/bin/viewauth/AtlasProtected/HSG4Uncertainties # pdf uncertainty if mode == 'gg': if energy == 8: sample.AddOverallSys('pdf_Higgs_gg', 0.93, 1.08) else: # 7 TeV sample.AddOverallSys('pdf_Higgs_gg', 0.92, 1.08) else: if energy == 8: sample.AddOverallSys('pdf_Higgs_qq', 0.97, 1.03) else: # 7 TeV sample.AddOverallSys('pdf_Higgs_qq', 0.98, 1.03) # EWK NLO CORRECTION FOR VBF ONLY if mode == 'VBF': sample.AddOverallSys('NLO_EW_Higgs', 0.98, 1.02) # QCDscale_ggH3in HistoSys ONLY FOR MVA # also see ggH3in script if mva and mode == 'gg' and category.name == 'vbf': Rel_Error_2j = 0.215 Error_exc = 0.08613046469238815 # Abs error on the exclusive xsec xsec_exc = 0.114866523583739 # Exclusive Xsec Error_3j = sqrt(Error_exc**2 - (Rel_Error_2j*xsec_exc)**2) rel_error = Error_3j / xsec_exc dphi = rec['true_dphi_jj_higgs_no_overlap'] scores = rec['classifier'] idx_2j = ((pi - dphi) < 0.2) & (dphi >= 0) idx_3j = ((pi - dphi) >= 0.2) & (dphi >= 0) # get normalization factor dphi_2j = weights[idx_2j].sum() dphi_3j = weights[idx_3j].sum() weight_up = np.ones(len(weights)) weight_dn = np.ones(len(weights)) weight_up[idx_2j] -= (dphi_3j / dphi_2j) * rel_error weight_dn[idx_2j] += (dphi_3j / dphi_2j) * rel_error weight_up[idx_3j] += rel_error weight_dn[idx_3j] -= rel_error weight_up *= weights weight_dn *= weights up_hist = nominal.clone(shallow=True, name=sample_nom.name + '_QCDscale_ggH3in_UP') up_hist.Reset() dn_hist = nominal.clone(shallow=True, name=sample_nom.name + '_QCDscale_ggH3in_DOWN') dn_hist.Reset() fill_hist(up_hist, scores, weight_up) fill_hist(dn_hist, scores, weight_dn) if uniform: up_hist = uniform_hist(up_hist) dn_hist = uniform_hist(dn_hist) shape = histfactory.HistoSys('QCDscale_ggH3in', low=dn_hist, high=up_hist) norm, shape = histfactory.split_norm_shape(shape, sample_nom) sample.AddHistoSys(shape)
def histfactory(self, sample, category, systematics=True): if not systematics: return if len(self.modes) != 1: raise TypeError( 'histfactory sample only valid for single production mode') if len(self.masses) != 1: raise TypeError( 'histfactory sample only valid for single mass point') mode = self.modes[0] if mode in ('Z', 'W'): _qcd_scale_mode = 'VH' else: _qcd_scale_mode = self.MODES_WORKSPACE[mode] # QCD_SCALE for qcd_scale_term, qcd_scale_mode, qcd_scale_category, values in self.QCD_SCALE: if qcd_scale_mode == _qcd_scale_mode and qcd_scale_category.lower() in category.name.lower(): high, low = map(float, values.split('/')) sample.AddOverallSys(qcd_scale_term, low, high) # GEN_QMASS for qmass_term, qmass_mode, qmass_category, values in self.GEN_QMASS: if qmass_mode == _qcd_scale_mode and qmass_category.lower() in category.name.lower(): high, low = map(float, values.split('/')) sample.AddOverallSys(qmass_term, low, high) # BR_tautau _, (br_up, br_down) = yellowhiggs.br( self.mass, 'tautau', error_type='factor') sample.AddOverallSys('ATLAS_BR_tautau', br_down, br_up) # <NormFactor Name="mu_BR_tautau" Val="1" Low="0" High="200" /> sample.AddNormFactor('mu_BR_tautau', 1., 0., 200., True) if self.year == 2011: energy = 7 elif self.year == 2012: energy = 8 else: raise ValueError( "collision energy is unknown for year {0:d}".format(self.year)) #mu_XS[energy]_[mode] #_, (xs_up, xs_down) = yellowhiggs.xs( # energy, self.mass, self.MODES_DICT[self.mode][0], # error_type='factor') #sample.AddOverallSys( # 'mu_XS{0:d}_{1}'.format(energy, self.MODES_WORKSPACE[self.mode]), # xs_down, xs_up) sample.AddNormFactor( 'mu_XS{0:d}_{1}'.format(energy, self.MODES_WORKSPACE[self.mode]), 1., 0., 200., True) # https://twiki.cern.ch/twiki/bin/viewauth/AtlasProtected/HSG4Uncertainties # underlying event uncertainty in the VBF category if 'vbf' in category.name.lower(): if mode == 'gg': sample.AddOverallSys('ATLAS_UE_gg', 0.7, 1.3) elif mode == 'VBF': sample.AddOverallSys('ATLAS_UE_qq', 0.94, 1.06) # pdf uncertainty if mode == 'gg': if energy == 8: sample.AddOverallSys('pdf_Higgs_gg', 0.93, 1.08) else: # 7 TeV sample.AddOverallSys('pdf_Higgs_gg', 0.92, 1.08) else: if energy == 8: sample.AddOverallSys('pdf_Higgs_qq', 0.97, 1.03) else: # 7 TeV sample.AddOverallSys('pdf_Higgs_qq', 0.98, 1.03) # QCDscale_ggH3in MVA only if mode == 'gg' and category.name == 'vbf': up = self.QCDscale_ggH3in_file.up_fit dn = self.QCDscale_ggH3in_file.dn_fit nom = sample.hist up_hist = nom.clone(shallow=True, name=nom.name + '_QCDscale_ggH3in_UP') dn_hist = nom.clone(shallow=True, name=nom.name + '_QCDscale_ggH3in_DOWN') up_hist *= up dn_hist *= dn shape = histfactory.HistoSys('QCDscale_ggH3in', low=dn_hist, high=up_hist) norm, shape = histfactory.split_norm_shape(shape, nom) sample.AddHistoSys(shape)
def histfactory(self, sample, category, systematics=False, rec=None, weights=None, mva=False, uniform=False, nominal=None): if not systematics: return if len(self.modes) != 1: raise TypeError( 'histfactory sample only valid for single production mode') if len(self.masses) != 1: raise TypeError( 'histfactory sample only valid for single mass point') # isolation systematic sample.AddOverallSys('ATLAS_ANA_HH_{0:d}_Isolation'.format(self.year), 1. - 0.06, 1. + 0.06) mode = self.modes[0] if mode in ('Z', 'W'): _uncert_mode = 'VH' else: _uncert_mode = self.MODES_WORKSPACE[mode] if self.year == 2011: energy = 7 elif self.year == 2012: energy = 8 else: raise ValueError( "collision energy is unknown for year {0:d}".format(self.year)) # QCD_SCALE for qcd_scale_term, qcd_scale_mode, qcd_scale_category, values in self.QCD_SCALE: if qcd_scale_mode == _uncert_mode and qcd_scale_category == category.name: high, low = map(float, values.split('/')) sample.AddOverallSys(qcd_scale_term, low, high) # UE UNCERTAINTY for ue_term, ue_mode, ue_category, values in self.UE_UNCERT: if ue_mode == _uncert_mode and ue_category == category.name: high, low = map(float, values.split('/')) sample.AddOverallSys(ue_term, low, high) # PDF ACCEPTANCE UNCERTAINTY (OverallSys) for pdf_term, pdf_mode, pdf_category, values in self.PDF_ACCEPT_NORM_UNCERT: if pdf_mode == _uncert_mode and pdf_category == category.name: high, low = map(float, values.split('/')) sample.AddOverallSys(pdf_term, low, high) sample_nom = sample.hist # PDF ACCEPTANCE UNCERTAINTY (HistoSys) ONLY FOR MVA if mva: for pdf_term, pdf_mode, pdf_category, hist_names in self.PDF_ACCEPT_SHAPE_UNCERT: if pdf_mode == _uncert_mode and pdf_category == category.name: high_name, low_name = hist_names.format(energy).split('/') high_shape, low_shape = self.PDF_ACCEPT_file[ high_name], self.PDF_ACCEPT_file[low_name] if len(high_shape) != len(sample.hist): log.warning("skipping pdf acceptance shape systematic " "since histograms are not compatible") continue high = sample_nom.Clone(shallow=True, name=sample_nom.name + '_{0}_UP'.format(pdf_term)) low = sample_nom.Clone(shallow=True, name=sample_nom.name + '_{0}_DOWN'.format(pdf_term)) high *= high_shape low *= low_shape histsys = histfactory.HistoSys(pdf_term, low=low, high=high) sample.AddHistoSys(histsys) # BR_tautau _, (br_up, br_down) = yellowhiggs.br(self.mass, 'tautau', error_type='factor') sample.AddOverallSys('ATLAS_BR_tautau', br_down, br_up) # <NormFactor Name="mu_BR_tautau" Val="1" Low="0" High="200" /> sample.AddNormFactor('mu_BR_tautau', 1., 0., 200., True) #mu_XS[energy]_[mode] #_, (xs_up, xs_down) = yellowhiggs.xs( # energy, self.mass, self.MODES_DICT[self.mode][0], # error_type='factor') #sample.AddOverallSys( # 'mu_XS{0:d}_{1}'.format(energy, self.MODES_WORKSPACE[self.mode]), # xs_down, xs_up) sample.AddNormFactor( 'mu_XS{0:d}_{1}'.format(energy, self.MODES_WORKSPACE[self.mode]), 1., 0., 200., True) # https://twiki.cern.ch/twiki/bin/viewauth/AtlasProtected/HSG4Uncertainties # pdf uncertainty if mode == 'gg': if energy == 8: sample.AddOverallSys('pdf_Higgs_gg', 0.93, 1.08) else: # 7 TeV sample.AddOverallSys('pdf_Higgs_gg', 0.92, 1.08) else: if energy == 8: sample.AddOverallSys('pdf_Higgs_qq', 0.97, 1.03) else: # 7 TeV sample.AddOverallSys('pdf_Higgs_qq', 0.98, 1.03) # EWK NLO CORRECTION FOR VBF ONLY if mode == 'VBF': sample.AddOverallSys('NLO_EW_Higgs', 0.98, 1.02) # QCDscale_ggH3in HistoSys ONLY FOR MVA # also see ggH3in script if mva and mode == 'gg' and category.name == 'vbf': Rel_Error_2j = 0.215 Error_exc = 0.08613046469238815 # Abs error on the exclusive xsec xsec_exc = 0.114866523583739 # Exclusive Xsec Error_3j = sqrt(Error_exc**2 - (Rel_Error_2j * xsec_exc)**2) rel_error = Error_3j / xsec_exc dphi = rec['true_dphi_jj_higgs_no_overlap'] scores = rec['classifier'] idx_2j = ((pi - dphi) < 0.2) & (dphi >= 0) idx_3j = ((pi - dphi) >= 0.2) & (dphi >= 0) # get normalization factor dphi_2j = weights[idx_2j].sum() dphi_3j = weights[idx_3j].sum() weight_up = np.ones(len(weights)) weight_dn = np.ones(len(weights)) weight_up[idx_2j] -= (dphi_3j / dphi_2j) * rel_error weight_dn[idx_2j] += (dphi_3j / dphi_2j) * rel_error weight_up[idx_3j] += rel_error weight_dn[idx_3j] -= rel_error weight_up *= weights weight_dn *= weights up_hist = nominal.clone(shallow=True, name=sample_nom.name + '_QCDscale_ggH3in_UP') up_hist.Reset() dn_hist = nominal.clone(shallow=True, name=sample_nom.name + '_QCDscale_ggH3in_DOWN') dn_hist.Reset() fill_hist(up_hist, scores, weight_up) fill_hist(dn_hist, scores, weight_dn) if uniform: up_hist = uniform_hist(up_hist) dn_hist = uniform_hist(dn_hist) shape = histfactory.HistoSys('QCDscale_ggH3in', low=dn_hist, high=up_hist) norm, shape = histfactory.split_norm_shape(shape, sample_nom) sample.AddHistoSys(shape)