def test_replace_context_nested_list_dict(self): _cfg = deepcopy(self.BASE_CFG) _cfg['templates'][0].update({ 'replace_under_here': { 'context_nested_list_dict': [{ 'inner_key': [ ContextValue('namespace[key]'), ContextValue('namespace[key]') ] }, { 'inner_key': [ ContextValue('namespace[key]'), ContextValue('namespace[key]') ] }], } }) _results = self._run_palisade(config=_cfg) self.assertEqual(len(_results), 1) for _i in range(2): for _j in range(2): self.assertEqual( _results[0]['replace_under_here'] ['context_nested_list_dict'][_i]['inner_key'][_j], self.BASE_CFG['expansions']['namespace'][0]['key'])
def test_replace_context_value_lazy_arithmetic_2(self): _cfg = deepcopy(self.BASE_CFG) _cfg['templates'][0].update({ 'replace_under_here': { 'context_value': ContextValue('namespace[key]') * ContextValue('namespace[key]'), } }) _results = self._run_palisade(config=_cfg) self.assertEqual(len(_results), 1) self.assertEqual(_results[0]['replace_under_here']['context_value'], self.BASE_CFG['expansions']['namespace'][0]['key']**2)
def test_replace_context_value_lazy_meta_key(self): _cfg = deepcopy(self.BASE_CFG) _cfg['templates'][0].update({ 'replace_under_here': { 'context_value': ContextValue( String("namespace[{}]").format( ContextValue('namespace[meta_key]'))), } }) _results = self._run_palisade(config=_cfg) self.assertEqual(len(_results), 1) self.assertEqual(_results[0]['replace_under_here']['context_value'], self.BASE_CFG['expansions']['namespace'][0]['key'])
def test_context_value_unsupported_syntax(self): _cfg = deepcopy(self.BASE_CFG) _cfg['templates'][0].update({ 'replace_under_here': { 'context_value': ContextValue('namespace+key'), } }) with self.assertRaises(ConfigurationError): _results = self._run_palisade(config=_cfg)
def test_replace_context_string_top_level_key(self): _cfg = deepcopy(self.BASE_CFG) _cfg['templates'][0].update({ 'replace_this_as_well': ContextValue('namespace[key]'), }) _results = self._run_palisade(config=_cfg) self.assertEqual(len(_results), 1) self.assertEqual(_results[0]['replace_this_as_well'], self.BASE_CFG['expansions']['namespace'][0]['key'])
def test_context_value_inexistent_key_raise(self): _cfg = deepcopy(self.BASE_CFG) _cfg['templates'][0].update({ 'replace_under_here': { 'context_value': ContextValue('namespace[inexistent_key]'), } }) with self.assertRaises(ConfigurationError) as _err: _results = self._run_palisade(config=_cfg) self.assertIn("'inexistent_key'", _err.exception.args[0])
def test_replace_context_value_namespace_with_space(self): _cfg = deepcopy(self.BASE_CFG) _cfg['expansions'] = { # ...times fifteen contexts 'namespace with space': [dict(key=42)], } _cfg['templates'][0].update({ 'replace_under_here': { 'context_value': ContextValue('"namespace with space"[key]'), } }) _results = self._run_palisade(config=_cfg) self.assertEqual(len(_results), 1) self.assertEqual(_results[0]['replace_under_here']['context_value'], 42)
def test_replace_context_nested_list(self): _cfg = deepcopy(self.BASE_CFG) _cfg['templates'][0].update({ 'replace_under_here': { 'context_nested_list': [[{ 'inner_key': ContextValue('namespace[key]') }]], } }) _results = self._run_palisade(config=_cfg) self.assertEqual(len(_results), 1) self.assertEqual( _results[0]['replace_under_here']['context_nested_list'][0][0] ['inner_key'], self.BASE_CFG['expansions']['namespace'][0]['key'])
def get_config(directory_name, channel, sample_names, quantities, split_quantity, basename_fullsim, basename_noNP, file_suffix_fullsim, file_suffix_noNP, output_format, info_labels, upper_label): """ function to create configurations for the plots :param directory_name: Name of the analysis which produced the histograms or identifying prefix in the ROOT file :param channel: Lepton channel of the corresponding data :param sample_names: Name of the samples :param quantities: Quantities to plot :param split_quantities: Quantities for which the data was selected and ordered :param basename_fullsim: prefix for (path to) ROOT files containing histograms from full simulation :param basename_noNP: prefix for (path to) ROOT files containing histograms from simulation with turned-off non-perturbative part :param file_suffix_fullsim: suffix of ROOT files containing histograms from full simulation :param file_suffix_noNP: suffix of ROOT files containing histograms with turned-off non-perturbative part :param output_format: format string indicating full path to output plot :param info_labels: labels indicating additional information, shown as annotations in plot :param upper_label: label to use in upper-right corner of plot :return: Karma config dictionary for plotting in the format described in the [Palisade user doc](https://karma-hep.readthedocs.io/en/latest/parts/palisade/user_guide.html) """ # raise exception if samples specified are unknown _unknown_quantities = set(sample_names).difference( LOOKUP_SAMPLE_EXPANSION.keys()) if _unknown_quantities: raise ValueError("Unknown samples: {}".format( ', '.join(_unknown_quantities))) # raise exception if quantities specified are unknown _unknown_quantities = set(quantities).difference( _q['name'] for _q in QUANTITIES['global'].values()) if _unknown_quantities: raise ValueError("Unknown quantities: {}".format( ', '.join(_unknown_quantities))) # -- construct list of input files and correction level expansion dicts _input_files = dict() for _sample in sample_names: # fullsim: with non-perturbative parts _input_file_withNP = os.path.join( "{BASENAME_FULLSIM}".format(BASENAME_FULLSIM=basename_fullsim), "{SAMPLE_NAME}{SUFFIX_FULLSIM}.root".format( SUFFIX_FULLSIM="_" + str(file_suffix_fullsim) if str(file_suffix_fullsim) else "", SAMPLE_NAME=_sample)) # raise exception if inputfile doesn't exist if not os.path.exists(_input_file_withNP): raise ValueError("Couldn't find inputfile {} for sample {}".format( _input_file_withNP, _sample)) _input_files['withNP_{}'.format(_sample)] = _input_file_withNP # NP off: non-perturbative parts of simulation turned-off _input_file_noNP = os.path.join( "{BASENAME_NONP}".format(BASENAME_NONP=basename_noNP), "{SAMPLE_NAME}{SUFFIX_NONP}.root".format(SUFFIX_NONP="_" + str(file_suffix_noNP), SAMPLE_NAME=_sample)) # raise exception if inputfile doesn't exist if not os.path.exists(_input_file_noNP): raise ValueError("Couldn't find inputfile {} for sample {}".format( _input_file_noNP, _sample)) _input_files['noNP_{}'.format(_sample)] = _input_file_noNP # -- expansions # quantities to plot _expansions = { 'quantity': [ _q_dict for _q_dict in EXPANSIONS['quantity'] if _q_dict['name'] in quantities ] } # ystar-yboost-bins if split_quantity == 'ystar': _expansions.update({ 'split': [ dict(name=_k, label=r"${}\leq y^{{*}}<{}$".format( _v['ystar'][0], _v['ystar'][1])) for _k, _v in SPLITTINGS['ystar'].iteritems() ] }) elif split_quantity == 'yboost': _expansions.update({ 'split': [ dict(name=_k, label=r"${}\leq y_{{\mathrm{{b}}}}<{}$".format( _v['yboost'][0], _v['yboost'][1])) for _k, _v in SPLITTINGS['zpt'].iteritems() ] }) elif split_quantity is None: _expansions['split'] = [dict(name='', label=r"incl.")] elif split_quantity == 'ystar*yboost': _expansions.update({ 'split': [ dict(name=_k1 + _k2, label=(r"${}\leq y_{{\mathrm{{b}}}}<{}$".format( _v2['yboost'][0], _v2['yboost'][1]) + r", ${}\leq y^{{*}}<{}$".format( _v1['ystar'][0], _v1['ystar'][1]))) for _k1, _v1 in SPLITTINGS['ystar'].iteritems() for _k2, _v2 in SPLITTINGS['yboost'].iteritems() if _v1['ystar'][0] + _v2['yboost'][0] <= 2 ] }) else: raise ValueError( 'Expansions not implemented for split quantity {}!'.format( split_quantity)) # append '[name]' to format keys that correspond to above expansion keys output_format = output_format.format( channel=channel, # get other possible replacements from expansions definition **{ _expansion_key: "{{{0}[name]}}".format(_expansion_key) for _expansion_key in _expansions.keys() }) # check which samples are requested with their identifier or sample name _sample_dicts = [LOOKUP_SAMPLE_EXPANSION[_s] for _s in sample_names] return { 'input_files': _input_files, 'figures': [ { 'filename': output_format, 'dump_yaml': True, 'figsize': (6, 6), 'subplots': [ # NP-Corr = Ratio withNP/noNP dict(expression="({})/({})".format( build_expression("withNP", _s.get('name'), directory_name, "{quantity[name]}"), build_expression("noNP", _s.get('name'), directory_name, "{quantity[name]}")), label=_s.get('label', _s.get('name')), plot_method='errorbar', color=_s.get('style', dict()).get('color', 'k'), marker=_s.get('style', dict()).get('marker', 'd'), marker_style="full", pad=0) for _s in _sample_dicts ] + [ # Ratio to first sample dict(expression="({})/({})".format( "({})/({})".format( build_expression("withNP", _s.get('name'), directory_name, "{quantity[name]}"), build_expression( "noNP", _s.get('name'), directory_name, "{quantity[name]}")), "({})/({})".format( build_expression( "withNP", _sample_dicts[0].get('name'), directory_name, "{quantity[name]}"), build_expression( "noNP", _sample_dicts[0].get('name'), directory_name, "{quantity[name]}"))), label=None, plot_method='errorbar', color=_s.get('style', dict()).get('color', 'k'), marker=_s.get('style', dict()).get('marker', 'd'), marker_style="full", pad=1) for _s in _sample_dicts ], 'pad_spec': { 'right': 0.95, 'bottom': 0.15, 'top': 0.925, 'hspace': 0.075, }, 'pads': [ # top pad { 'height_share': 3, 'x_range': None, 'x_scale': '{quantity[scale]}', 'y_label': 'non-pert. corr.', # 'y_range' : (1e-3, 1e9), 'axvlines': [ dict(values=ContextValue( 'quantity[expected_values]')) ], 'x_ticklabels': [], 'y_scale': 'linear', 'legend_kwargs': dict(loc='upper right'), }, # ratio pad { 'height_share': 1, 'x_label': '{quantity[label]}', 'x_range': None, 'x_scale': '{quantity[scale]}', 'y_label': 'Ratio', 'y_range': (0.8, 1.2), 'axhlines': [dict(values=[1.0])], 'axvlines': [ dict(values=ContextValue( 'quantity[expected_values]')) ], 'y_scale': 'linear', 'legend_kwargs': dict(loc='upper right'), }, ], 'texts': [ dict(TEXTS['bold_label'], xy=(0, 0), xycoords='axes fraction', xytext=(15, 15), textcoords='offset points', text="{split[label]}"), TEXTS["CMS-in-plot-v2"], TEXTS["Preliminary-in-plot-v2"], TEXTS["Z{}".format(channel)], dict(TEXTS['upper_label'], text=upper_label if upper_label is not None else ""), ] + [ # labels with other information dict(text=_label, xy=(0, 0), xycoords='axes fraction', xytext=(15, 35 + 15 * _i_label), textcoords='offset points') for _i_label, _label in enumerate(reversed(info_labels)) ], }, ], 'expansions': _expansions }
def test_context_value_nested(self): _cval = ContextValue( String("namespace[{}]").format( ContextValue('namespace[meta_key]'))) self.assertEquals(_cval.eval(self._context), self._context['namespace']['key_1'])
def test_context_value_expression(self): _cval = ContextValue('namespace[key_1]') + ContextValue( 'namespace[key_2]') self.assertEquals( _cval.eval(self._context), self._context['namespace']['key_1'] + self._context['namespace']['key_2'])
def test_context_value_simple(self): _cval = ContextValue('namespace[key_1]') self.assertEquals(_cval.eval(self._context), self._context['namespace']['key_1'])