| """Source of truth for the Tables S10/S11 notebook (MagNET-Zero/MagNET-PCM scaling factors). Edit |
| the cell sources here, then regenerate: |
| |
| python3 build_nb_scaling.py si_table_s10_s11_scaling |
| jupyter nbconvert --to notebook --execute --inplace ../si_tables/si_table_s10_s11_scaling.ipynb |
| |
| Regenerating overwrites the .ipynb (clearing its execution outputs). All real code lives in |
| scaling_factors.py (the published tables and the re-derivation from delta-22); the notebook only |
| runs it. The notebook lives in analysis/si_tables/ (grouped by role), not here. |
| |
| Run with no arguments to list the available notebook names. |
| """ |
| import os |
| import sys |
|
|
| from nb_build import md, code, save_notebook as _save |
|
|
|
|
| _BOOTSTRAP = r""" |
| import os, sys |
| |
| # make the in-repo modules importable (not pip-installed) |
| REPO = os.path.abspath("../..") |
| for _p in ("data/delta22", "data/applications", |
| "analysis/code", "analysis/code/shared"): |
| sys.path.insert(0, os.path.join(REPO, _p)) |
| """ |
|
|
| _IMPORTS = r""" |
| import numpy as np |
| import pandas as pd |
| import scaling_factors |
| import build_composite_model |
| import paths |
| from applications_reader import Applications |
| """ |
|
|
| _SETUP = r""" |
| DELTA22 = paths.dataset_file("delta22", root=REPO) |
| XLSX = os.path.join(REPO, "data", "delta22", "delta22_experimental.xlsx") |
| |
| def document_path(name): |
| os.makedirs("documents", exist_ok=True) |
| return os.path.join("documents", name) |
| """ |
|
|
| si_table_s10_s11_scaling = [ |
| md(r""" |
| # Tables S10 and S11: MagNET-Zero/MagNET-PCM scaling factors |
| |
| Per-solvent linear coefficients (intercept, stationary, pcm) mapping MagNET-Zero shieldings plus a |
| MagNET-PCM correction to predicted shifts: ¹H (S10) and ¹³C (S11). The published coefficients are |
| reflection-symmetrized (each MagNET-Zero/PCM shielding averaged over twenty forward passes, the input |
| geometry mirrored for half), which removes the model's reflection-parity error. |
| """), |
| code(_BOOTSTRAP), |
| code(_IMPORTS), |
| code(_SETUP), |
| code(r""" |
| tables = scaling_factors.published_scaling_tables() # {"H": Table S10, "C": Table S11} |
| print("Table S10 (1H):"); display(tables["H"]) |
| print("Table S11 (13C):"); display(tables["C"]) |
| |
| out = document_path("si_table_s10_s11_scaling.xlsx") |
| with pd.ExcelWriter(out) as writer: |
| tables["H"].reset_index().to_excel(writer, sheet_name="Table S10 (1H)", index=False) |
| tables["C"].reset_index().to_excel(writer, sheet_name="Table S11 (13C)", index=False) |
| print("wrote", os.path.relpath(out, REPO)) |
| """), |
| md(r""" |
| ## Reproducibility check: re-derive from delta-22 |
| |
| The published tables are reflection-symmetrized, so reproducing them exactly needs the model |
| checkpoints (`build_scaling_tables(symmetrized=True)`). The checkpoint-free |
| `build_scaling_tables(symmetrized=False)` below fits the raw single-pass shieldings stored in the |
| released delta-22 file and lands within ~0.01 ppm. |
| """), |
| code(r""" |
| derived = scaling_factors.build_scaling_tables(DELTA22, XLSX) # symmetrized=False (checkpoint-free) |
| for nucleus in ("H", "C"): |
| p = tables[nucleus] |
| d = derived[nucleus].reindex(p.index)[p.columns] |
| max_dev = float(np.abs(p.values - d.values).max()) |
| print(f"{nucleus}: largest published-vs-raw-refit deviation = {max_dev:.2e} ppm") |
| assert max_dev < 1.5e-2, f"{nucleus} raw refit drifted too far from the published values" |
| print("raw delta-22 refit reproduces the published tables to the reflection-parity floor") |
| """), |
| md(r""" |
| ## Composite-model coefficients (Figure 5C/5D, SI S15) |
| |
| The natural-products figures use a larger family of per-solvent coefficients fit on delta-22: OLS |
| fits, 1000-seed bootstrap resamples, their RMSE distributions, and the PCM conversion factors. These |
| regenerate from released inputs alone. |
| """), |
| code(r""" |
| reader = Applications(paths.dataset_file("applications", root=REPO)) |
| regenerated = build_composite_model.regenerate(reader) |
| max_dev = build_composite_model.verify(reader, regenerated) |
| print(f"composite_model largest regenerated-vs-stored deviation = {max_dev:.2e}") |
| """), |
| ] |
|
|
|
|
| |
| NOTEBOOKS = { |
| "si_table_s10_s11_scaling": (si_table_s10_s11_scaling, |
| "analysis/si_tables/si_table_s10_s11_scaling.ipynb"), |
| } |
|
|
| if __name__ == "__main__": |
| here = os.path.dirname(os.path.abspath(__file__)) |
| repo = os.path.abspath(os.path.join(here, "..", "..")) |
| names = sys.argv[1:] |
| if not names: |
| print("available names:", ", ".join(NOTEBOOKS)) |
| sys.exit(0) |
| unknown = [n for n in names if n not in NOTEBOOKS] |
| if unknown: |
| raise SystemExit(f"unknown notebook name(s): {unknown}; available: {', '.join(NOTEBOOKS)}") |
| for name in names: |
| cells, relpath = NOTEBOOKS[name] |
| _save(cells, os.path.join(repo, relpath)) |
|
|