Source code for croak.plotting.units

"""Display units and number formatting shared by the plotters.

Retrieval quantities are SI (s, rad/s, W); the axes show fs, PHz and an
SI-prefixed power, and read-outs use three significant digits (:func:`sig3`).
"""

from __future__ import annotations

import numpy as np

_TWOPI_PHZ = 2 * np.pi * 1e15


[docs] def sig3(value: float) -> str: """Format a number to three significant digits, for a read-out. Three significant digits keeps a read-out equally informative across the ranges croak covers (sub-fs attosecond pulses through hundreds of fs; nanometre-wide through hundred-nanometre-wide spectra), where a fixed number of decimals is too coarse at one end and spuriously precise at the other. Unlike the ``g`` format this keeps trailing zeros (so ``7.00``, not ``7``) and never switches to exponent notation, both of which read badly beside a sibling entry in a legend or a table column. Parameters ---------- value : float The number to format, already in its display unit. Returns ------- str The formatted number, with no unit attached. Examples -------- >>> sig3(9.512), sig3(7.0), sig3(0.85), sig3(953.2) ('9.51', '7.00', '0.850', '953') """ if not np.isfinite(value) or value == 0.0: return f"{value:.2f}" # digits before the decimal point (<= 0 for |value| < 1), so 3 - that many is # how many decimals are still needed to reach three significant digits. integer_digits = int(np.floor(np.log10(abs(value)))) + 1 return f"{value:.{max(0, 3 - integer_digits)}f}"
def _fs(seconds: float) -> str: """Format a duration (s) as femtoseconds to three significant digits.""" return sig3(seconds / 1e-15) _POWER_PREFIXES: tuple[tuple[float, str], ...] = ( (1e15, "PW"), (1e12, "TW"), (1e9, "GW"), (1e6, "MW"), (1e3, "kW"), (1.0, "W"), (1e-3, "mW"), (1e-6, "µW"), )
[docs] def si_power(watts: float) -> tuple[float, str]: """Return ``(scale, unit)`` to display ``watts`` with an SI power prefix. Picks the largest prefix not exceeding ``|watts|`` (so e.g. ``1.2e7`` W → ``(1e6, "MW")``). Non-finite or non-positive inputs fall back to plain watts; values below the smallest prefix use that smallest prefix. """ w = abs(float(watts)) if not np.isfinite(w) or w <= 0: return 1.0, "W" for scale, unit in _POWER_PREFIXES: if w >= scale: return scale, unit return _POWER_PREFIXES[-1]