File size: 3,081 Bytes
64c992d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | """Figure S12: MagNET vs DFT carbon-13 shielding of carbon monoxide across bond lengths.
The CO bond is stretched/compressed; at each length the 13C shielding is computed by DFT
(PBE1PBE/pcSseg-1, gas, Gaussian) and by the MagNET foundation model. This module holds only the
loading; the plot is drawn inline in the figure notebook.
Two files ship alongside:
- `co_magnet_vs_gaussian.csv` - one row per bond length: DFT and MagNET shieldings and their
difference, for carbon and oxygen.
- `co_bond_lengths_histogram.csv` - carbon-oxygen bond-length distribution across real molecules,
as (bin left edge, count) rows, for the shaded histogram.
"""
import os
import pandas as pd
from stats import mae
HERE = os.path.dirname(os.path.abspath(__file__))
COMPARISON_CSV = os.path.join(HERE, "..", "..", "data", "carbon_monoxide", "co_magnet_vs_gaussian.csv")
HISTOGRAM_CSV = os.path.join(HERE, "..", "..", "data", "carbon_monoxide", "co_bond_lengths_histogram.csv")
def load_comparison(path=COMPARISON_CSV):
"""The bond-length scan as a DataFrame, sorted by bond length. Columns: `bond_length` (angstrom),
and for each of carbon and oxygen the `gaussian`, `magnet`, and `delta` (MagNET minus DFT)
shieldings in ppm."""
raw = pd.read_csv(path)
return pd.DataFrame(
{
"bond_length": raw["bond_length_angstrom"].astype(float),
"gaussian_c": raw["gaussian_shielding_c_ppm"].astype(float),
"magnet_c": raw["magnet_shielding_c_ppm"].astype(float),
"delta_c": raw["delta_c_ppm"].astype(float),
"gaussian_o": raw["gaussian_shielding_o_ppm"].astype(float),
"magnet_o": raw["magnet_shielding_o_ppm"].astype(float),
"delta_o": raw["delta_o_ppm"].astype(float),
}
).sort_values("bond_length", ignore_index=True)
def load_bond_length_histogram(path=HISTOGRAM_CSV):
"""The sampled carbon-oxygen bond-length distribution as `(centers, counts, bin_width)`: the bin
centers in angstrom, the count in each bin, and the (uniform) bin width. The stored table holds
left bin edges and counts."""
table = pd.read_csv(path)
left_edges = table["bin_left_angstrom"].to_numpy(dtype=float)
counts = table["count"].to_numpy(dtype=float)
bin_width = float(left_edges[1] - left_edges[0]) if len(left_edges) > 1 else 0.01
centers = left_edges + bin_width / 2.0
return centers, counts, bin_width
def equilibrium_vs_extreme_error(comparison=None, window=(1.0, 1.3), extreme=(0.9, 1.4)):
"""The figure's claim as two numbers: the mean absolute carbon error (ppm) for bond lengths
inside the near-equilibrium `window`, and for the non-equilibrium tails outside `extreme`. The
second is far larger, which is the whole point of the panel."""
df = load_comparison() if comparison is None else comparison
near = df[(df["bond_length"] >= window[0]) & (df["bond_length"] <= window[1])]
tails = df[(df["bond_length"] < extreme[0]) | (df["bond_length"] > extreme[1])]
return mae(near["delta_c"], 0), mae(tails["delta_c"], 0)
|