The mutools package is a consolidated collection of often-reused functions that I have developed over time for various projects. It serves as a personal utility library to enhance organization and efficiency in coding tasks.
- Python >= 3.9
Clone the repository and install in editable mode:
git clone <repo-url>
cd mutools
uv syncTo include development dependencies (e.g. ruff):
uv sync --group devClone the repository, create a virtual environment, and install in editable mode:
git clone <repo-url>
cd mutools
python -m venv .venv
source .venv/bin/activate
pip install -e .run(config) dispatches a TOML-driven batch of plots. The config can be
a pre-parsed dict, a raw TOML string, or a path to a .toml file.
import mutools.plotting as mp
mp.run("plots.toml")A minimal TOML configuration looks like:
[general]
input = "output.root"
code_version = "v1.0"
selection_version = "v2.3"
subchannels = ["CC QE", "CC Res", "NC"]
savefig = true
output = "figures/"
# Integer keys map detector/channel indices to display labels.
# channel_label is shown on a new line beneath detector_label in the legend.
[general.detectors]
1 = "SBND"
2 = "ICARUS"
[general.channels]
0 = "CC Inclusive"
1 = "NC"
[[plot]]
type = "histogram"
variable = 0
channel = 0
detectors = [1, 2]
xlabel = "Reconstructed energy [GeV]"
ylabel = "Events / bin"
[[plot]]
type = "uncertainty"
channel = 0
detectors = [1, 2]
tags = ["flux", "xsec", "detector"]
xlabel = "Reconstructed energy [GeV]"The [general.channels] block is optional. When present, the label for
plot.channel is looked up automatically and passed as channel_label to
the plotting function.
Individual plot functions are also available directly:
from mutools.plotting.profit import histogram, uncertainty, overlay, ProfitPlotData
data = ProfitPlotData("output.root")
histogram(
data,
variable=0, detector=1, channel=0,
xlabel="Reconstructed energy [GeV]",
ylabel="Events / bin",
code_version="v1.0", selection_version="v2.3",
subchannels=["CC QE", "CC Res", "NC"],
detector_label="SBND",
channel_label="CC Inclusive", # optional: shown beneath detector label
colors=["steelblue", "tomato", "C2"], # optional: one per subchannel
)
uncertainty(
data,
detector=1, channel=0,
tags=["flux", "xsec", "detector"],
xlabel="Reconstructed energy [GeV]",
code_version="v1.0", selection_version="v2.3",
detector_label="SBND",
colors=["C3", "C5", "#aabbcc"], # optional: one per tag
)
overlay(
data,
variable=0, detectors=[1, 2], channel=0,
xlabel="Reconstructed energy [GeV]",
ylabel="Events / bin",
code_version="v1.0", selection_version="v2.3",
detector_labels=["SBND", "ICARUS"],
channel_label="CC Inclusive", # optional: shown as legend title
colors=["C1", "C4"], # optional: one per detector
)All three functions accept an optional colors list. Each entry can be
any matplotlib color spec — color cycle indices ("C0", "C3", ...),
named colors ("steelblue"), hex strings, or RGB tuples. When omitted,
the active style's color cycle is used in order.
overlay draws the total CV spectrum for each detector as a step-filled
histogram with a semi-transparent fill and solid edge, making it easy to
compare shapes. A different variable can be assigned to each detector by
passing a list to variable:
overlay(
data,
variable=[0, 1], detectors=[1, 2], channel=0,
...
)In TOML, overlay produces a single figure covering all listed detectors
rather than one figure per detector:
[[plot]]
type = "overlay"
variable = 0 # or variable = [0, 1] for per-detector variables
channel = 0
detectors = [1, 2]
xlabel = "Reconstructed energy [GeV]"
ylabel = "Events / bin"
colors = ["C1", "C4"] # optional: one per detectorratio overlays the pre- and post-fit systematic uncertainty bands for a
numerator/denominator detector pair. The main panel shows the ratio central
value as step lines (black = pre-fit, red = post-fit) with hatched
uncertainty bands. When data ratio points are present in the file they are
overlaid as errorbars, including statistical uncertainties when a
bin_error column is available. A sub-panel shows the error improvement
(post-fit error / pre-fit error) as a step plot.
from mutools.plotting.profit import ratio
ratio(
data,
detector_num=0, detector_den=1, channel=0,
xlabel="Reconstructed neutrino energy [GeV]",
code_version="v1.0", selection_version="v2.3",
detector_num_label="SBND",
detector_den_label="ICARUS",
channel_label="CC Inclusive", # optional: shown beneath pair label
show_data=True, # optional: overlay RATIO_DATA points
ylim=(3.0, 10.0), # optional
rlim=(0.3, 1.1), # optional: sub-panel y range
)In TOML, ratio takes a detector pair rather than a list:
[[plot]]
type = "ratio"
detector_num = 0
detector_den = 1
channel = 0
xlabel = "Reconstructed neutrino energy [GeV]"
show_data = true # optional, default true
xlim = [0.0, 3.0]
ylim = [3.0, 10.0]
rlim = [0.3, 1.1]Detector labels are looked up from [general.detectors] automatically.
The channel label is picked up from [general.channels] when present.
prism_schematic produces a two-panel figure (SBND | ICARUS) showing
the transverse cross-section of each detector overlaid with concentric
rings for the PRISM off-axis angle (OAA) bins.
from mutools.plotting import prism_schematic
fig = prism_schematic() # display in notebook
fig = prism_schematic(n_bins=6, oaa_max=2.0) # uniform binning
fig = prism_schematic(bin_edges=[0.0, 0.4, 0.9, 1.4, 1.7]) # explicit edges
fig = prism_schematic(show_cathode=False) # hide cathode ring
fig = prism_schematic(output="figures/") # save to figures/prism_schematic.pdfAll plotting functions accept an output keyword argument. When provided,
the figure is saved to that directory via the module-level saver object,
which holds persistent settings (DPI, format, bounding-box trimming).
import mutools.plotting as mp
# Configure once — applies everywhere
mp.saver.configure(fmt="png", dpi=300)
# Temporary override for a single block
with mp.saver.settings(fmt="svg", dpi=600):
histogram(data, ..., output="figures/")
# Rasterize histogram bars to eliminate inter-bin seam artefacts in PDF/SVG
mp.saver.configure(rasterized=True)
# Fix y-axis tick label precision for consistent width across frames
mp.saver.configure(ytick_precision=1) # e.g. 1.0 × 10³Supported formats: eps, pdf, png, ps, svg.
The rasterized flag embeds histogram bars as a raster image inside the
vector file, which prevents the thin vertical streaks that PDF/SVG viewers
render between adjacent bins of a stacked histogram.
create_gif stitches an ordered sequence of PNG files into an animated GIF
using imageio.
import mutools.plotting as mp
mp.create_gif(
["frame_0.png", "frame_1.png", "frame_2.png"],
output="figures/animation.gif",
fps=2.0, # frames per second (default: 2.0)
loop=0, # 0 = loop forever (default)
)Only PNG inputs are supported. The output directory is created automatically if it does not exist.
import mutools.plotting as mp
mp.list_styles() # list available style sheets
mp.use_style("rootlike") # apply a style