| """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) |
|
|
|
|
|
|