codex/arrhenius-temperature-sensitivity.py

arrhenius-temperature-sensitivity.py
"""Plot two constant-parameter Arrhenius models and their linear transforms.

Writes its PNG to the current working directory. Curves are exact teaching
models, not experimental observations. Each is anchored at k(300 K)=0.1/s,
so the two pre-exponential factors are different.
"""

from pathlib import Path

import matplotlib.pyplot as plt
import numpy as np

R = 8.31446261815324
REFERENCE_TEMPERATURE = 300.0
REFERENCE_RATE = 0.1


def log_relative_rate(temperature, activation_energy):
    return activation_energy / R * (
        1 / REFERENCE_TEMPERATURE - 1 / np.asarray(temperature)
    )


def main():
    plt.rcParams.update({"font.size": 11, "axes.titlesize": 13})
    temperature = np.linspace(280, 350, 1401)
    fig, axes = plt.subplots(1, 2, figsize=(11.8, 5.3), dpi=100)
    for energy, color in [(40000.0, "#2867a0"), (80000.0, "#bd5f21")]:
        log_ratio = log_relative_rate(temperature, energy)
        label = f"Eₐ = {energy / 1000:g} kJ/mol"
        axes[0].semilogy(temperature, REFERENCE_RATE * np.exp(log_ratio),
                         color=color, linewidth=2.5, label=label)
        axes[1].plot(1000 / temperature, log_ratio, color=color,
                     linewidth=2.5, label=label)
        rate310 = REFERENCE_RATE * np.exp(log_relative_rate(310, energy))
        axes[0].scatter([310], [rate310], s=38, color=color, zorder=4)
    axes[0].set_title("A 10 K rise gives different rate multipliers")
    axes[0].set_xlabel("Absolute temperature T (K)")
    axes[0].set_ylabel("First-order rate constant k (s⁻¹), logarithmic scale")
    axes[0].set_xlim(280, 350)
    axes[0].axvline(300, color="#666666", linestyle=":", linewidth=1.2)
    axes[0].annotate("Common k(300 K) = 0.100 s⁻¹",
                     xy=(300, 0.1), xytext=(284, 1.4),
                     arrowprops={"arrowstyle": "->", "color": "#555555"},
                     fontsize=10, color="#333333")
    axes[0].text(316, 0.036, "At 310 K:\n40 kJ/mol → 0.167749 s⁻¹\n"
                 "80 kJ/mol → 0.281398 s⁻¹",
                 fontsize=10, bbox={"facecolor": "white", "edgecolor": "#dddddd"})
    axes[1].set_title("Inverse temperature makes each model linear")
    axes[1].set_xlabel("Inverse temperature 1/T (10⁻³ K⁻¹)")
    axes[1].set_ylabel("ln[k(T) / k(300 K)]")
    axes[1].axhline(0, color="#777777", linewidth=1, alpha=0.45)
    axes[1].axvline(1000 / 300, color="#666666", linestyle=":", linewidth=1.2)
    for ax in axes:
        ax.grid(alpha=0.18)
        ax.legend(loc="upper left", fontsize=10, framealpha=0.96)
    axes[0].legend(loc="lower right", fontsize=10, framealpha=0.96)
    fig.suptitle("Arrhenius temperature sensitivity and its straight-line representation",
                 fontsize=15, y=0.98)
    fig.text(0.5, 0.035,
             "Chosen constant-parameter models, not measured data. "
             "Equal reference rates imply different prefactors.",
             ha="center", fontsize=10)
    fig.subplots_adjust(left=0.085, right=0.975, top=0.82, bottom=0.19, wspace=0.24)
    fig.savefig(Path.cwd() / (Path(__file__).stem + ".png"),
                facecolor="white", transparent=False)
    plt.close(fig)


if __name__ == "__main__":
    main()