codexfree-energy-temperature-crossing.py
free-energy-temperature-crossing.py
"""Plot a constructed two-phase Gibbs free-energy difference.
Writes an opaque white PNG with this script's basename in the working directory.
Tested with Python 3.14.4, NumPy 2.3.5, Matplotlib 3.10.7.
"""
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
def main():
temperature = np.linspace(260.0, 340.0, 321)
difference = 12.0 - 0.040 * temperature
samples = np.array([280.0, 300.0, 320.0])
values = 12.0 - 0.040 * samples
assert np.allclose(values, [0.8, 0.0, -0.8])
assert np.isclose(12.0 / 0.040, 300.0)
plt.rcParams.update({"font.size": 12, "figure.facecolor": "white",
"axes.facecolor": "white", "savefig.facecolor": "white",
"axes.spines.top": False, "axes.spines.right": False})
fig, ax = plt.subplots(figsize=(8.0, 4.7), dpi=130)
fig.subplots_adjust(left=0.13, right=0.96, bottom=0.22, top=0.80)
fig.suptitle("Two phases: enthalpy favors A, entropy favors B", fontsize=15)
fig.text(0.5, 0.865, "Fixed pressure; illustrative constants over a limited window",
ha="center", fontsize=11, color="#444444")
blue, orange = "#236a9e", "#b6532b"
ax.axvspan(260, 300, color=blue, alpha=0.07)
ax.axvspan(300, 340, color=orange, alpha=0.07)
ax.axhline(0.0, color="#555555", lw=1.2)
ax.axvline(300.0, color="#666666", ls="--", lw=1.0)
ax.plot(temperature, difference, color="#324d73", lw=2.6)
ax.scatter(samples, values, c=[blue, "#333333", orange], s=55, zorder=3)
ax.annotate("280 K: +0.8", (280, 0.8), xytext=(268, 1.25), fontsize=11,
arrowprops={"arrowstyle": "-", "color": blue}, color=blue)
ax.annotate("300 K: equal", (300, 0), xytext=(304, 0.40), fontsize=11,
arrowprops={"arrowstyle": "-", "color": "#555555"})
ax.annotate("320 K: −0.8", (320, -0.8), xytext=(308, -1.35), fontsize=11,
arrowprops={"arrowstyle": "-", "color": orange}, color=orange)
ax.text(277, -1.10, "A has lower G", ha="center", color=blue)
ax.text(324, 1.10, "B has lower G", ha="center", color=orange)
ax.set(xlim=(260, 340), ylim=(-1.75, 1.75), xlabel="Temperature T (K)",
ylabel=r"Molar difference $G_B-G_A$ (kJ mol$^{-1}$)")
ax.set_xticks([260, 280, 300, 320, 340])
ax.set_yticks([-1.6, -0.8, 0, 0.8, 1.6])
fig.text(0.5, 0.04, "ΔH = 12 kJ/mol; ΔS = 0.040 kJ/(mol K) • ΔG = ΔH − TΔS\n"
"Thermodynamic preference supplies no conversion timescale.",
ha="center", fontsize=11)
fig.savefig(Path.cwd() / Path(__file__).with_suffix(".png").name,
facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()