diff --git a/.flake8 b/.flake8 index 8521cd87..6359511f 100644 --- a/.flake8 +++ b/.flake8 @@ -1,5 +1,5 @@ [flake8] -ignore = W391, W503 +ignore = W391, W503, E712 max-line-length = 88 extend-ignore = E203, E704, E266 exclude = menu_tools/object_performance/quality_obj.py,menu_tools/**/test_*.py diff --git a/README.md b/README.md index d22aee6e..82170b99 100644 --- a/README.md +++ b/README.md @@ -6,20 +6,28 @@ trigger turn-on curves, and scalings for the assessment of the physics performance of the CMS Phase-2 L1 Menu. For further instructions on how to run the tools, see the `docs`. + Some documentation can also be found in the [wiki](https://github.com/cms-l1-dpg/Phase2-L1MenuTools/wiki). ## Setup + These tools are expected to be used primarily on lxplus. + To clone the repository run + + ```bash + git clone git@github.com:cms-l1-dpg/Phase2-L1MenuTools.git + ``` + A standard venv with Python3.11 can be created on lxplus via `python3.11 -m venv ` and all necessary - dependencies installed via `pip install .`: + dependencies installed via `pip install -e .`: ```bash - python3.11 -m venv pyenv + python3.11 -m venv source /bin/activate - pip install . + pip install -e . ``` - **ATTENTION:** Whenever you pull changes you need to `pip install . --upgrade` + **ATTENTION:** If you do not use the `-e` flag (editable), you will `pip install . --upgrade` whenever you pull changes. You can then execute the tools via diff --git a/docs/development.md b/docs/development.md index f0533378..d45033a7 100644 --- a/docs/development.md +++ b/docs/development.md @@ -4,10 +4,10 @@ Poetry is used as a backend for packaging and for dependency management. To set up a working development environment, create a virtual environment and install the `poetry` python package. -Then install all develpoment dependencies: +Then `poetry install` installs all develpoment dependencies: ```bash -python@3.11 -m venv +python3.11 -m venv source /bin/activate pip install poetry poetry install @@ -24,7 +24,7 @@ pytest -vv ``` to run all tests. The `-vv` option is optional and can be omitted. -For some of the tests the presence of the V29 caching files is required. +For some of the tests the presence of the `V29` caching files is required. ## Code Formatting and Linting diff --git a/docs/object-performance.md b/docs/object-performance.md index dfacfec9..d7332051 100644 --- a/docs/object-performance.md +++ b/docs/object-performance.md @@ -2,13 +2,16 @@ The object performance tools allow the user to produce matching efficiency, turn-on curves, and scaling plots for - the various L1 objects under test. + the various L1 objects. The definition of each object to be tested is detailed in this [TWiki page](https://twiki.cern.ch/twiki/bin/view/CMS/PhaseIIL1TriggerMenuTools). - A detailed description of each step, together with instructions on how to set up the configuration files for the cache and plotting steps, is given in [the Wiki pages](https://github.com/cms-l1-dpg/Phase2-L1MenuTools/wiki). + A detailed description of each step, together with instructions on how to set up the + configuration files for the cache and plotting steps, is given in + [the Wiki pages](https://github.com/cms-l1-dpg/Phase2-L1MenuTools/wiki). - The following presents the commands to be run to produce the standard set of validation plots (cf. [these slides](https://twiki.cern.ch/twiki/pub/CMS/PhaseIIL1TriggerMenuTools/Phase2Menu_validation123x-3.pdf)). + The following presents the commands to be run to produce the standard set of validation + plots (cf. [these slides](https://twiki.cern.ch/twiki/pub/CMS/PhaseIIL1TriggerMenuTools/Phase2Menu_validation123x-3.pdf)). ## Table of content @@ -17,6 +20,10 @@ * [Reference config files](#reference-config-files) ## Caching the NTuple trees + + **Note:** When running on lxplus, usually the cache files will already + be present in the symliked directory included with the repo. + In order to run the next steps, the object `TTrees` from the L1NTuples need to be cached as `awkward` arrays saved into `.parquet` files. @@ -27,9 +34,11 @@ ``` An example for a caching config can be found at `configs/V29/caching.yaml`. - The output of this step is saved to `cache`. The repository by default includes a symlink to a directory which should already contain most of the desireable object caches. - This step needs to be run only once per configuration (unless changes in the input L1 ntuples occur) and the `.parquet` files generated by the code can be used for all the subsequent steps of the workflow, - without having to open the `.root` files and load the objects every time the framework is run. + The output of this step is saved to `cache`. + This step needs to be run only once per configuration (unless changes in the input + L1 ntuples occur) and the `.parquet` files generated by the code can be + used for all the subsequent steps of the workflow, without having to open the + `.root` files and load the objects every time the framework is run. ## Efficiency and Scalings To produce matching efficiencies, turn-on curves, and L1 scalings run @@ -47,9 +56,13 @@ Default configuration files are contained in `configs` and should be used as templates for custom configurations. - The points used for the calculation of the scalings correspond to the list of threshold cuts defined in [`scaling_thresholds`](https://github.com/cms-l1-dpg/Phase2-L1MenuTools/blob/main/configs/scaling_thresholds.yaml). + The points used for the calculation of the scalings correspond to the list of + threshold cuts defined in + [`scaling_thresholds`](https://github.com/cms-l1-dpg/Phase2-L1MenuTools/blob/main/configs/scaling_thresholds.yaml). - The outputs will be written to the `outputs/` directory, where `` is the version of the ntuples used for the plots as specified in the `.yaml` config file (more details on the config file are given below). + The outputs will be written to the `outputs/` directory, + where `` is the version of the ntuples used for the plots as specified + in the `.yaml` config file (more details on the config file are given below). In the current version of the code, the plots will be saved in three folders under `outputs/`: * `distributions`: plots of the distributions (histograms) used to compute the efficiencies. For each efficiency curve plotted, these plots depict the distributions used for as numberator and denumerator in the computation of the efficiencies. diff --git a/docs/objects.md b/docs/objects.md index e0ad8508..b3bf7645 100644 --- a/docs/objects.md +++ b/docs/objects.md @@ -7,7 +7,7 @@ centrally, by default at configs//objects ``` -All objects found in yaml files in this directory will be found by +All objects defined in yaml files in this directory will be found by the code. ## Object configuration diff --git a/menu_tools/object_performance/plotter.py b/menu_tools/object_performance/plotter.py index bf37c81b..808a6257 100755 --- a/menu_tools/object_performance/plotter.py +++ b/menu_tools/object_performance/plotter.py @@ -5,6 +5,7 @@ import warnings import yaml +import matplotlib as mpl import matplotlib.pyplot as plt import mplhep as hep import numpy as np @@ -17,6 +18,19 @@ from menu_tools.utils.objects import Object +colors = [ + "#3f90da", + "#ffa90e", + "#bd1f01", + "#94a4a2", + "#832db6", + "#a96b59", + "#e76300", + "#b9ac70", + "#717581", + "#92dadd", +] +mpl.rcParams["axes.prop_cycle"] = mpl.cycler(color=colors) plt.style.use(hep.style.CMS) @@ -170,8 +184,12 @@ def _plot_efficiency_curve(self): # Save figure plot_fname = f"{self.plot_name}_{self.threshold}_{self.version}" - plt.savefig(os.path.join(self._outdir_turnons, f"{plot_fname}.png")) - plt.savefig(os.path.join(self._outdir_turnons, f"{plot_fname}.pdf")) + plt.savefig( + os.path.join(self._outdir_turnons, f"{plot_fname}.png"), bbox_inches="tight" + ) + plt.savefig( + os.path.join(self._outdir_turnons, f"{plot_fname}.pdf"), bbox_inches="tight" + ) self._save_json(os.path.join(self._outdir_turnons, f"{plot_fname}.json")) # Save config @@ -210,7 +228,9 @@ def _plot_iso_vs_efficiency_curve(self): # Save figure plot_fname = f"{self.plot_name}_{self.threshold}_{self.version}" plt.savefig(os.path.join(self._outdir_turnons, f"{plot_fname}.png")) - plt.savefig(os.path.join(self._outdir_turnons, f"{plot_fname}.pdf")) + plt.savefig( + os.path.join(self._outdir_turnons, f"{plot_fname}.pdf"), bbox_inches="tight" + ) self._save_json(os.path.join(self._outdir_turnons, f"{plot_fname}.json")) # Save config @@ -243,11 +263,19 @@ def _plot_raw_counts(self): xbins, ref_hist[0], where="mid", - label="ref: " + obj_key, - ls="--", - color="k", + ls="-.", ) + # mock plot outside of visible range to create legend entry for ref + ax.plot( + [xbins[0] - 100, xbins[0] - 99], + [0, 0], + label="reference object", + color="black", + linestyle="-.", + ) + plt.gca().set_prop_cycle(None) + for obj_key, gen_hist_trig in self.turnon_collection.hists.items(): if obj_key == "ref": continue @@ -263,11 +291,14 @@ def _plot_raw_counts(self): **err_kwargs, ) - self._style_plot(fig, ax) + self._style_plot(fig, ax, legend_loc="best") # Save figure plot_fname = f"{self.plot_name}_{self.threshold}_dist_{self.version}" plt.savefig(os.path.join(self._outdir_distributions, f"{plot_fname}.png")) - plt.savefig(os.path.join(self._outdir_distributions, f"{plot_fname}.pdf")) + plt.savefig( + os.path.join(self._outdir_distributions, f"{plot_fname}.pdf"), + bbox_inches="tight", + ) plt.close() @@ -408,7 +439,7 @@ def plot(self): f"{self.plot_name}_{self.version}", ) plt.savefig(f"{plot_fname}.png") - plt.savefig(f"{plot_fname}.pdf") + plt.savefig(f"{plot_fname}.pdf", bbox_inches="tight") self._save_json(f"{plot_fname}.json") ## save config diff --git a/menu_tools/rate_plots/plotter.py b/menu_tools/rate_plots/plotter.py index db0a15da..1d23d8c8 100644 --- a/menu_tools/rate_plots/plotter.py +++ b/menu_tools/rate_plots/plotter.py @@ -3,6 +3,7 @@ import json import awkward as ak +import matplotlib as mpl import matplotlib.pyplot as plt import mplhep as hep import numpy as np @@ -14,6 +15,19 @@ from menu_tools.utils.objects import Object from menu_tools.rate_plots.config import RatePlotConfig +colors = [ + "#3f90da", + "#ffa90e", + "#bd1f01", + "#94a4a2", + "#832db6", + "#a96b59", + "#e76300", + "#b9ac70", + "#717581", + "#92dadd", +] +mpl.rcParams["axes.prop_cycle"] = mpl.cycler(color=colors) plt.style.use(hep.style.CMS) @@ -97,7 +111,7 @@ def _plot_single_version_rate_curves(self): ) print("Saving to ", fname) plt.savefig(fname + ".png") - plt.savefig(fname + ".pdf") + plt.savefig(fname + ".pdf", bbox_inches="tight") with open(fname + ".json", "w") as outfile: outfile.write(json.dumps(plot_dict, indent=4)) @@ -152,7 +166,7 @@ def _plot_version_comparsion_rate_curves(self): self._outdir, f"{v1}-vs-{v2}_{self._online_offline}_{self.cfg.plot_name}" ) plt.savefig(fname + ".png") - plt.savefig(fname + ".pdf") + plt.savefig(fname + ".pdf", bbox_inches="tight") plt.close() diff --git a/menu_tools/rate_table/menu_table.py b/menu_tools/rate_table/menu_table.py index 7ce69057..7b6ac832 100644 --- a/menu_tools/rate_table/menu_table.py +++ b/menu_tools/rate_table/menu_table.py @@ -124,7 +124,6 @@ def _load_cached_arrays(self, object_name: str) -> ak.Array: # instead of a single number. if isinstance(arr[0], ak.highlevel.Record): arr = ak.zip({field: [[k] for k in arr[field]] for field in arr.fields}) - # arr = ak.zip({field: np.expand_dims(arr[field], 0) for field in arr.fields}) if "eta" in arr.fields: arr = ak.with_name(arr, "Momentum4D") diff --git a/menu_tools/utils/compare_plots.py b/menu_tools/utils/compare_plots.py index 5e8b876d..51fbbaba 100644 --- a/menu_tools/utils/compare_plots.py +++ b/menu_tools/utils/compare_plots.py @@ -155,12 +155,12 @@ def comp_plots( d_p2 = dict(zip(plots[1]["xbins"], plots[1]["efficiency"])) # add 100% eff line - # axs[0].axhline(1,ls = ":", alpha = 0.5, c = "k") + # axs[0].axhline(1,ls = ":", alpha = 0.5, c = "k") df_p1 = pd.Series(d_p1) df_p2 = pd.Series(d_p2) - # ax = axs[1] + # ax = axs[1] if (df_p1.sum() != 0) and (df_p1.sum() != 0): diff = df_p1 - df_p2 @@ -168,16 +168,7 @@ def comp_plots( diff /= df_p2 label = p1["label"].split(",")[0] - diff.plot( - ax=axs[1], color=color, label=label - ) # , marker = ".", color = color) - # axs[1].errorbar( - # p1["xbins"],df_p1 - df_p2, - # yerr = np.hypot(plots[0]["efficiency_err"], plots[1]["efficiency_err"]), - # # label = label, marker = ".", color = color, - # label = label, ls = lss[i], color = color, mfc="none" if i == 1 else color, - # **(p1["err_kwargs"]) - # ) + diff.plot(ax=axs[1], color=color, label=label) if ptype == "turnon": if len(plots[0]["efficiency_err"][0]) != len( plots[1]["efficiency_err"][0] @@ -192,7 +183,7 @@ def comp_plots( diff.index, diff.values - y_err, diff.values + y_err, - # label = label, + # label = label, alpha=0.3, color=color, )