codex/gravimetric-dilution-uncertainty.py

gravimetric-dilution-uncertainty.py
"""Original first-order uncertainty model for mass-based dilution; PNG to cwd."""
from pathlib import Path

import matplotlib

matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np


def main():
    factors = np.geomspace(1, 1000, 400)
    f = 1 / factors
    total_mass, mass_uncertainty, stock_relative = 20.0, 0.001, 0.005
    # The total is nominal across examples; component-mass errors are independent.
    weighing = mass_uncertainty / (total_mass * f) * np.sqrt((1-f)**2 + f**2)
    combined = np.hypot(weighing, stock_relative)
    at_ten = 0.001 / 2 * np.sqrt(0.9**2 + 0.1**2)
    assert np.isclose(at_ten, 0.0004527692569068709)
    fig, ax = plt.subplots(figsize=(8, 5), dpi=100, facecolor="white")
    fig.subplots_adjust(left=0.12, right=0.97, top=0.76, bottom=0.24)
    fig.suptitle("Strong dilution magnifies an independent weighing contribution",
                 y=0.96, fontsize=13, weight="bold", color="#172330")
    fig.text(0.5, 0.88, "Chosen model: nominal final mass 20 g; each net mass has 1 mg standard uncertainty",
             ha="center", fontsize=10, color="#45535d")
    ax.loglog(factors, 100*weighing, color="#216c9b", linewidth=2.3,
              label="Component-mass contribution")
    ax.loglog(factors, 100*combined, color="#b45335", linewidth=2.3,
              label="Combined with stock-value uncertainty")
    ax.axhline(100*stock_relative, color="#71818b", linestyle=":", linewidth=1.7,
               label="Assumed stock-value contribution: 0.5%")
    ax.plot(10, 100*at_ten, "o", color="#216c9b", markersize=5)
    ax.annotate("F = 10: 0.0453%", (10, 100*at_ten), (16, 0.018),
                fontsize=10, color="#216c9b",
                arrowprops={"arrowstyle": "-", "color": "#216c9b"})
    ax.set_xlim(1, 1000)
    ax.set_ylim(0.003, 10)
    ax.set_xlabel(r"Nominal gravimetric dilution factor $F=1/f$", fontsize=11)
    ax.set_ylabel("Relative standard uncertainty (%)", fontsize=11)
    ax.grid(which="major", color="#dce4e9", linewidth=0.7)
    ax.legend(loc="upper left", fontsize=9, framealpha=0.96)
    fig.text(0.5, 0.075, "Illustrative first-order model, not measured balance performance.\n"
             "A shared multiplicative mass-scale error cancels from the ratio; it is not this independent contribution.",
             ha="center", fontsize=9.5, color="#45535d")
    fig.savefig(Path.cwd() / (Path(__file__).stem + ".png"), dpi=100,
                facecolor="white", transparent=False)
    plt.close(fig)


if __name__ == "__main__":
    main()