codex/clapeyron-coexistence-slopes-and-volume-jump.py

clapeyron-coexistence-slopes-and-volume-jump.py
"""Local coexistence tangents from stipulated data.

Run from _media with Python 3.14, matplotlib 3.10.7 and numpy 2.3.5.
"""

from pathlib import Path

import matplotlib.pyplot as plt
import numpy as np


def main():
    plt.rcParams.update({"font.size": 12, "axes.spines.top": False,
                         "axes.spines.right": False, "figure.facecolor": "white",
                         "axes.facecolor": "white"})
    fig, axes = plt.subplots(1, 2, figsize=(10.8, 5.6))
    fig.subplots_adjust(left=0.09, right=0.96, bottom=0.34, top=0.77, wspace=0.34)
    fig.suptitle(r"Coexistence slope: $dp/dT=L/(T\,\Delta v)$", y=0.97, fontsize=19)
    fig.text(0.5, 0.88, "Local tangents from stipulated inputs; not measured phase diagrams",
             ha="center", fontsize=12)
    vapor_slope = 40000 * 10000 / (8.314 * 300**2) / 1000
    water_slope = 334000 / (273.15 * (1 / 1000 - 1 / 917)) / 1e6
    models = [
        (axes[0], np.linspace(-2, 2, 100), vapor_slope, 1,
         "Liquid → gas: positive volume jump", "Pressure change (kPa)", "#146a92"),
        (axes[1], np.linspace(-0.08, 0.08, 100), water_slope, 1 / water_slope,
         "Ice → water: negative volume jump", "Pressure change (MPa)", "#a74828"),
    ]
    for ax, dt, slope, step_t, title, ylabel, color in models:
        ax.plot(dt, slope * dt, color=color, lw=3)
        ax.axhline(0, color="0.65", lw=0.8)
        ax.axvline(0, color="0.65", lw=0.8)
        step_p = slope * step_t
        ax.plot([0, step_t, step_t], [0, 0, step_p], "--", color="0.3", lw=1.5)
        ax.scatter([0, step_t], [0, step_p], color=color, s=32, zorder=4)
        ax.set(title=title, xlabel="Temperature change (K)", ylabel=ylabel)
        ax.grid(alpha=0.15)
        if ax is axes[0]:
            ax.text(0.55, -0.23, "+1 K", ha="center")
            ax.text(1.08, 0.20, "+0.535\nkPa", fontsize=11)
        else:
            ax.set_xticks([-0.08, -0.04, 0, 0.04, 0.08])
            ax.text(-0.038, -0.25, "−0.0740 K", ha="center", fontsize=11)
            ax.text(-0.069, 0.32, "+1\nMPa", fontsize=11)
    fig.text(0.09, 0.20,
             r"$T=300\ \mathrm{K},\quad L_m=40.0\ \mathrm{kJ\,mol^{-1}}$" "\n"
             r"$\Delta v_m\simeq RT/p=0.24942\ \mathrm{m^3\,mol^{-1}}$" "\n"
             r"$dp/dT=+0.5346\ \mathrm{kPa\,K^{-1}}$", fontsize=12, va="top")
    fig.text(0.59, 0.20,
             r"$T_m=273.15\ \mathrm{K},\quad L=334\ \mathrm{kJ\,kg^{-1}}$" "\n"
             r"$\Delta v=1/1000-1/917=-9.0513\!\times\!10^{-5}\ \mathrm{m^3\,kg^{-1}}$" "\n"
             r"$dT_m/dp=-0.07402\ \mathrm{K\,MPa^{-1}}$", fontsize=11, va="top")
    fig.text(0.5, 0.035, "Positive latent heat: the sign of the slope follows the sign of the volume jump.",
             ha="center", fontsize=12)
    fig.savefig(Path(__file__).with_suffix(".png").name, dpi=100,
                facecolor="white", transparent=False)
    plt.close(fig)


if __name__ == "__main__":
    main()