50 minReference constants & modelExecuted reading edition

Evaluate a reference vapor-pressure correlation

Reproduce IAPWS reference pressure in its valid range, check reference points and examine a Clausius–Clapeyron approximation.

Chemistry · Lesson 05🔋 Offline core analysis⏱ 50 minReference constants & model📦 numpy · pandas · matplotlib · IPython

Goal and how to run

This lesson assumes basic Python knowledge. Read each method and caption before changing parameters. AI exercises are optional; no AI account is needed for the core workflow. Data lives in notebook metadata, so there is no companion data download. Keep the notebook saved in the active working folder.

Your goal is to understand the research question, execute transparent calculations, check the results, and state the limits of the conclusion.

Setup and parameters

In [1] · Python
from pathlib import Path
import base64, gzip, hashlib, json, platform
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from IPython.display import display, HTML
from IPython.display import display as _display
def display(value):
    if isinstance(value,pd.DataFrame):
        table=value.to_html(max_rows=12,escape=True)
        style="<style>.vibeit-readable-table{max-width:100%;overflow-x:auto}.vibeit-readable-table table{width:max-content;max-width:none;border-collapse:collapse}.vibeit-readable-table td,.vibeit-readable-table th{white-space:nowrap!important;word-break:normal!important;overflow-wrap:normal!important}</style>"
        return _display(HTML(style+'<div class="vibeit-readable-table">'+table+'</div>'))
    return _display(value)
RECIPE_ID='ch05-vapor-pressure'
OUTPUT_DIR=Path.cwd()/"results"/RECIPE_ID
plt.rcParams.update({"figure.dpi":120,"font.size":10,"axes.spines.top":False,"axes.spines.right":False,
                      "axes.prop_cycle":plt.cycler(color=["#18785f","#416aa6","#bc6541","#9974a5"])})
print("Python:",platform.python_version(),"; NumPy:",np.__version__,"; Pandas:",pd.__version__)
print("Working folder:",Path.cwd())
print("Core data mode: embedded snapshot; no network requests")
Python: 3.13.13 ; NumPy: 2.5.3 ; Pandas: 3.0.6
Working folder: /Users/zhiluo/Developer/vibeit-cookbook/cookbook/downloads
Core data mode: embedded snapshot; no network requests

Load the embedded snapshot

In [2] · Python
def load_snapshot(recipe_id, expected_sha256, snapshot_key):
    """Read embedded data from this notebook, including a renamed copy."""
    for path in sorted(Path.cwd().glob("*.ipynb")):
        try:
            notebook = json.loads(path.read_text(encoding="utf-8"))
            metadata = notebook.get("metadata", {}).get("vibeit_cookbook", {})
            blob = metadata.get("snapshots", {}).get(snapshot_key)
            if metadata.get("recipe_id") != recipe_id or not blob:
                continue
            if blob.get("sha256") != expected_sha256:
                continue
            raw = gzip.decompress(base64.b64decode(blob["gzip_base64"], validate=True))
            if hashlib.sha256(raw).hexdigest() != expected_sha256:
                raise ValueError("Embedded snapshot checksum mismatch: " + snapshot_key)
            return json.loads(raw)
        except (OSError, json.JSONDecodeError):
            continue
    raise FileNotFoundError(
        "Save/open the complete .ipynb in the active working folder. "
        "The embedded snapshot could not be located for " + recipe_id)

SNAPSHOT_HASHES={'models': '48fd2e6e706650fe2ee0c0ad80b6d794a546a1dec78dd9d370ac09db3cc3adbf', 'constants': '2533802a1a546c612d06ba2e931e4f32a418be854c75a5e9c327ff57d5b56126'}
def snapshot(key):
    return load_snapshot(RECIPE_ID,SNAPSHOT_HASHES[key],key)
print("Embedded snapshots:",list(SNAPSHOT_HASHES))
Embedded snapshots: ['models', 'constants']

Read the method functions

In [3] · Python
def water_saturation_pressure(temperature_K,parameters):
    """IAPWS SR1-86(1992) saturation-pressure correlation, output in pascals."""
    T=np.asarray(temperature_K,dtype=float);Tc=parameters['critical_temperature_K'];Pc=parameters['critical_pressure_Pa']
    if not np.isfinite(T).all() or np.any(T<273.16) or np.any(T>Tc):
        raise ValueError('Reference correlation requires triple-point through critical temperatures')
    tau=1-T/Tc;series=np.zeros_like(T)
    for coefficient,power in zip(parameters['coefficients'],parameters['exponents']):series+=coefficient*tau**power
    return Pc*np.exp(Tc/T*series)

print("Method ready: water_saturation_pressure")
Method ready: water_saturation_pressure

Data and model boundary

PubChem properties and conformers are calculated database fields, not new experimental measurements. V2000 parsing here is intentionally limited to atoms, reported bonds and formal charges. Titration and calibration use stated ideal/simulated teaching assumptions. Water pressure uses the attributed IAPWS reference correlation. Descriptors, distances and visual proximity do not establish biological activity or binding strength.

Steps

1. Respect the reference correlation range

IAPWS SR1-86(1992) defines saturation pressure between the triple and critical temperatures. The correlation returns absolute pressure in Pa. Coefficients and critical values are transcribed from the official release, whose cover permits unrestricted publication.

In [4] · Python
p=snapshot("models")["water_saturation"];R=snapshot("constants")["constants"]["R"]["value"]
temperature=np.linspace(273.16,400,501);pressure=water_saturation_pressure(temperature,p)
fig,ax=plt.subplots(figsize=(8,4));ax.semilogy(temperature-273.15,pressure/1000)
ax.set(xlabel="Reference temperature (°C)",ylabel="Absolute saturation pressure (kPa)",title="IAPWS reference saturation-pressure correlation");plt.tight_layout();plt.show()
Computed figure from this code cell

2. Inspect a Clausius–Clapeyron linear approximation

In an ideal-vapor, negligible-liquid-volume, constant-enthalpy approximation, ln(P) is linear in 1/T. Fit a declared 280–370 K reference interval and show residuals separately. These are calculated reference pressures, not independent experimental observations.

In [5] · Python
fit_temperature=np.linspace(280,370,101);fit_pressure=water_saturation_pressure(fit_temperature,p)
coefficient=np.polyfit(1/fit_temperature,np.log(fit_pressure),1);predicted=np.polyval(coefficient,1/fit_temperature)
apparent_enthalpy=-R*coefficient[0]
fig,axes=plt.subplots(1,2,figsize=(10,4));axes[0].plot(1/fit_temperature,np.log(fit_pressure),label="Reference correlation")
axes[0].plot(1/fit_temperature,predicted,"--",label="Linear approximation");axes[0].set(xlabel="1/T (1/K)",ylabel="ln(P / 1 Pa)");axes[0].legend(fontsize=8)
axes[1].plot(fit_temperature,np.log(fit_pressure)-predicted);axes[1].set(xlabel="T (K)",ylabel="Log-pressure residual")
plt.tight_layout();plt.show();print("Apparent interval enthalpy under ideal-vapor approximation (kJ/mol):",apparent_enthalpy/1000)
Computed figure from this code cell
Apparent interval enthalpy under ideal-vapor approximation (kJ/mol): 43.16264125387878

3. Inspect local apparent enthalpy

R T² dln(P)/dT gives an apparent molar vaporization enthalpy only under the same volume/ideal-vapor approximation. Its change over temperature exposes the constant-enthalpy simplification. Do not extend this estimate to critical conditions as an exact latent heat.

In [6] · Python
local=R*fit_temperature**2*np.gradient(np.log(fit_pressure),fit_temperature,edge_order=2)
fig,ax=plt.subplots(figsize=(8,4));ax.plot(fit_temperature,local/1000)
ax.axhline(apparent_enthalpy/1000,color="gray",linestyle="--",label="Interval linear-fit value")
ax.set(xlabel="Reference temperature (K)",ylabel="Apparent ideal-vapor enthalpy (kJ/mol)",title="Temperature-dependent reference slope");ax.legend();plt.tight_layout();plt.show()
Computed figure from this code cell

4. Check pressure benchmarks and rejected extrapolation

Reproduce the critical pressure and the triple-point reference to stated rounding precision. Below-triple-point input is rejected because this correlation is not a general ice-vapor equation. Export absolute units and the reference release.

In [7] · Python
assert np.isclose(water_saturation_pressure(np.array([647.096]),p)[0],22.064e6,rtol=1e-12)
assert np.isclose(water_saturation_pressure(np.array([273.16]),p)[0],611.657,rtol=5e-6)
assert np.all(np.diff(pressure)>0)
try:water_saturation_pressure(np.array([250]),p)
except ValueError:print("Below-range extrapolation rejected")
else:raise AssertionError("Unsupported temperature accepted")
OUTPUT_DIR.mkdir(parents=True,exist_ok=True)
pd.DataFrame({"temperature_K":temperature,"reference_pressure_Pa":pressure}).to_csv(OUTPUT_DIR/"reference-saturation-pressure.csv",index=False)
pd.DataFrame({"temperature_K":fit_temperature,"apparent_enthalpy_J_per_mol":local,"linear_log_pressure_residual":np.log(fit_pressure)-predicted}).to_csv(OUTPUT_DIR/"reference-approximation-audit.csv",index=False)
print("Reference-point and range checks passed; exports:",OUTPUT_DIR)
Below-range extrapolation rejected
Reference-point and range checks passed; exports: /Users/zhiluo/Developer/vibeit-cookbook/cookbook/downloads/results/ch05-vapor-pressure

Exercises and reference explanation

  1. Distinguish reference-correlated pressure from a new measurement.
  2. What assumptions support the apparent enthalpy slope?

Reference explanation. IAPWS is an attributed reference correlation. The slope interpretation assumes ideal vapor, negligible liquid volume and an enthalpy approximation; it is not exact near critical conditions.

Keep a copy before changing parameters. Compare old and new figures and record which inputs, calculations and conclusions changed.

Optional AI coding exercises

Prompt 1

Using NumPy/Pandas/Matplotlib, compare linear-fit intervals 280–320 and 320–370 K within the valid correlation range.

Prompt 2

Using the same packages, add a normal-pressure boiling root using SciPy brentq and explicit Pa/K units.

Human acceptance. Reference benchmarks and range guards pass, pressure is absolute and derived quantities retain approximation labels.

Review the edited source, then manually run affected cells and subsequent checks. Ask the assistant to explain the purpose, packages and data provenance of its changes, and reconcile that explanation with actual output.

Optional online extension

In [8] · Python
RUN_ONLINE_UPDATE=False
if RUN_ONLINE_UPDATE:
    import requests
    try:
        response=requests.get('https://iapws.org/technical-guidance/release/Supp-sat',timeout=20)
        response.raise_for_status()
        print("Source:",response.url)
        print(response.text[:700])
    except requests.RequestException as error:
        print("Online extension unavailable:",type(error).__name__,str(error)[:160])
else:
    print("Online extension skipped; embedded core data unchanged")
Online extension skipped; embedded core data unchanged

Sources, snapshots and next steps

  • NIST CODATA physical constants — NIST public reference data; credit NIST/CODATA; retrieved 2026-10-09. SHA-256 77fb90e66c40db3e6eb16630bc9c88e4c7c8beddbe5e71be406f2f26e3f67e67. Preserve official ASCII table; extract seven explicitly named SI constants and their units.

  • IAPWS saturation reference release — IAPWS SR1-86(1992): unrestricted publication allowed in all countries (cover page); retrieved 2026-10-09. SHA-256 a10d38d73339b0713c86cfac88f9adcba137202167ccdfa1cd58238c438361d7. Reference equations and numerical coefficients transcribed from release pages 2–3; pedagogical model values are not new experiments.

  • Authored numerical teaching model parameters — Original teaching parameters/code MIT; reference equation coefficients attributed to IAPWS SR1-86(1992); retrieved 2026-10-09. SHA-256 4350c4305a83c772fa295a8be0ef86a247f2bfaae930a081720d3cd0c753abe0. Declared parameters and seed; generated trajectories and calibration observations must be labelled simulated.

Next steps. Transfer these methods to your own public or authorized data while retaining input checks, recorded parameters and result validation. Document the new experimental design and method limits; this example does not validate a new experiment. Full data licenses and generation instructions accompany the cookbook.