Source code for stochkin.style

"""Publication-quality Matplotlib style for stochkin.

The defaults reproduce the look-and-feel of the FES_2D.ipynb analysis
notebook (Arial font, inward ticks, 300 dpi, single-column figure width,
…).  Two entry-points are provided:

* :func:`set_publication_style` — apply the style globally.
* :func:`publication_style` — context manager that restores the
  previous rcParams on exit.

Quick start::

    import stochkin as sk
    sk.set_publication_style()          # global
    # or
    with sk.publication_style():        # scoped
        fig, ax = plt.subplots()
        ...
"""
from __future__ import annotations

import contextlib
import os
from typing import Optional

import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib import font_manager

# ── Default publication parameters ─────────────────────────────────────
# These mirror the final (overriding) rcParams block in FES_2D.ipynb.

_PUBLICATION_RC: dict = {
    # Font
    "font.family": "sans-serif",
    "font.sans-serif": ["Arial", "Helvetica", "DejaVu Sans"],
    "font.size": 7.0,
    "font.style": "normal",
    "font.variant": "normal",
    "font.weight": 400,
    # Figure
    "figure.dpi": 300,
    "figure.figsize": (3.3, 2.8),
    "figure.titlesize": 10,
    "figure.autolayout": True,
    # Axes
    "axes.facecolor": "white",
    "axes.linewidth": 0.75,
    "axes.titlesize": 6,
    "axes.labelsize": 7,
    "axes.labelpad": 0.5,
    "axes.grid": False,
    "axes.axisbelow": "line",
    # Lines
    "lines.linewidth": 1.0,
    # X ticks
    "xtick.direction": "in",
    "xtick.major.size": 5,
    "xtick.minor.size": 3,
    "xtick.major.width": 0.8,
    "xtick.minor.width": 0.8,
    "xtick.major.pad": 4,
    "xtick.labelsize": 8,
    # Y ticks
    "ytick.right": True,
    "ytick.direction": "in",
    "ytick.major.size": 5,
    "ytick.minor.size": 3,
    "ytick.major.width": 0.8,
    "ytick.minor.width": 0.8,
    "ytick.major.pad": 4,
    "ytick.labelsize": 8,
    # Legend
    "legend.fontsize": 6,
    # Saving
    "savefig.dpi": 300,
    "savefig.bbox": "tight",
    "savefig.pad_inches": 0.02,
}


def _try_register_arial() -> None:
    """Try to register the system Arial font with Matplotlib (Linux)."""
    font_path = "/usr/share/fonts/truetype/msttcorefonts/Arial.ttf"
    if os.path.exists(font_path):
        font_manager.fontManager.addfont(font_path)


[docs] def set_publication_style(rc_overrides: Optional[dict] = None) -> None: """Apply the stochkin publication rcParams globally. Parameters ---------- rc_overrides : dict, optional Extra key/value pairs that override the defaults. """ _try_register_arial() rc = dict(_PUBLICATION_RC) if rc_overrides: rc.update(rc_overrides) mpl.rcParams.update(rc)
[docs] @contextlib.contextmanager def publication_style(rc_overrides: Optional[dict] = None): """Context manager that temporarily applies the publication style. On exit, the previous rcParams are restored:: with publication_style(): fig, ax = plt.subplots() ... """ old = mpl.rcParams.copy() try: set_publication_style(rc_overrides) yield finally: mpl.rcParams.update(old)
# ── Convenience tick/label size constants (for inline overrides) ────── # These match the values that the notebook cells apply with explicit # ``ax.tick_params(...)`` and ``ax.set_xlabel(..., size=...)``. LABEL_SIZE = 12 # ax.set_xlabel(…, size=LABEL_SIZE) TICK_SIZE = 10 # ax.tick_params(axis="both", labelsize=TICK_SIZE) LEGEND_SIZE = 8 # ax.legend(fontsize=LEGEND_SIZE) CBAR_LABEL_SIZE = 14 CBAR_TICK_SIZE = 10 TITLE_SIZE = 10 __all__ = [ "set_publication_style", "publication_style", "_PUBLICATION_RC", "LABEL_SIZE", "TICK_SIZE", "LEGEND_SIZE", "CBAR_LABEL_SIZE", "CBAR_TICK_SIZE", "TITLE_SIZE", ]