Plotting

Publication-quality plotting utilities for SED fits, SFH recovery, corner plot comparisons, and convergence diagnostics tables. Designed for ApJ/MNRAS figures.

Style Setup

tengri.analysis.plotting.setup_style(style='tengri')[source]

Configure matplotlib for publication-quality astronomy figures.

Defaults to a single cohesive style across every tengri notebook and example, built on top of the scienceplots science preset — the standard Nature/PRL/ApJ-compatible look (serif fonts, thin inward ticks on all four sides, frameless legends, no grid).

Parameters:

style (str) –

Which composite style to apply. One of:

  • "tengri" (default) — scienceplots.science + no-latex + tengri-specific tick and font-size overrides. Safe everywhere (no system LaTeX required).

  • "tengri-nature" — Nature-journal variant (single-column width, sans-serif Arial). Use for slide decks.

  • "tengri-minimal" — same tengri overrides but skip scienceplots so the style works when scienceplots is unavailable. Keeps the BAGPIPES-inspired look as a fallback.

Return type:

None

Examples

>>> from tengri.analysis.plotting import setup_style
>>> setup_style()  # default: science + tengri overrides
>>> setup_style("tengri-minimal")  # fallback if scienceplots absent

SFH Plots

tengri.analysis.plotting.plot_sfh(model, posterior, true_params=None, ax=None, color=None, label='Posterior', method='RT', show_draws=True, n_draws=30, ci_levels=(16, 84), xlim=(0, 13.5), show_mean_sfh=True)[source]

Plot SFH posterior with uncertainty band — BAGPIPES/Prospector style.

Features: - Filled 68% credible interval (not sample spaghetti) - Optional faint sample draws underneath - Truth as solid black line - Mean SFH backbone as dashed line - Clean axis labels with proper units

Parameters:
  • model (SEDModel) – Fitted model instance.

  • posterior (Posterior) – Posterior from Fitter.run().

  • true_params (dict, optional) – Ground truth parameters for mock recovery plots.

  • ax (matplotlib Axes, optional) – Axes to plot on. Creates new figure if None.

  • color (str, optional) – Line color. Defaults to sampler-specific color from style sheet.

  • label (str) – Legend label. Default "Posterior".

  • method (str) – Sampler name for auto-coloring ("RT", "geoVI", "NUTS", "MAP").

  • show_draws (bool) – If True, draw faint individual SFH samples. Default True.

  • n_draws (int) – Number of sample draws to plot. Default 30.

  • ci_levels (tuple of float) – Percentile levels for the filled credible interval. Default (16, 84).

  • xlim (tuple of float) – x-axis limits in lookback time. [Gyr]

  • show_mean_sfh (bool) – If True, overplot the smooth mean SFH backbone as a dashed line. Default True.

Returns:

ax

Return type:

matplotlib Axes

Examples

from tengri import plot_sfh

ax = plot_sfh(model, posterior, true_params=true_params, method="NUTS")
ax.figure.savefig("sfh_recovery.pdf")
tengri.analysis.plotting.plot_sfh_comparison(model, results, true_params=None, methods=None, figsize=(15, 4))[source]

Side-by-side SFH recovery for multiple methods.

BAGPIPES-style multi-panel layout.

Parameters:
  • model (SEDModel) – Fitted model instance.

  • results (dict) – Mapping from method name to Posterior (e.g. {"NUTS": post, "VI": post2}).

  • true_params (dict, optional) – Ground truth parameters for mock recovery plots.

  • methods (list of str, optional) – Panel order. Defaults to list(results.keys()).

  • figsize (tuple of float) – Figure size in inches. Default (15, 4).

Returns:

  • fig (matplotlib Figure)

  • axes (list of matplotlib Axes)

Examples

from tengri import plot_sfh_comparison

results = {"NUTS": posterior_nuts, "VI": posterior_vi}
fig, axes = plot_sfh_comparison(model, results, true_params=true_params)
fig.savefig("sfh_comparison.pdf")

SED Plots

tengri.analysis.plotting.plot_sed_fit(wave_eff, flux_obs, noise, flux_true=None, posterior_draws=None, ax=None, band_names=None, show_residuals=True)[source]

Plot observed photometry with model fit — Prospector style.

Parameters:
  • wave_eff (array_like, shape (n_bands,)) – Effective filter wavelengths. [Angstrom]

  • flux_obs (array_like, shape (n_bands,)) – Observed photometric fluxes. [erg/s/cm²/Hz]

  • noise (array_like, shape (n_bands,)) – 1-sigma photometric uncertainties. [erg/s/cm²/Hz]

  • flux_true (array_like, shape (n_bands,), optional) – True (noiseless) fluxes for mock recovery plots.

  • posterior_draws (array_like, shape (n_draws, n_bands), optional) – Posterior predictive flux draws for uncertainty shading.

  • ax (matplotlib Axes, optional) – Axes to plot on. Creates new figure if None.

  • band_names (list of str, optional) – Filter names for axis labels.

  • show_residuals (bool) – If True, adds a residual panel below the main SED panel. Default True.

Returns:

fig

Return type:

matplotlib Figure

Examples

from tengri import plot_sed_fit
import numpy as np

wave = np.array([4800.0, 6200.0, 7700.0, 9000.0])
flux = np.array([1.2e-18, 1.8e-18, 2.1e-18, 1.9e-18])
noise = flux * 0.05
fig = plot_sed_fit(wave, flux, noise, show_residuals=False)
tengri.analysis.plotting.plot_spectrum_fit(wave_obs, spec_obs, noise, spec_true=None, spec_draws=None, features=None, z=0.1)[source]

Plot spectroscopic fit with residuals — CIGALE style.

Parameters:
  • wave_obs (array_like, shape (n_pix,)) – Observed-frame wavelength grid. [Angstrom]

  • spec_obs (array_like, shape (n_pix,)) – Observed spectrum flux density. [erg/s/cm²/Hz]

  • noise (array_like, shape (n_pix,)) – 1-sigma per-pixel uncertainties. [erg/s/cm²/Hz]

  • spec_true (array_like, shape (n_pix,), optional) – True (noiseless) spectrum for mock recovery plots.

  • spec_draws (array_like, shape (n_draws, n_pix), optional) – Posterior predictive spectrum draws for uncertainty shading.

  • features (dict, optional) – Rest-frame line/feature wavelengths to annotate, e.g. {"Hα": 6563.0}.

  • z (float) – Redshift for shifting feature wavelengths to observed frame. Default 0.1.

Returns:

fig

Return type:

matplotlib Figure

Examples

from tengri import plot_spectrum_fit

fig = plot_spectrum_fit(
    wave_obs,
    spec_obs,
    noise,
    spec_draws=posterior_spec_draws,
    features={"Hα": 6563.0, "Hβ": 4861.0},
    z=0.5,
)
fig.savefig("spectrum_fit.pdf")

Corner Plots

tengri.analysis.plotting.safe_corner(posterior, **kwargs)[source]

Wrapper around Posterior.plot_corner that handles degenerate posteriors.

Parameters:
  • posterior (Posterior) – Fitted posterior object with a plot_corner method.

  • **kwargs – Additional keyword arguments passed to posterior.plot_corner.

Returns:

fig – Corner plot figure, or None if generation failed.

Return type:

matplotlib.figure.Figure or None

Examples

from tengri import safe_corner

fig = safe_corner(posterior, params=["sfh_dpl_alpha", "dust_tau_bc"])
if fig is not None:
    fig.savefig("corner.pdf")
tengri.analysis.plotting.plot_corner_comparison(posteriors, labels, colors=None, truths=None, params=None)[source]

Overlay multiple posteriors on a single corner plot.

Parameters:
  • posteriors (list of Posterior) – Posterior objects to overlay.

  • labels (list of str) – Legend labels for each posterior (e.g. ["NUTS", "VI", "MAP"]).

  • colors (list of str, optional) – Colors for each posterior. Defaults to sampler palette.

  • truths (dict, optional) – True parameter values for recovery plots.

  • params (list of str, optional) – Parameter names to include. Defaults to all free parameters.

Returns:

fig – Combined corner plot, or None if all posteriors failed.

Return type:

matplotlib Figure or None

Examples

from tengri import plot_corner_comparison

fig = plot_corner_comparison(
    [post_nuts, post_vi],
    labels=["NUTS", "VI"],
    truths=true_params,
    params=["sfh_dpl_alpha", "dust_tau_bc"],
)
if fig is not None:
    fig.savefig("corner_comparison.pdf")

Diagnostics

tengri.analysis.plotting.diagnostics_table(results, names=None)[source]

Print a formatted diagnostics comparison table.

Parameters:
  • results (dict) – Mapping from method name to Posterior (e.g. from Fitter.run()).

  • names (list of str, optional) – Display order. Defaults to list(results.keys()).

Return type:

None

Examples

from tengri import diagnostics_table

results = {"NUTS": posterior_nuts, "VI": posterior_vi}
diagnostics_table(results)
# Method          Wall time   ESS (min)   ESS (med)   Accept %
# ----------------------------------------------------------------
# NUTS               42.3s         812         934      82.4%
# VI                  3.1s           —           —         —

Constants

tengri.analysis.plotting.COLORS = {'data': '#333333', 'g': '#008837', 'geovi': '#ff7f0e', 'i': '#fc8d59', 'map': '#888888', 'mcmc_nuts': '#2ca02c', 'mcmc_raytrace': '#1f77b4', 'mgvi': '#9467bd', 'model': '#d62728', 'nuts': '#2ca02c', 'r': '#d73027', 'rt': '#1f77b4', 'seq': ['#d4d4d4', '#a8a8a8', '#1f77b4', '#2ca02c', '#d62728'], 'sfh_full': '#ff7f0e', 'sfh_gp': '#2ca02c', 'sfh_mean': '#1f77b4', 'truth': '#1a1a1a', 'u': '#7b3294', 'vi': '#ff7f0e', 'vi_linear': '#9467bd', 'z': '#4575b4'}

dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object’s

(key, value) pairs

dict(iterable) -> new dictionary initialized as if via:

d = {} for k, v in iterable:

d[k] = v

dict(**kwargs) -> new dictionary initialized with the name=value pairs

in the keyword argument list. For example: dict(one=1, two=2)

tengri.analysis.plotting.SDSS_WAVE_EFF = array([3551, 4686, 6166, 7480, 8932])

ndarray(shape, dtype=None, buffer=None, offset=0, strides=None, order=None) –

ndarray(shape, dtype=float, buffer=None, offset=0, strides=None, order=None)

An array object represents a multidimensional, homogeneous array of fixed-size items. An associated data-type object describes the format of each element in the array (its byte-order, how many bytes it occupies in memory, whether it is an integer, a floating point number, or something else, etc.)

Arrays should be constructed using array, zeros or empty (refer to the See Also section below). The parameters given here refer to a low-level method (ndarray(…)) for instantiating an array.

For more information, refer to the numpy module and examine the methods and attributes of an array.

Parameters:
  • below) ((for the __new__ method; see Notes)

  • shape (tuple of ints) – Shape of created array.

  • dtype (data-type, optional) – Any object that can be interpreted as a numpy data type. Default is numpy.float64.

  • buffer (object exposing buffer interface, optional) – Used to fill the array with data.

  • offset (int, optional) – Offset of array data in buffer.

  • strides (tuple of ints, optional) – Strides of data in memory.

  • order ({'C', 'F'}, optional) – Row-major (C-style) or column-major (Fortran-style) order.

tengri.analysis.plotting.T

Transpose of the array.

Type:

ndarray

tengri.analysis.plotting.data

The array’s elements, in memory.

Type:

buffer

tengri.analysis.plotting.dtype

Describes the format of the elements in the array.

Type:

dtype object

tengri.analysis.plotting.flags

Dictionary containing information related to memory use, e.g., ‘C_CONTIGUOUS’, ‘OWNDATA’, ‘WRITEABLE’, etc.

Type:

dict

tengri.analysis.plotting.flat

Flattened version of the array as an iterator. The iterator allows assignments, e.g., x.flat = 3 (See ndarray.flat for assignment examples; TODO).

Type:

numpy.flatiter object

tengri.analysis.plotting.imag

Imaginary part of the array.

Type:

ndarray

tengri.analysis.plotting.real

Real part of the array.

Type:

ndarray

tengri.analysis.plotting.size

Number of elements in the array.

Type:

int

tengri.analysis.plotting.itemsize

The memory use of each array element in bytes.

Type:

int

tengri.analysis.plotting.nbytes

The total number of bytes required to store the array data, i.e., itemsize * size.

Type:

int

tengri.analysis.plotting.ndim

The array’s number of dimensions.

Type:

int

tengri.analysis.plotting.shape

Shape of the array.

Type:

tuple of ints

tengri.analysis.plotting.strides

The step-size required to move from one element to the next in memory. For example, a contiguous (3, 4) array of type int16 in C-order has strides (8, 2). This implies that to move from element to element in memory requires jumps of 2 bytes. To move from row-to-row, one needs to jump 8 bytes at a time (2 * 4).

Type:

tuple of ints

tengri.analysis.plotting.ctypes

Class containing properties of the array needed for interaction with ctypes.

Type:

ctypes object

tengri.analysis.plotting.base

If the array is a view into another array, that array is its base (unless that array is also a view). The base array is where the array data is actually stored.

Type:

ndarray

See also

array

Construct an array.

zeros

Create an array, each element of which is zero.

empty

Create an array, but leave its allocated memory unchanged (i.e., it contains “garbage”).

dtype

Create a data-type.

numpy.typing.NDArray

An ndarray alias generic w.r.t. its dtype.type <numpy.dtype.type>.

Notes

There are two modes of creating an array using __new__:

  1. If buffer is None, then only shape, dtype, and order are used.

  2. If buffer is an object exposing the buffer interface, then all keywords are interpreted.

No __init__ method is needed because the array is fully initialized after the __new__ method.

Examples

These examples illustrate the low-level ndarray constructor. Refer to the See Also section above for easier ways of constructing an ndarray.

First mode, buffer is None:

>>> import numpy as np
>>> np.ndarray(shape=(2,2), dtype=float, order='F')
array([[0.0e+000, 0.0e+000], # random
       [     nan, 2.5e-323]])

Second mode:

>>> np.ndarray((2,), buffer=np.array([1,2,3]),
...            offset=np.int_().itemsize,
...            dtype=int) # offset = 1*itemsize, i.e. skip first element
array([2, 3])
tengri.analysis.plotting.SPECTRAL_FEATURES = {'D4000': 4000.0, 'H$\\alpha$': 6563.0, 'H$\\beta$': 4861.0, 'H$\\delta$': 4102.0, 'H$\\gamma$': 4340.0, 'Ly$\\alpha$': 1216.0, 'Mg b': 5175.0, 'Na D': 5893.0, '[O III]': 5007.0}

dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object’s

(key, value) pairs

dict(iterable) -> new dictionary initialized as if via:

d = {} for k, v in iterable:

d[k] = v

dict(**kwargs) -> new dictionary initialized with the name=value pairs

in the keyword argument list. For example: dict(one=1, two=2)