magnus.plotting

Pre-packaged figures for Mag \(\nu\) s.

Every figure in notebooks/ used to be built by hand: roughly 25–40 lines of gridspec_kw, tick locators, legend keywords and savefig per plot, copy-pasted and lightly varied. This module collapses that into one call per figure while reproducing the same output, so that switching an existing figure over to it leaves the figure unchanged.

Taking stock of the fifty-odd figures showed that most of them are the same figure with different data. Curves against baseline, curves against energy, curves against a mixing angle, and the convergence studies of the matrix-exponential notebook all share one shape: a set of curves plotted against a swept variable, optionally over a short relative-error subpanel. plot_curves() is that shape, and the plot_probability_vs_* helpers are thin wrappers that only preset labels and limits. plot_curves_stacked() is its small-multiples form: the same plot repeated once per case down a shared abscissa, where the comparison is between panels. The genuinely distinct layouts are the profile-plus-probability stack, the bi-probability plane, and the oscillogram.

API conventions

The functions take named arguments for the quantities every figure has (data, labels, limits, scales, ticks, title, legend placement, output path) and explicit pass-through dictionaries for the long tail of Matplotlib settings: legend_kw, grid_kw, savefig_kw, subplots_kw, and per curve any Line2D keyword.

There is deliberately no bare **kwargs on any of these functions. A catch-all signature accepts a misspelled keyword in silence, and this project has already paid for that once: oscprob’s keyword chain forwarded unknown names down several layers before failing somewhere unrecognisable, which is why magnus.oscprob.osc_prob() now raises on stray keys. Here every keyword is either named in the signature – so a typo is a TypeError at the call site – or lands in a dictionary destined for one specific Matplotlib call – so a typo is an error from that call, naming the offending key. Nothing is swallowed.

Styling that is global (fonts, tick sizes and directions, LaTeX rendering) belongs to notebooks/matplotlibrc and is deliberately not set here; the defaults below cover only what the notebooks were overriding per figure.

All functions return (fig, ax) so that the caller can keep customising: fig for figure-level work and saving, ax for anything Matplotlib exposes on an axes.

Requirements

Matplotlib ships with Magνs, so this module is available in any installation and needs nothing extra.

Added in version 1.0.0.

Attributes

HOUSE_FIGSIZE

Default figure size, in inches, used throughout the notebooks.

HOUSE_RESIDUAL_HEIGHT

Height of the relative-error subpanel, relative to the main panel.

HOUSE_LEGEND_KW

Legend keywords repeated verbatim on essentially every notebook figure.

HOUSE_GRID_KW

Grid keywords used by the notebooks.

HOUSE_SAVEFIG_KW

Default savefig() keywords; figures go to ../fig/ as PDF.

Exceptions

MatplotlibNotFoundError

Raised when magnus.plotting is used without Matplotlib installed.

Functions

prob_label(→ str)

Return the LaTeX label for an oscillation probability.

plot_curves(x, curves, *[, xlabel, ylabel, title, ...])

Plot a set of curves against a swept variable, with an optional error subpanel.

plot_curves_stacked(x, panels, *[, xlabel, ylabel, ...])

Plot small multiples: one panel per case, stacked over a shared abscissa.

plot_probability_vs_baseline(distances, curves, *[, ...])

Plot oscillation probabilities against baseline.

plot_probability_vs_energy(energies, curves, *[, ...])

Plot oscillation probabilities against neutrino energy.

plot_probability_with_profile(x, profiles, panels, *)

Stack a matter-density panel above one or more probability panels.

plot_probability_with_average(x, probabilities, ...[, ...])

Overlay phase-averaged probabilities on the oscillating ones.

plot_biprobability(prob_nu, prob_nubar, *[, labels, ...])

Plot neutrino against antineutrino appearance probability.

plot_oscillogram(costhz, log10_energy, probability, *)

Plot an oscillogram: probability over zenith angle and energy.

Module Contents

exception magnus.plotting.MatplotlibNotFoundError[source]

Bases: ImportError

Raised when magnus.plotting is used without Matplotlib installed.

Added in version 1.0.0.

magnus.plotting.HOUSE_FIGSIZE: Tuple[float, float] = (18.0, 9.0)[source]

Default figure size, in inches, used throughout the notebooks.

Added in version 1.0.0.

magnus.plotting.HOUSE_RESIDUAL_HEIGHT: float = 0.3[source]

Height of the relative-error subpanel, relative to the main panel.

Added in version 1.0.0.

magnus.plotting.HOUSE_LEGEND_KW: Dict[str, Any][source]

Legend keywords repeated verbatim on essentially every notebook figure.

Added in version 1.0.0.

magnus.plotting.HOUSE_GRID_KW: Dict[str, Any][source]

Grid keywords used by the notebooks.

Added in version 1.0.0.

magnus.plotting.HOUSE_SAVEFIG_KW: Dict[str, Any][source]

Default savefig() keywords; figures go to ../fig/ as PDF.

Added in version 1.0.0.

magnus.plotting.prob_label(nu_i: int, nu_f: int, nubar: bool | None = False) str[source]

Return the LaTeX label for an oscillation probability.

A prob_label helper was defined separately in several notebooks, with hand-written if/elif chains covering only the three active flavours. This version also covers the sterile states, so the sterile-neutrino notebook can use it too.

Added in version 1.0.0.

Parameters:
  • nu_i (int) – Initial flavour, as one of the magnus.globaldefs constants NUE, NUMU, NUTAU, NUS1, NUS2.

  • nu_f (int) – Final flavour, same encoding.

  • nubar (bool, optional) – If True, label the antineutrino channel. Default is False.

Returns:

A LaTeX string such as '$P_{\\nu_e \\to \\nu_\\mu}$'.

Return type:

str

Raises:

ValueError – If either flavour index is not one of the known values.

Examples

import magnus.globaldefs as gd
from magnus.plotting import prob_label

print(prob_label(gd.NUMU, gd.NUE))
print(prob_label(gd.NUMU, gd.NUE, nubar=True))
$P_{\nu_\mu \to \nu_e}$
$P_{\bar{\nu}_\mu \to \bar{\nu}_e}$
magnus.plotting.plot_curves(x: Sequence[float], curves: Sequence[Sequence[float] | Dict[str, Any]], *, xlabel: str | None = None, ylabel: str | None = None, title: str | None = None, xlim: Tuple[float, float] | None = None, ylim: Tuple[float, float] | None = None, xscale: str = 'linear', yscale: str = 'linear', xmajor: float | None = None, xminor: float | None = None, ymajor: float | None = None, yminor: float | None = None, residual: Sequence[float] | None = None, residual_label: str | None = None, residual_ylim: Tuple[float, float] | None = None, residual_ymajor: float | None = None, residual_yminor: float | None = None, residual_height: float = HOUSE_RESIDUAL_HEIGHT, residual_kw: Dict[str, Any] | None = None, annotations: Sequence[Dict[str, Any]] | None = None, legend: bool = True, legend_title: str | None = None, legend_loc: str | None = None, legend_kw: Dict[str, Any] | None = None, grid: bool = False, grid_kw: Dict[str, Any] | None = None, ylabel_labelpad: float = 25.0, title_fontsize: float = 20.0, figsize: Tuple[float, float] = HOUSE_FIGSIZE, subplots_kw: Dict[str, Any] | None = None, savefig: str | None = None, savefig_kw: Dict[str, Any] | None = None, tight_layout: bool = True)[source]

Plot a set of curves against a swept variable, with an optional error subpanel.

This is the workhorse: most notebook figures are an instance of it. The plot_probability_vs_baseline and plot_probability_vs_energy wrappers differ from it only in their preset labels and limits, and the convergence studies of the matrix-exponential notebook use it directly with a slab count or grid size on the abscissa.

Added in version 1.0.0.

Parameters:
  • x (sequence of float) – Abscissa, shared by every curve and by the residual panel.

  • curves (sequence) – One entry per curve. An entry is either a bare ordinate array, or a dict carrying the ordinate under 'y' plus any Line2D keyword (label, color, ls, lw, …). Entries without an explicit colour take the 'C0', 'C1', … cycle in order.

  • xlabel (str, optional) – Axis labels and title. ylabel goes on the main panel.

  • ylabel (str, optional) – Axis labels and title. ylabel goes on the main panel.

  • title (str, optional) – Axis labels and title. ylabel goes on the main panel.

  • xlim (tuple of float, optional) – Axis limits. xlim is applied to the residual panel too, so the two panels stay aligned.

  • ylim (tuple of float, optional) – Axis limits. xlim is applied to the residual panel too, so the two panels stay aligned.

  • xscale (str, optional) – Matplotlib axis scales, e.g. 'log'. Default is 'linear'. xscale is applied to the residual panel as well.

  • yscale (str, optional) – Matplotlib axis scales, e.g. 'log'. Default is 'linear'. xscale is applied to the residual panel as well.

  • xmajor (float, optional) – Major/minor tick spacings for the main panel.

  • xminor (float, optional) – Major/minor tick spacings for the main panel.

  • ymajor (float, optional) – Major/minor tick spacings for the main panel.

  • yminor (float, optional) – Major/minor tick spacings for the main panel.

  • residual (sequence of float, optional) – If given, a short subpanel is added below the main panel and this is plotted in it – typically a relative error against a reference curve. The main panel’s tick labels are then suppressed, as in the notebooks.

  • residual_label (str, optional) – Ordinate label for the residual subpanel.

  • residual_ylim (tuple of float, optional) – Ordinate limits for the residual subpanel.

  • residual_ymajor (float, optional) – Tick spacings for the residual subpanel.

  • residual_yminor (float, optional) – Tick spacings for the residual subpanel.

  • residual_height (float, optional) – Height of the residual subpanel relative to the main panel. Default is HOUSE_RESIDUAL_HEIGHT.

  • residual_kw (dict, optional) – Extra Line2D keywords for the residual curve. Defaults to a thin black solid line.

  • annotations (sequence of dict, optional) – Text placed on the main panel. Each entry needs 'text' and 'xy' (axes fractions by default) and may carry any other annotate() keyword. Used by the BSM notebooks to record the parameter values a figure was made with.

  • legend (bool, optional) – Whether to draw a legend. Default is True; it is drawn only if at least one curve carries a label.

  • legend_title (str, optional) – Legend title.

  • legend_loc (str, optional) – Legend location.

  • legend_kw (dict, optional) – Extra keywords merged over HOUSE_LEGEND_KW and forwarded to legend().

  • grid (bool, optional) – Whether to draw a grid. Default is False.

  • grid_kw (dict, optional) – Extra keywords merged over HOUSE_GRID_KW.

  • ylabel_labelpad (float, optional) – Padding of the main ordinate label. Default is 25.0.

  • title_fontsize (float, optional) – Title font size. Default is 20.0.

  • figsize (tuple of float, optional) – Figure size in inches. Default is HOUSE_FIGSIZE.

  • subplots_kw (dict, optional) – Extra keywords for subplots().

  • savefig (str, optional) – If given, the figure is written here.

  • savefig_kw (dict, optional) – Extra keywords merged over HOUSE_SAVEFIG_KW.

  • tight_layout (bool, optional) – Whether to call tight_layout(). Default is True.

Returns:

  • fig (matplotlib.figure.Figure) – The figure, ready for further customisation or saving.

  • ax (matplotlib.axes.Axes or numpy.ndarray of Axes) – A single axes when there is no residual panel; an array of two (main, residual) when there is.

Examples

import matplotlib
matplotlib.use('Agg')
import numpy as np
from magnus.plotting import plot_curves

# starts away from zero: the reference appears in a denominator below,
# and sin(0)**2 is exactly 0
L = np.linspace(50.0, 1000.0, 200)
exact = np.sin(L / 200.0) ** 2
approx = exact + 1e-3 * np.cos(L / 50.0)

fig, ax = plot_curves(
    L,
    [dict(y=approx, label='Magnus expansion', color='C1'),
     dict(y=exact, label='Standard formula', color='k', ls='--')],
    xlabel=r'Baseline, $L$ [km]', ylabel='Probability',
    ylim=(0, 1), residual=(approx - exact) / exact,
    residual_label=r'$\epsilon_{\rm rel}$', legend_title='Method',
)
print(len(ax), ax[0].get_ylim())
2 (np.float64(0.0), np.float64(1.0))
magnus.plotting.plot_curves_stacked(x: Sequence[float], panels: Sequence[Sequence[Sequence[float] | Dict[str, Any]]], *, xlabel: str | None = None, ylabel: str | None = None, title: str | None = None, xlim: Tuple[float, float] | None = None, ylim: Tuple[float, float] | None = None, xscale: str = 'linear', yscale: str = 'linear', xmajor: float | None = None, xminor: float | None = None, ymajor: float | None = None, yminor: float | None = None, panel_labels: Sequence[str] | None = None, panel_label_xy: Tuple[float, float] = (0.02, 0.1), panel_label_kw: Dict[str, Any] | None = None, annotations: Sequence[Dict[str, Any]] | None = None, legend: bool = True, legend_panel: int = 0, legend_proxies: Sequence[Dict[str, Any]] | None = None, legend_title: str | None = None, legend_loc: str | None = None, legend_kw: Dict[str, Any] | None = None, grid: bool = False, grid_kw: Dict[str, Any] | None = None, ylabel_kw: Dict[str, Any] | None = None, title_fontsize: float = 23.0, figsize: Tuple[float, float] | None = None, height_ratios: Sequence[float] | None = None, subplots_kw: Dict[str, Any] | None = None, savefig: str | None = None, savefig_kw: Dict[str, Any] | None = None, tight_layout: bool = True)[source]

Plot small multiples: one panel per case, stacked over a shared abscissa.

The layout for “the same quantity, once per configuration” – one panel per detector, per baseline, per zenith angle – where the comparison the reader makes is between panels, so every panel must share limits, scales and tick spacings exactly. Only the bottom panel keeps its tick labels and abscissa label, and the ordinate label is a single figure-level label spanning the stack.

This differs from plot_probability_with_profile(), whose panels show different quantities (a density profile above a probability), and from plot_curves(), whose optional second panel is a relative error rather than another instance of the same plot.

Added in version 1.0.0.

Parameters:
  • x (sequence of float) – Abscissa, shared by every panel.

  • panels (sequence of sequence) – One entry per panel, each a sequence of curves in the form plot_curves() accepts: a bare ordinate array, or a dict carrying the ordinate under 'y' plus any Line2D keyword. Curves without an explicit colour take the 'C0', 'C1', … cycle within their panel, so the n-th curve of every panel matches by default.

  • xlabel (str, optional) – Abscissa label, placed on the bottom panel only.

  • ylabel (str, optional) – Ordinate label. Drawn once for the whole stack with supylabel(), since every panel shows the same quantity. Being figure-level, it takes no labelpad; use ylabel_kw for its placement.

  • title (str, optional) – Title, placed above the top panel.

  • xlim (tuple of float, optional) – Axis limits, applied to every panel.

  • ylim (tuple of float, optional) – Axis limits, applied to every panel.

  • xscale (str, optional) – Matplotlib axis scales, applied to every panel. Default 'linear'.

  • yscale (str, optional) – Matplotlib axis scales, applied to every panel. Default 'linear'.

  • xmajor (float, optional) – Major/minor tick spacings, applied to every panel.

  • xminor (float, optional) – Major/minor tick spacings, applied to every panel.

  • ymajor (float, optional) – Major/minor tick spacings, applied to every panel.

  • yminor (float, optional) – Major/minor tick spacings, applied to every panel.

  • panel_labels (sequence of str, optional) – One caption per panel, annotated inside it – the usual way of saying which case a panel is. Must match the number of panels.

  • panel_label_xy (tuple of float, optional) – Position of those captions, in axes fractions. Default (0.02, 0.10).

  • panel_label_kw (dict, optional) – Extra annotate() keywords for them.

  • annotations (sequence of dict, optional) – Free-form text. Each entry needs 'text' and 'xy', may name a 'panel' (index, default 0), and may carry any other annotate() keyword.

  • legend (bool, optional) – Whether to draw a legend. Default True; drawn only if there is something to put in it.

  • legend_panel (int, optional) – Which panel carries the legend. Default 0.

  • legend_proxies (sequence of dict, optional) – Legend entries that describe a style shared across panels rather than any one curve – e.g. “solid: 3+1, dashed: standard” when the colour varies from panel to panel. Each entry is a set of Line2D keywords including label, drawn as an empty proxy artist. When given, these replace the labels picked up from the curves themselves. This exists because the alternative, and what the notebooks did, is plotting dummy points outside the axis limits to manufacture legend handles.

  • legend_title (str, optional) – Legend title and location.

  • legend_loc (str, optional) – Legend title and location.

  • legend_kw (dict, optional) – Extra keywords merged over HOUSE_LEGEND_KW.

  • grid (bool, optional) – Whether to draw a grid on every panel. Default False.

  • grid_kw (dict, optional) – Extra keywords merged over HOUSE_GRID_KW.

  • ylabel_kw (dict, optional) – Extra keywords for supylabel().

  • title_fontsize (float, optional) – Title font size. Default 23.0.

  • figsize (tuple of float, optional) – Figure size in inches. Defaults to HOUSE_FIGSIZE’s width and half its height per panel, which reproduces the notebooks’ proportions.

  • height_ratios (sequence of float, optional) – Relative panel heights. Default: equal.

  • subplots_kw (dict, optional) – Extra keywords for subplots().

  • savefig (str, optional) – If given, the figure is written here.

  • savefig_kw (dict, optional) – Extra keywords merged over HOUSE_SAVEFIG_KW.

  • tight_layout (bool, optional) – Whether to call tight_layout(). Default True.

Returns:

  • fig (matplotlib.figure.Figure) – The figure, ready for further customisation or saving.

  • ax (numpy.ndarray of Axes) – One axes per panel, top to bottom. Always an array, including for a single panel, so that indexing does not depend on the panel count.

Examples

import matplotlib
matplotlib.use('Agg')
import numpy as np
from magnus.plotting import plot_curves_stacked

E = np.linspace(1.0, 40.0, 200)
cases = [0.5, 1.0, 2.0]
panels = [
    [dict(y=np.sin(k*E/8.0)**2, color=f'C{i}'),
     dict(y=np.sin(k*E/8.0)**2*0.8, color='0.7', ls='--')]
    for i, k in enumerate(cases)
]

fig, ax = plot_curves_stacked(
    E, panels,
    xlabel=r'Neutrino energy, $E_\nu$ [GeV]', ylabel='Probability',
    ylim=(0, 1), xlim=(1.0, 40.0),
    panel_labels=[f'baseline {k:.1f} kton-yr' for k in cases],
    legend_proxies=[dict(label='3+1', color='k', ls='-'),
                    dict(label=r'standard', color='k', ls='--')],
)
print(ax.shape, ax[0].get_xticklabels()[0].get_text() == '')
(3,) True
magnus.plotting.plot_probability_vs_baseline(distances: Sequence[float], curves: Sequence[Sequence[float] | Dict[str, Any]], *, nu_i: int | None = None, nu_f: int | None = None, num_flavors: int | None = None, xlabel: str = 'Baseline, $L$ [km]', ylabel: str | None = None, ylim: Tuple[float, float] = (0.0, 1.0), xscale: str = 'log', ymajor: float | None = 0.1, yminor: float | None = 0.02, **_forbidden: Any)[source]

Plot oscillation probabilities against baseline.

A thin preset over plot_curves(): log abscissa, ordinate on \([0, 1]\) with the notebooks’ tick spacings, and an ordinate label built from the flavour pair.

Added in version 1.0.0.

Parameters:
  • distances (sequence of float) – Baselines [km].

  • curves (sequence) – As in plot_curves().

  • nu_i (int, optional) – Flavour pair, used to build the ordinate label via prob_label() when ylabel is not given.

  • nu_f (int, optional) – Flavour pair, used to build the ordinate label via prob_label() when ylabel is not given.

  • num_flavors (int, optional) – If given, prefixes the ordinate label with 'Two-', 'Three-', 'Four-' or 'Five-neutrino probability'.

  • xlabel (str, optional) – Abscissa label.

  • ylabel (str, optional) – Ordinate label; overrides the one built from the flavour pair.

  • ylim (tuple of float, optional) – Ordinate limits. Default is (0.0, 1.0).

  • xscale (str, optional) – Abscissa scale. Default is 'log'.

  • ymajor (float, optional) – Ordinate tick spacings.

  • yminor (float, optional) – Ordinate tick spacings.

  • **_forbidden – Every remaining keyword of plot_curves() is accepted and forwarded unchanged; unknown names raise TypeError there.

Returns:

  • fig (matplotlib.figure.Figure)

  • ax (matplotlib.axes.Axes or numpy.ndarray of Axes)

Examples

import matplotlib
matplotlib.use('Agg')
import numpy as np
import magnus.globaldefs as gd
from magnus.plotting import plot_probability_vs_baseline

L = np.logspace(1, 5, 200)
P = np.sin(L / 3000.0) ** 2
fig, ax = plot_probability_vs_baseline(
    L, [dict(y=P, label='Magnus expansion')],
    nu_i=gd.NUE, nu_f=gd.NUE, num_flavors=2, xlim=(L[0], L[-1]),
)
print(ax.get_xlabel())
Baseline, $L$ [km]
magnus.plotting.plot_probability_vs_energy(energies: Sequence[float], curves: Sequence[Sequence[float] | Dict[str, Any]], *, nu_i: int | None = None, nu_f: int | None = None, num_flavors: int | None = None, energy_unit: str = 'GeV', xlabel: str | None = None, ylabel: str | None = None, ylim: Tuple[float, float] = (0.0, 1.0), xscale: str = 'log', ymajor: float | None = 0.1, yminor: float | None = 0.02, **_forbidden: Any)[source]

Plot oscillation probabilities against neutrino energy.

The energy counterpart of plot_probability_vs_baseline().

Added in version 1.0.0.

Parameters:
  • energies (sequence of float) – Neutrino energies, in the unit named by energy_unit.

  • curves (sequence) – As in plot_curves().

  • nu_i (int, optional) – Flavour pair for the ordinate label.

  • nu_f (int, optional) – Flavour pair for the ordinate label.

  • num_flavors (int, optional) – Flavour count, for the ordinate label prefix.

  • energy_unit (str, optional) – Unit shown in the abscissa label. Default is 'GeV'.

  • xlabel (str, optional) – Abscissa label; overrides the one built from energy_unit.

  • ylabel (str, optional) – Ordinate label.

  • ylim (tuple of float, optional) – Ordinate limits. Default is (0.0, 1.0).

  • xscale (str, optional) – Abscissa scale. Default is 'log'.

  • ymajor (float, optional) – Ordinate tick spacings.

  • yminor (float, optional) – Ordinate tick spacings.

  • **_forbidden – Forwarded to plot_curves().

Returns:

  • fig (matplotlib.figure.Figure)

  • ax (matplotlib.axes.Axes or numpy.ndarray of Axes)

Examples

import matplotlib
matplotlib.use('Agg')
import numpy as np
import magnus.globaldefs as gd
from magnus.plotting import plot_probability_vs_energy

E = np.logspace(-1, 1, 200)
P = np.cos(1.0 / E) ** 2
fig, ax = plot_probability_vs_energy(
    E, [dict(y=P, label='Magnus expansion')],
    nu_i=gd.NUMU, nu_f=gd.NUE, xlim=(E[0], E[-1]),
)
print(ax.get_xlabel())
Neutrino energy, $E_\nu$ [GeV]
magnus.plotting.plot_probability_with_profile(x: Sequence[float], profiles: Sequence[Sequence[float] | Dict[str, Any]] | None, panels: Sequence[Sequence[Sequence[float] | Dict[str, Any]]], *, xlabel: str = 'Baseline, $L$~[km]', profile_ylabel: str = '$\\frac{N_e}{N_{\\rm Av}}$~[cm$^{-3}$]', panel_ylabels: Sequence[str | None] | None = None, panel_annotations: Sequence[str | None] | None = None, panel_annotation_xy: Tuple[float, float] = (0.02, 0.88), panel_annotation_fontsize: float = 23.0, shared_ylabel: str | None = None, shared_ylabel_labelpad: float = 20.0, title: str | None = None, title_fontsize: float = 23.0, xlim: Tuple[float, float] | None = None, xscale: str = 'log', xmajor: float | None = None, xminor: float | None = None, profile_ylim: Tuple[float, float] | None = None, profile_ymajor: float | None = None, profile_yminor: float | None = None, profile_height: float = 0.4, panel_ylim: Tuple[float, float] | None = (0.0, 1.0), panel_yscale: str = 'linear', panel_ymajor: float | None = 0.1, panel_yminor: float | None = 0.02, legend: bool = True, legend_title: str | None = None, legend_loc: str | None = None, legend_kw: Dict[str, Any] | None = None, legend_on_panel: int = 0, grid: bool = True, grid_kw: Dict[str, Any] | None = None, ylabel_labelpad: float = 25.0, figsize: Tuple[float, float] | None = None, subplots_kw: Dict[str, Any] | None = None, savefig: str | None = None, savefig_kw: Dict[str, Any] | None = None, tight_layout: bool = False)[source]

Stack a matter-density panel above one or more probability panels.

This is the layout of the long-baseline notebook: the electron-density profile along the trajectory on top, then one probability panel per detector or per profile, sharing the abscissa. With a single probability panel it is the profile-plus-probability figure of the introduction and the two-flavour notebooks.

Added in version 1.0.0.

Parameters:
  • x (sequence of float) – Shared abscissa, or, when the panels have different abscissae, the one used by the profile panel. Individual curves may carry their own x.

  • profiles (sequence or None) – Curves for the density panel, in the form plot_curves() takes. A curve may add its own abscissa under 'x'. Pass None (or an empty sequence) to omit the density panel entirely and get a plain stack of probability panels sharing an abscissa – the layout the long-baseline notebook uses for a probability above its energy-smoothed version.

  • panels (sequence of sequence) – One entry per probability panel; each entry is a sequence of curves.

  • xlabel (str, optional) – Abscissa label, placed under the bottom panel.

  • profile_ylabel (str, optional) – Ordinate label of the density panel.

  • panel_ylabels (sequence of str, optional) – Ordinate labels for the probability panels. Entries may be None.

  • panel_annotations (sequence, optional) – Text placed inside each probability panel, one entry per panel, at panel_annotation_xy in axes coordinates. An entry is a string, or a dict with 'text' plus any other annotate() keyword – a bbox, for instance, when the text would otherwise sit over dense curves. Entries may be None. The three-flavour notebook uses this to name the channel each panel shows, rather than repeating it in the ordinate label.

  • panel_annotation_xy (tuple of float, optional) – Position of those annotations, in axes fractions. Default (0.02, 0.88).

  • panel_annotation_fontsize (float, optional) – Their font size. Default is 23.0.

  • shared_ylabel (str, optional) – A single ordinate label spanning the whole stack, drawn on a frameless overlay axes. Use it instead of panel_ylabels when every panel shows the same quantity.

  • shared_ylabel_labelpad (float, optional) – Padding of that shared label. Default is 20.0.

  • title (str, optional) – Title, placed above the density panel.

  • title_fontsize (float, optional) – Title font size. Default is 23.0.

  • xlim (tuple of float, optional) – Shared abscissa limits.

  • xscale (str, optional) – Shared abscissa scale. Default is 'log'.

  • xmajor (float, optional) – Major/minor tick spacings on the shared abscissa. Only meaningful on a linear scale.

  • xminor (float, optional) – Major/minor tick spacings on the shared abscissa. Only meaningful on a linear scale.

  • profile_ylim (tuple of float, optional) – Ordinate limits of the density panel.

  • profile_ymajor (float, optional) – Tick spacings for the density panel.

  • profile_yminor (float, optional) – Tick spacings for the density panel.

  • profile_height (float, optional) – Height of the density panel relative to a probability panel. Default is 0.4.

  • panel_ylim (tuple of float, optional) – Ordinate limits shared by the probability panels. None autoscales.

  • panel_yscale (str, optional) – Ordinate scale shared by the panels, e.g. 'log' when they carry something other than a probability. Default is 'linear'.

  • panel_ymajor (float, optional) – Tick spacings for the probability panels.

  • panel_yminor (float, optional) – Tick spacings for the probability panels.

  • legend (bool, optional) – Whether to draw a legend.

  • legend_title (str, optional) – Legend title.

  • legend_loc (str, optional) – Legend location.

  • legend_kw (dict, optional) – Extra keywords merged over HOUSE_LEGEND_KW.

  • legend_on_panel (int, optional) – Index of the probability panel carrying the legend, or -1 to give every panel its own. Default is 0.

  • grid (bool, optional) – Whether to draw grids. Default is True.

  • grid_kw (dict, optional) – Extra keywords merged over HOUSE_GRID_KW.

  • ylabel_labelpad (float, optional) – Padding of the ordinate labels.

  • figsize (tuple of float, optional) – Figure size. Defaults to (18, 9) for one probability panel, growing by 4.5 inches per extra panel.

  • subplots_kw (dict, optional) – Extra keywords for subplots().

  • savefig (str, optional) – If given, the figure is written here.

  • savefig_kw (dict, optional) – Extra keywords merged over HOUSE_SAVEFIG_KW.

  • tight_layout (bool, optional) – Whether to call tight_layout. Default is False, matching the notebooks, whose explicit subplots_adjust this would override.

Returns:

  • fig (matplotlib.figure.Figure)

  • ax (numpy.ndarray of Axes) – Length 1 + len(panels) with a density panel, which comes first; length len(panels) without one.

Examples

import matplotlib
matplotlib.use('Agg')
import numpy as np
from magnus.plotting import plot_probability_with_profile

L = np.logspace(2, 4, 300)
n_e = 5.0 * np.exp(-L / 5000.0)
P = np.sin(L / 900.0) ** 2

fig, ax = plot_probability_with_profile(
    L, [dict(y=n_e, color='C0')], [[dict(y=P, label='PREM')]],
    xlim=(L[0], L[-1]), profile_ylim=(0, 6),
)
print(len(ax))
2
magnus.plotting.plot_probability_with_average(x: Sequence[float], probabilities: Sequence[float] | Sequence[Sequence[float]], averages: float | Sequence[float] | Sequence[Sequence[float]], *, labels: Sequence[str] | None = None, colors: Sequence[str] | None = None, average_label: str = 'Phase-averaged', oscillating_kw: Dict[str, Any] | None = None, average_kw: Dict[str, Any] | None = None, **_forbidden: Any)[source]

Overlay phase-averaged probabilities on the oscillating ones.

The figure of the averaged-probability notebook: rapidly oscillating curves, each with its decohered limit drawn through it as a dashed line of the same colour – the value magnus.oscprob.osc_prob() returns with average=True.

Several channels are usually shown at once, so the legend carries one entry per channel plus a single entry explaining the dashed style, rather than repeating “averaged” once per curve.

Added in version 1.0.0.

Parameters:
  • x (sequence of float) – Abscissa, typically baseline [km].

  • probabilities (sequence of float or sequence of sequence of float) – One oscillating probability, or several.

  • averages (float or sequence) – The corresponding phase-averaged values: a scalar per curve (broadcast across x), or a full curve each. Must match probabilities in number.

  • labels (sequence of str, optional) – Legend label per channel.

  • colors (sequence of str, optional) – Colour per channel. Defaults to the 'C0', 'C1', … cycle; each average takes its channel’s colour.

  • average_label (str, optional) – Text of the single legend entry explaining the dashed lines.

  • oscillating_kw (dict, optional) – Extra Line2D keywords applied to every oscillating or every averaged curve.

  • average_kw (dict, optional) – Extra Line2D keywords applied to every oscillating or every averaged curve.

  • **_forbidden – Forwarded to plot_probability_vs_baseline().

Returns:

  • fig (matplotlib.figure.Figure)

  • ax (matplotlib.axes.Axes or numpy.ndarray of Axes)

Raises:

ValueError – If the number of averages does not match the number of probabilities.

Examples

import matplotlib
matplotlib.use('Agg')
import numpy as np
from magnus.plotting import plot_probability_with_average

L = np.linspace(1.0, 1000.0, 500)
P = np.sin(L / 7.0) ** 2
fig, ax = plot_probability_with_average(L, P, 0.5, xscale='linear')
print(len(ax.get_lines()))
2
magnus.plotting.plot_biprobability(prob_nu: Sequence[Sequence[float]], prob_nubar: Sequence[Sequence[float]], *, labels: Sequence[str] | None = None, curve_kw: Sequence[Dict[str, Any]] | None = None, markers: Sequence[Dict[str, Any]] | None = None, xlabel: str | None = None, ylabel: str | None = None, title: str | None = None, title_fontsize: float = 20.0, xlim: Tuple[float, float] | None = None, ylim: Tuple[float, float] | None = None, xmajor: float | None = None, xminor: float | None = None, ymajor: float | None = None, yminor: float | None = None, annotations: Sequence[Dict[str, Any]] | None = None, legend: bool = True, legend_title: str = '$\\delta_{\\rm CP}$', legend_loc: str | None = None, legend_kw: Dict[str, Any] | None = None, figsize: Tuple[float, float] = (9.0, 9.0), subplots_kw: Dict[str, Any] | None = None, savefig: str | None = None, savefig_kw: Dict[str, Any] | None = None, tight_layout: bool = False)[source]

Plot neutrino against antineutrino appearance probability.

The bi-probability plane: for each configuration, the locus traced out as \(\delta_{\rm CP}\) runs over \([-\pi, \pi]\), with optional markers at selected phases.

Added in version 1.0.0.

Parameters:
  • prob_nu (sequence of sequence of float) – One entry per curve, each a sequence of probabilities over the same grid of \(\delta_{\rm CP}\) values.

  • prob_nubar (sequence of sequence of float) – One entry per curve, each a sequence of probabilities over the same grid of \(\delta_{\rm CP}\) values.

  • labels (sequence of str, optional) – Legend label per curve.

  • curve_kw (sequence of dict, optional) – Per-curve Line2D keywords.

  • markers (sequence of dict, optional) – Markers at selected phases. Each entry gives its position either as 'index' (a position along the curve) or as 'xy' (an explicit coordinate pair, which is what you have when the marked phases were computed separately from the curve). Optionally 'marker', 'label', 'filled' and 'curve' (which curve it belongs to, default all).

  • xlabel (str, optional) – Axis labels. Default to the \(\nu_\mu \to \nu_e\) pair.

  • ylabel (str, optional) – Axis labels. Default to the \(\nu_\mu \to \nu_e\) pair.

  • title (str, optional) – Title.

  • title_fontsize (float, optional) – Title font size.

  • xlim (tuple of float, optional) – Axis limits.

  • ylim (tuple of float, optional) – Axis limits.

  • xmajor (float, optional) – Tick spacings.

  • xminor (float, optional) – Tick spacings.

  • ymajor (float, optional) – Tick spacings.

  • yminor (float, optional) – Tick spacings.

  • annotations (sequence of dict, optional) – Passed to annotate(); each entry needs 'text' and 'xy', and may carry any other annotate keyword. Coordinates are axes fractions.

  • legend (bool, optional) – Whether to draw a legend.

  • legend_title (str, optional) – Legend title. Default is '$\\delta_{\\rm CP}$'.

  • legend_loc (str, optional) – Legend location.

  • legend_kw (dict, optional) – Extra keywords merged over HOUSE_LEGEND_KW.

  • figsize (tuple of float, optional) – Figure size. Default is (9.0, 9.0), the square panel this plot uses.

  • subplots_kw (dict, optional) – Extra keywords for subplots().

  • savefig (str, optional) – If given, the figure is written here.

  • savefig_kw (dict, optional) – Extra keywords merged over HOUSE_SAVEFIG_KW.

  • tight_layout (bool, optional) – Whether to call tight_layout. Default is False.

Returns:

  • fig (matplotlib.figure.Figure)

  • ax (matplotlib.axes.Axes)

Examples

import matplotlib
matplotlib.use('Agg')
import numpy as np
from magnus.plotting import plot_biprobability

d = np.linspace(-np.pi, np.pi, 100)
P_nu = 0.05 + 0.02 * np.sin(d)
P_nubar = 0.04 + 0.02 * np.sin(d + 0.4)

fig, ax = plot_biprobability([P_nu], [P_nubar], labels=['NO'])
print(ax.get_xlabel())
$P_{\nu_\mu \to \nu_e}$
magnus.plotting.plot_oscillogram(costhz: Sequence[float], log10_energy: Sequence[float], probability: Sequence[Sequence[float]], *, nu_i: int | None = None, nu_f: int | None = None, levels: int = 120, cmap: str = 'plasma', xlabel: str = 'Zenith angle, $\\cos(\\theta_z)$', ylabel: str = 'Neutrino energy, $\\log_{10}(E_\\nu/{\\rm GeV})$', cbar_label: str | None = None, cbar_label_prefix: str = '', cbar_fontsize: float = 25.0, cbar_labelsize: float = 25.0, annotation: str | None = None, annotation_fontsize: float = 23.0, xlim: Tuple[float, float] | None = None, ylim: Tuple[float, float] | None = None, xmajor: float | None = 0.2, xminor: float | None = 0.02, ymajor: float | None = 0.1, yminor: float | None = 0.02, figsize: Tuple[float, float] = (9.0, 9.0), contourf_kw: Dict[str, Any] | None = None, subplots_kw: Dict[str, Any] | None = None, savefig: str | None = None, savefig_kw: Dict[str, Any] | None = None, tight_layout: bool = False)[source]

Plot an oscillogram: probability over zenith angle and energy.

A filled contour map of the oscillation probability in the plane of \(\cos\theta_z\) (equivalently, baseline through the Earth) and \(\log_{10} E_\nu\), with a colour bar and the channel annotated in the corner over a white stroke so it stays legible against the colour map.

Added in version 1.0.0.

Parameters:
  • costhz (sequence of float) – Zenith-angle cosines, the abscissa.

  • log10_energy (sequence of float) – \(\log_{10}\) of the energy in GeV, the ordinate.

  • probability (sequence of sequence of float) – Probability with shape (len(log10_energy), len(costhz)).

  • nu_i (int, optional) – Flavour pair, used for the colour-bar label and the annotation when those are not given explicitly.

  • nu_f (int, optional) – Flavour pair, used for the colour-bar label and the annotation when those are not given explicitly.

  • levels (int, optional) – Number of filled contour levels. Default is 120.

  • cmap (str, optional) – Colour map. Default is 'plasma'.

  • xlabel (str, optional) – Axis labels.

  • ylabel (str, optional) – Axis labels.

  • cbar_label (str, optional) – Colour-bar label; overrides the one built from the flavour pair.

  • cbar_label_prefix (str, optional) – Text placed before the probability label on the colour bar.

  • cbar_fontsize (float, optional) – Colour-bar label and tick-label sizes.

  • cbar_labelsize (float, optional) – Colour-bar label and tick-label sizes.

  • annotation (str, optional) – Corner annotation. Defaults to the probability label when the flavour pair is given; pass '' to suppress it.

  • annotation_fontsize (float, optional) – Corner annotation size.

  • xlim (tuple of float, optional) – Axis limits. Default to the data range.

  • ylim (tuple of float, optional) – Axis limits. Default to the data range.

  • xmajor (float, optional) – Tick spacings.

  • xminor (float, optional) – Tick spacings.

  • ymajor (float, optional) – Tick spacings.

  • yminor (float, optional) – Tick spacings.

  • figsize (tuple of float, optional) – Figure size. Default is (9.0, 9.0).

  • contourf_kw (dict, optional) – Extra keywords for contourf().

  • subplots_kw (dict, optional) – Extra keywords for subplots().

  • savefig (str, optional) – If given, the figure is written here.

  • savefig_kw (dict, optional) – Extra keywords merged over HOUSE_SAVEFIG_KW.

  • tight_layout (bool, optional) – Whether to call tight_layout. Default is False.

Returns:

  • fig (matplotlib.figure.Figure)

  • ax (matplotlib.axes.Axes)

Examples

import matplotlib
matplotlib.use('Agg')
import numpy as np
import magnus.globaldefs as gd
from magnus.plotting import plot_oscillogram

c = np.linspace(-1.0, 0.0, 40)
lE = np.linspace(-1.0, 1.0, 30)
P = np.sin(np.outer(10 ** lE, 1.0 + c)) ** 2

fig, ax = plot_oscillogram(c, lE, P, nu_i=gd.NUMU, nu_f=gd.NUMU)
print(ax.get_xlabel())
Zenith angle, $\cos(\theta_z)$