ekwan16 commited on
Commit
90b47cf
·
verified ·
1 Parent(s): e250f65

sync code from github@d2dbab26ef57a01a14fbe811b084a3716af12e52

Browse files
README.md CHANGED
@@ -26,10 +26,10 @@ MagNET is a family of neural networks for predicting NMR chemical shifts. This r
26
 
27
  | Model | Description | Details |
28
  |---|---|---|
29
- | **MagNET** | foundation model | - for near-equilibrium geometries <br /> - trained on PBE0/pcSseg-1/gas shieldings |
30
- | **MagNET-Zero** | high-quality gas-phase solute shieldings | - use AIMNet2-optimized geometries <br /> - WP04/pcSseg-2 (<sup>1</sup>H shieldings) <br /> - ωB97X-D/pcSseg-2 (<sup>13</sup>C shieldings) |
31
- | **MagNET-PCM** | implicit solvent corrections | - shielding(PCM) - shielding(gas) <br />- use AIMNet2-optimized geometries <br /> - computed at B3LYP/pcSseg-2/chloroform |
32
- | **MagNET-x** | explicit solvent corrections | - shielding(solute+solvent) - shielding(solute) <br /> - supports chloroform, benzene, methanol, and water <br /> - use classical MD geometries <br /> - trained at PBE0/pcSseg-1 |
33
 
34
  All models use Equiformer-V2 and have approximately 10M weights. Separate weights are given for <sup>1</sup>H and <sup>13</sup>C prediction. Input structures can contain H, C, N, O, F, Cl, and S (inference should not be performed on structures with unsupported elements).
35
 
@@ -37,11 +37,11 @@ All models use Equiformer-V2 and have approximately 10M weights. Separate weight
37
 
38
  | Dataset | Details |
39
  |---|---|
40
- | **_sigma_-shake** | - 4.4M solutes from GDB-13/17 with functional group augmentation <br /> - stationary and quasiclassically perturbed structures at B3LYP-D3(BJ)/6-31G\* <br /> - PBE0/pcSseg-1/gas shieldings |
41
- | **_sigma_-fresh** | - 10K representative natural product, drug-like, sugar, and peptide solutes dissolved in benzene, chloroform, methanol, and water <br /> - ~10 computed poses/solute with many more solvated geometries available <br /> - 462K poses have computed PBE0/pcSseg-1 shieldings |
42
- | **_sigma_-pepper** (part 1) | - GDB molecules with ≤ 10 heavy atoms <br /> - AIMNet2 stationary structures <br /> - PBE0/pcSseg-1 shieldings |
43
- | **_sigma_-pepper** (part 2) | - GDB molecules with ≤ 9 heavy atoms <br /> - AIMNet2 stationary structures <br /> - WP04/pcSseg-2/gas (<sup>1</sup>H shieldings) <br /> - ωB97X-D/pcSseg-2/gas (<sup>13</sup>C shieldings) |
44
- | **_sigma_-concentrate** | - 50K random structures from *sigma*-shake <br /> - PCM(chloroform) corrections at B3LYP/pcSseg-2 |
45
 
46
  ### Installing MagNET
47
 
@@ -128,8 +128,7 @@ for name, indices in sites.items():
128
  print(f"{name:<13} {shifts[indices].mean():6.2f} ppm")
129
  ```
130
 
131
- The predictions land on the measured shifts (experiment is acetone in chloroform, from the delta22
132
- dataset):
133
 
134
  | site | MagNET | experiment (CDCl<sub>3</sub>) |
135
  |---|---|---|
@@ -140,7 +139,7 @@ dataset):
140
  **Notes:**
141
 
142
  - **Passes and symmetry.** `n_passes` (default 10) averages out equivariance error over the specified number of forward passes. If `symmetrize` (default True) is set, an additional `n_passes` are also performed on the mirror image of the input geometry (for a total of `2*n_passes`).
143
- - **Geometry.** MagNET-Zero and MagNET-PCM require AIMNet2-optimized geometries. Do not use other geometries.
144
  - **Components.** Pass `return_components=True` to also get the MagNET-Zero shielding, the MagNET-PCM correction, and the scaling coefficients behind each shift, as a `dict`.
145
  - **Other models.** For raw shieldings and solvent corrections, see the [API documentation](#api-documentation).
146
 
@@ -155,16 +154,16 @@ If you only want to download a single dataset, add
155
 
156
  | dataset | size | contents |
157
  |---|---|---|
158
- | `sigma-shake` | 2.2 GB | main training set: 4.4M GDB solutes, perturbed geometries, PBE0/pcSseg-1 shieldings |
159
- | `sigma-fresh` | 30 GB | explicit-solvent poses: 10K solutes in benzene, chloroform, methanol, and water |
160
  | `sigma-pepper` | 501 MB | AIMNet2 GDB structures used to train MagNET-Zero and MagNET-PCM |
161
  | `sigma-concentrate` | 17 MB | PCM corrections on 50K *sigma*-shake structures |
162
  | `delta22` | 2.5 GB | experimental <sup>1</sup>H/<sup>13</sup>C shifts with matched DFT and MagNET predictions |
163
- | `dft8k` | 12 MB | external benchmark of 7111 organics (Guan and Paton), two levels of theory |
164
- | `gdb_qcd` | 291 MB | quasiclassical-dynamics benchmark of 2461 GDB molecules, for rovibrational corrections |
165
- | `magnet_test_predictions` | 822 MB | MagNET's raw per-atom predictions and DFT targets on the test sets |
166
- | `applications` | 5.1 GB | natural-product evaluation set, with MD geometries and experimental shifts |
167
- | `supertestset_magnet_x` | 1.2 MB | explicit-solvent test set for MagNET-x: isolated vs solvated DFT shieldings |
168
 
169
  ### Reproducing Figures and Tables
170
 
@@ -186,7 +185,7 @@ pip install -r requirements.txt "pytest>=7"
186
  pytest
187
  ```
188
 
189
- The tests use small synthetic fixtures and do not need the downloaded files. Any test that does require the large model weights or datasets is skipped automatically when they are absent, and runs once they have been downloaded.
190
 
191
  ### API Documentation
192
 
@@ -203,7 +202,7 @@ API: `predict_shifts`, `predict_shieldings`, `implicit_solvent_correction`,
203
 
204
  "Chemical Shift Prediction Beyond the Electronic Structure Limit."
205
 
206
- Adams, K.; Wagen, C.C.W.; Wolford, J.; Sak, M.H.; Saurí, J.; Feng, Z.; Bhadauria, A.S.; Bailey, M.A.; Downs, J.S.; Li, S.Z.; Liu, A.I.; Smidt, T.; Paton, R.S.; Liu, R.Y.; Coley, C.W.\*; Kwan, E.E.\*
207
 
208
  *submitted*, July 2026.
209
 
@@ -211,5 +210,5 @@ Adams, K.; Wagen, C.C.W.; Wolford, J.; Sak, M.H.; Saurí, J.; Feng, Z.; Bhadauri
211
 
212
  - The original code, model weights, and datasets in this repository are released under the MIT License (see [`LICENSE`](LICENSE)).
213
  - Third-party literature data redistributed here remains subject to its original publications' terms and should be
214
- cited accordingly: the Goodman CP3 spreadsheet (`data/cp3/`), the Schattenberg and Kaupp NS372
215
- spreadsheet (`data/ns372/`), and the DFT8K benchmark (`data/dft8k/`, from Guan and Paton).
 
26
 
27
  | Model | Description | Details |
28
  |---|---|---|
29
+ | **MagNET** | foundation model | <ul><li>for near-equilibrium geometries</li><li>trained on PBE0/pcSseg-1/gas shieldings</li></ul> |
30
+ | **MagNET-Zero** | high-quality gas-phase solute shieldings | <ul><li>use AIMNet2-optimized geometries</li><li>WP04/pcSseg-2 (<sup>1</sup>H shieldings)</li><li>ωB97X-D/pcSseg-2 (<sup>13</sup>C shieldings)</li></ul> |
31
+ | **MagNET-PCM** | implicit solvent corrections | <ul><li>shielding(PCM) - shielding(gas)</li><li>use AIMNet2-optimized geometries</li><li>computed at B3LYP-D3(BJ)/pcSseg-2/chloroform</li></ul> |
32
+ | **MagNET-x** | explicit solvent corrections | <ul><li>shielding(solute+solvent) - shielding(solute)</li><li>supports chloroform, benzene, methanol, and water</li><li>use classical MD geometries</li><li>trained at PBE0/pcSseg-1</li></ul> |
33
 
34
  All models use Equiformer-V2 and have approximately 10M weights. Separate weights are given for <sup>1</sup>H and <sup>13</sup>C prediction. Input structures can contain H, C, N, O, F, Cl, and S (inference should not be performed on structures with unsupported elements).
35
 
 
37
 
38
  | Dataset | Details |
39
  |---|---|
40
+ | **_sigma_-shake** | <ul><li>4.4M solutes from GDB-13/17 with functional group augmentation</li><li>stationary and quasiclassically perturbed structures at B3LYP-D3(BJ)/6-31G\*</li><li>PBE0/pcSseg-1/gas shieldings</li></ul> |
41
+ | **_sigma_-fresh** | <ul><li>10K representative natural product, drug-like, sugar, and peptide solutes dissolved in benzene, chloroform, methanol, and water</li><li>~10 computed poses/solute with many more solvated geometries available</li><li>462K poses have computed PBE0/pcSseg-1 shieldings</li></ul> |
42
+ | **_sigma_-pepper** (part 1) | <ul><li>GDB molecules with ≤ 10 heavy atoms</li><li>AIMNet2 stationary structures</li><li>PBE0/pcSseg-1 shieldings</li></ul> |
43
+ | **_sigma_-pepper** (part 2) | <ul><li>GDB molecules with ≤ 9 heavy atoms</li><li>AIMNet2 stationary structures</li><li>WP04/pcSseg-2/gas (<sup>1</sup>H shieldings)</li><li>ωB97X-D/pcSseg-2/gas (<sup>13</sup>C shieldings)</li></ul> |
44
+ | **_sigma_-concentrate** | <ul><li>50K random structures from *sigma*-shake</li><li>PCM(chloroform) corrections at B3LYP/pcSseg-2</li></ul> |
45
 
46
  ### Installing MagNET
47
 
 
128
  print(f"{name:<13} {shifts[indices].mean():6.2f} ppm")
129
  ```
130
 
131
+ The predictions are close to experiment:
 
132
 
133
  | site | MagNET | experiment (CDCl<sub>3</sub>) |
134
  |---|---|---|
 
139
  **Notes:**
140
 
141
  - **Passes and symmetry.** `n_passes` (default 10) averages out equivariance error over the specified number of forward passes. If `symmetrize` (default True) is set, an additional `n_passes` are also performed on the mirror image of the input geometry (for a total of `2*n_passes`).
142
+ - **Geometry.** MagNET-Zero and MagNET-PCM require [AIMNet2](https://github.com/isayevlab/AIMNet2)-optimized geometries. Do not use other geometries.
143
  - **Components.** Pass `return_components=True` to also get the MagNET-Zero shielding, the MagNET-PCM correction, and the scaling coefficients behind each shift, as a `dict`.
144
  - **Other models.** For raw shieldings and solvent corrections, see the [API documentation](#api-documentation).
145
 
 
154
 
155
  | dataset | size | contents |
156
  |---|---|---|
157
+ | `sigma-shake` | 2.2 GB | 4.4M GDB solutes, perturbed geometries, PBE0/pcSseg-1 shieldings |
158
+ | `sigma-fresh` | 30 GB | 10K solutes in explicit benzene, chloroform, methanol, and water |
159
  | `sigma-pepper` | 501 MB | AIMNet2 GDB structures used to train MagNET-Zero and MagNET-PCM |
160
  | `sigma-concentrate` | 17 MB | PCM corrections on 50K *sigma*-shake structures |
161
  | `delta22` | 2.5 GB | experimental <sup>1</sup>H/<sup>13</sup>C shifts with matched DFT and MagNET predictions |
162
+ | `dft8k` | 12 MB | external benchmark of 7111 organics |
163
+ | `gdb_qcd` | 291 MB | quasiclassical dynamics of 2461 GDB molecules |
164
+ | `magnet_test_predictions` | 822 MB | MagNET predictions and DFT targets |
165
+ | `applications` | 5.1 GB | natural product MD geometries and experimental shifts |
166
+ | `supertestset_magnet_x` | 1.2 MB | explicit solvent test set for MagNET-x |
167
 
168
  ### Reproducing Figures and Tables
169
 
 
185
  pytest
186
  ```
187
 
188
+ The tests use small synthetic fixtures. If the large files are present, then more comprehensive tests will run. The `magnet` package tests need the Option B checkout with the inference stack installed (`pip install -r magnet/requirements.txt`); without PyTorch they are skipped.
189
 
190
  ### API Documentation
191
 
 
202
 
203
  "Chemical Shift Prediction Beyond the Electronic Structure Limit."
204
 
205
+ Adams, K.; Wagen, C.C.; Wolford, J.; Sak, M.H.; Saurí, J.; Feng, Z.; Bhadauria, A.S.; Bailey, M.A.; Downs, J.S.; Li, S.Z.; Liu, A.I.; Smidt, T.; Paton, R.S.; Liu, R.Y.; Coley, C.W.\*; Kwan, E.E.\*
206
 
207
  *submitted*, July 2026.
208
 
 
210
 
211
  - The original code, model weights, and datasets in this repository are released under the MIT License (see [`LICENSE`](LICENSE)).
212
  - Third-party literature data redistributed here remains subject to its original publications' terms and should be
213
+ cited accordingly: CP3 (`data/cp3/`, Smith and Goodman), NS372
214
+ (`data/ns372/`, Schattenberg and Kaupp), and DFT8K (`data/dft8k/`, Guan and Paton).
analysis/code/build_nb_composite_models.py CHANGED
@@ -54,13 +54,13 @@ N_SPLITS = 250
54
 
55
  si_composite_models_ablations = [
56
  md(r"""
57
- # SI Figure S15: composite-formula ablation workbook (`ablations.xlsx`)
58
 
59
- Rebuilds the `ablations.xlsx` workbook behind ~32 of SI Figure S15's images: ~25 composite-formula
60
- variants (stationary geometry plus some combination of implicit PCM, explicit Desmond, and
61
- rovibrational QCD corrections) across all 12 delta-22 solvents, at two reference levels:
62
- DSD-PBEP86/pcSseg-3 (geometry PBE0/tz) and the MagNET-Zero training reference (WP04/pcSseg-2 for ¹H,
63
- wB97X-D/pcSseg-2 for ¹³C).
64
  """),
65
  code(_BOOTSTRAP),
66
  code(_IMPORTS),
@@ -93,13 +93,13 @@ PCM-vs-Desmond table.
93
  """),
94
  code(r"""
95
  for nucleus, label in [("H", "Proton"), ("C", "Carbon")]:
96
- corr = delta22.si_s15_feature_correlations(query_df_dft, nucleus)
97
  nuc_label = "1H" if nucleus == "H" else "13C"
98
  nuc_title = "$^{1}$H" if nucleus == "H" else "$^{13}$C"
99
  composite_plots.plot_feature_correlation_heatmap(
100
  corr["r"], vmin=-1, vmax=1, cmap="RdBu",
101
  title=f"Solvent-Averaged Pearson $r$ Correlation Matrix for {nuc_title}",
102
- save_path=figure_path(f"si_figure_s15_feature_corr_r_{nuc_label}.png"))
103
  plt.show()
104
  """),
105
  code(r"""
@@ -107,7 +107,7 @@ pcm_desmond_corr = delta22.pcm_desmond_correlation_by_solvent(query_df_dft)
107
  print("PCM vs. Desmond Pearson R by nucleus and solvent:")
108
  display(pcm_desmond_corr.round(3))
109
  composite_plots.plot_pcm_desmond_correlation_table(pcm_desmond_corr,
110
- save_path=figure_path("si_figure_s15_pcm_desmond_corr_table.png"))
111
  plt.show()
112
  """),
113
  ]
 
54
 
55
  si_composite_models_ablations = [
56
  md(r"""
57
+ # Composite-formula ablation workbook
58
 
59
+ Rebuilds the composite-formula ablation grid: ~25 composite-formula variants (stationary geometry
60
+ plus some combination of implicit PCM, explicit Desmond, and rovibrational QCD corrections) across
61
+ all 12 delta-22 solvents, at two reference levels: DSD-PBEP86/pcSseg-3 (geometry PBE0/tz) and the
62
+ MagNET-Zero training reference (WP04/pcSseg-2 for ¹H, ωB97X-D/pcSseg-2 for ¹³C). Also reports the
63
+ solvent-averaged correlations between the composite-model features.
64
  """),
65
  code(_BOOTSTRAP),
66
  code(_IMPORTS),
 
93
  """),
94
  code(r"""
95
  for nucleus, label in [("H", "Proton"), ("C", "Carbon")]:
96
+ corr = delta22.ablations_feature_correlations(query_df_dft, nucleus)
97
  nuc_label = "1H" if nucleus == "H" else "13C"
98
  nuc_title = "$^{1}$H" if nucleus == "H" else "$^{13}$C"
99
  composite_plots.plot_feature_correlation_heatmap(
100
  corr["r"], vmin=-1, vmax=1, cmap="RdBu",
101
  title=f"Solvent-Averaged Pearson $r$ Correlation Matrix for {nuc_title}",
102
+ save_path=figure_path(f"ablations_feature_corr_r_{nuc_label}.png"))
103
  plt.show()
104
  """),
105
  code(r"""
 
107
  print("PCM vs. Desmond Pearson R by nucleus and solvent:")
108
  display(pcm_desmond_corr.round(3))
109
  composite_plots.plot_pcm_desmond_correlation_table(pcm_desmond_corr,
110
+ save_path=figure_path("ablations_pcm_desmond_corr_table.png"))
111
  plt.show()
112
  """),
113
  ]
analysis/code/build_nb_leveling.py CHANGED
@@ -29,7 +29,7 @@ Two independent NMR shielding datasets, analyzed separately (never pooled):
29
  | Dataset | Source | Methods | Nuclei | Reference |
30
  |---|---|---|---|---|
31
  | **NS372** | Schattenberg & Kaupp, *JCTC* **17**, 7602 (2021) | 44 DFT/WFT functionals | ¹H ¹¹B ¹³C ¹⁵N ¹⁷O ¹⁹F ³¹P ³³S | CCSD(T)/pcSseg-3 |
32
- | **delta22** | in-house `delta22.hdf5` | 18 gas-phase functionals, largest basis (pcSseg-3; mp2 → pcSseg-2), PBE0/cc-pVTZ geometry | ¹H ¹³C | DSD-PBEP86 (highest-rung in-set method, stands in for CCSD(T)) |
33
  """
34
 
35
  _CONFIG_MD = "## 1. Configuration"
@@ -69,8 +69,11 @@ def figure_path(name):
69
  return os.path.join("figures", name)
70
 
71
  # self-clean: this notebook builds PNG names dynamically (one per nucleus), so drop any
72
- # previously written figures before regenerating (glob on a missing folder returns [])
73
- for _stale in glob.glob(os.path.join("figures", "*.png")):
 
 
 
74
  os.remove(_stale)
75
 
76
  pd.set_option('display.width', 150)
@@ -120,14 +123,14 @@ _NS372_DEF_MD = r"""
120
 
121
  Conventional GIAO shieldings for 44 functionals across 8 main-group nuclei, with a
122
  CCSD(T)/pcSseg-3 reference. Kaupp Reduced-Set exclusions (F₃⁻, O₃, BH - multireference
123
- outliers) are applied. Input: `ct1c00919_si_002.xlsx`.
124
  """
125
 
126
  _DELTA22_DEF_MD = r"""
127
  ## 3. delta22 - definitions
128
 
129
- Gas-phase conventional GIAO shieldings from `delta22.hdf5`: 18 functionals at their
130
- largest available basis (pcSseg-3; plain `mp2` only to pcSseg-2), at the PBE0/cc-pVTZ
131
  geometry. Observations are pooled ¹H / ¹³C atom sites across all 22 solutes. There is no
132
  CCSD(T) reference in the file; **DSD-PBEP86 is used as the reference** for delta22 - it
133
  is the highest-rung double-hybrid available in this method set and stands in for CCSD(T)
@@ -138,7 +141,7 @@ _LOAD_MD = r"""
138
  ## 4. Load datasets + global colour scale
139
 
140
  Run the loaders, analyse every nucleus, and compute the figure-wide
141
- `-log10(1-|r|)` maximum (`GLOBAL_VMAX`) used as the colour scale on every correlation matrix
142
  below, for NS372 and delta22 alike.
143
  """
144
 
@@ -190,8 +193,9 @@ sum_ns372.round(5)
190
  md(r"""
191
  ### 5.2 Per-method scaled RMSE vs CCSD(T)
192
 
193
- `scaled_rmse` = RMSE of residuals after a per-method linear fit
194
- `sigma_method ~ a*sigma_CCSD(T) + b` - the error that survives empirical linear scaling.
 
195
  """),
196
  code(r"""
197
  rmse_ns372 = pd.DataFrame({nuc: res_ns372[nuc]['scaled_rmse'] for nuc in res_ns372})
 
29
  | Dataset | Source | Methods | Nuclei | Reference |
30
  |---|---|---|---|---|
31
  | **NS372** | Schattenberg & Kaupp, *JCTC* **17**, 7602 (2021) | 44 DFT/WFT functionals | ¹H ¹¹B ¹³C ¹⁵N ¹⁷O ¹⁹F ³¹P ³³S | CCSD(T)/pcSseg-3 |
32
+ | **delta22** | in-house delta-22 set | 18 gas-phase functionals, largest basis (pcSseg-3; mp2 → pcSseg-2), PBE0/cc-pVTZ geometry | ¹H ¹³C | DSD-PBEP86 (highest-rung in-set method, stands in for CCSD(T)) |
33
  """
34
 
35
  _CONFIG_MD = "## 1. Configuration"
 
69
  return os.path.join("figures", name)
70
 
71
  # self-clean: this notebook builds PNG names dynamically (one per nucleus), so drop any
72
+ # previously written figures before regenerating. Scope the delete to THIS figure's own prefix:
73
+ # the figures/ folder is shared with the other notebooks in this directory, and a full reproduce
74
+ # run executes them in name order, so a broad "figures/*.png" wipe would destroy the panels written
75
+ # by notebooks that sort before this one. (glob on a missing folder returns [])
76
+ for _stale in glob.glob(os.path.join("figures", "si_figure_s03_*.png")):
77
  os.remove(_stale)
78
 
79
  pd.set_option('display.width', 150)
 
123
 
124
  Conventional GIAO shieldings for 44 functionals across 8 main-group nuclei, with a
125
  CCSD(T)/pcSseg-3 reference. Kaupp Reduced-Set exclusions (F₃⁻, O₃, BH - multireference
126
+ outliers) are applied. Input: the Kaupp NS372 supporting-information spreadsheet.
127
  """
128
 
129
  _DELTA22_DEF_MD = r"""
130
  ## 3. delta22 - definitions
131
 
132
+ Gas-phase conventional GIAO shieldings from the delta-22 set: 18 functionals at their
133
+ largest available basis (pcSseg-3; plain MP2 only to pcSseg-2), at the PBE0/cc-pVTZ
134
  geometry. Observations are pooled ¹H / ¹³C atom sites across all 22 solutes. There is no
135
  CCSD(T) reference in the file; **DSD-PBEP86 is used as the reference** for delta22 - it
136
  is the highest-rung double-hybrid available in this method set and stands in for CCSD(T)
 
141
  ## 4. Load datasets + global colour scale
142
 
143
  Run the loaders, analyse every nucleus, and compute the figure-wide
144
+ -log10(1-|r|) maximum used as the colour scale on every correlation matrix
145
  below, for NS372 and delta22 alike.
146
  """
147
 
 
193
  md(r"""
194
  ### 5.2 Per-method scaled RMSE vs CCSD(T)
195
 
196
+ Scaled RMSE = RMSE of residuals after a per-method linear fit of each method's shieldings
197
+ against the CCSD(T) reference (slope and intercept) - the error that survives empirical
198
+ linear scaling.
199
  """),
200
  code(r"""
201
  rmse_ns372 = pd.DataFrame({nuc: res_ns372[nuc]['scaled_rmse'] for nuc in res_ns372})
analysis/code/build_nb_scaling.py CHANGED
@@ -92,9 +92,9 @@ print("Symmetrized deployment Table S11 (13C):"); display(symmetrized["C"])
92
  md(r"""
93
  ## Composite-model coefficients (Figure 5C/5D, SI S15)
94
 
95
- The natural-products figures use a larger family of per-solvent coefficients fit on delta-22 (the
96
- `composite_model` group in `applications.hdf5`): OLS fits, 1000-seed bootstrap resamples, their RMSE
97
- distributions, and the PCM conversion factors. The group regenerates from released inputs alone.
98
  """),
99
  code(r"""
100
  reader = Applications(paths.dataset_file("applications", root=REPO))
 
92
  md(r"""
93
  ## Composite-model coefficients (Figure 5C/5D, SI S15)
94
 
95
+ The natural-products figures use a larger family of per-solvent coefficients fit on delta-22: OLS
96
+ fits, 1000-seed bootstrap resamples, their RMSE distributions, and the PCM conversion factors. These
97
+ regenerate from released inputs alone.
98
  """),
99
  code(r"""
100
  reader = Applications(paths.dataset_file("applications", root=REPO))
analysis/code/composite_models.py CHANGED
@@ -1,4 +1,4 @@
1
- """Reproduces the delta-22 composite-formula ablation study behind SI Figure S15's Excel screenshots.
2
 
3
  Rebuilds `ablations.xlsx`: ~25 composite-formula variants (stationary geometry plus some combination
4
  of implicit PCM, explicit Desmond, and rovibrational QCD corrections) across all 12 delta-22 solvents,
@@ -110,8 +110,8 @@ CARBON_FORMULA_CONFIG = {
110
  _FORMULA_CONFIG = {"H": PROTON_FORMULA_CONFIG, "C": CARBON_FORMULA_CONFIG}
111
 
112
  # This ablation study fits plain OLS composite formulas, not an ML model. The canonical SI text
113
- # for Figure S15 states that "since no ML was used in this particular analysis, all 22 molecules
114
- # were considered, including nitromethane" -- other, ML-related analyses (e.g. scaling_factors.py's
115
  # EXCLUDE_SOLUTES) drop nitromethane as an ML-training outlier; this module's default excludes nothing.
116
  EXCLUDE_SOLUTES = ()
117
 
 
1
+ """Reproduces the delta-22 composite-formula ablation study (the ablations.xlsx workbook).
2
 
3
  Rebuilds `ablations.xlsx`: ~25 composite-formula variants (stationary geometry plus some combination
4
  of implicit PCM, explicit Desmond, and rovibrational QCD corrections) across all 12 delta-22 solvents,
 
110
  _FORMULA_CONFIG = {"H": PROTON_FORMULA_CONFIG, "C": CARBON_FORMULA_CONFIG}
111
 
112
  # This ablation study fits plain OLS composite formulas, not an ML model. The canonical SI text
113
+ # for this ablation analysis states that "since no ML was used in this particular analysis, all 22
114
+ # molecules were considered, including nitromethane" -- other, ML-related analyses (e.g. scaling_factors.py's
115
  # EXCLUDE_SOLUTES) drop nitromethane as an ML-training outlier; this module's default excludes nothing.
116
  EXCLUDE_SOLUTES = ()
117
 
analysis/code/composite_plots.py CHANGED
@@ -1,6 +1,6 @@
1
- """Correlation-heatmap panels for the composite-model ablation SI figure (Figure S15).
2
 
3
- Two seaborn heatmaps behind Figure S15's feature-correlation panels: a per-nucleus lower-triangle
4
  correlation matrix among the five composite-model features (stationary shielding, PCM, Desmond, its
5
  vibrational analogue, QCD), and a PCM-vs-Desmond Pearson r table by nucleus and solvent. The notebook
6
  supplies the correlation table(s) computed by delta22.py and calls these functions directly.
 
1
+ """Correlation-heatmap panels for the composite-formula ablation workbook.
2
 
3
+ Two seaborn heatmaps behind the ablation workbook's feature-correlation panels: a per-nucleus lower-triangle
4
  correlation matrix among the five composite-model features (stationary shielding, PCM, Desmond, its
5
  vibrational analogue, QCD), and a PCM-vs-Desmond Pearson r table by nucleus and solvent. The notebook
6
  supplies the correlation table(s) computed by delta22.py and calls these functions directly.
analysis/code/delta22.py CHANGED
@@ -721,15 +721,15 @@ def correlation_matrix(query_df, value_col, sites_index, column_var):
721
  return pivot.corr()
722
 
723
 
724
- # SI Figure S15 "Correlations Between Features": correlations among the composite features within
725
- # one nucleus and solvent.
726
  FEATURE_CORRELATION_COLUMNS = ("stationary", "pcm", "desmond", "desmond_vib", "qcd")
727
 
728
 
729
  def feature_correlation_matrix(query_df, nucleus, solvent, cols=FEATURE_CORRELATION_COLUMNS, squared=False):
730
  """Pearson correlation matrix (R^2 if squared=True) between the feature columns, within one
731
  nucleus and solvent, over sites where both features have a value. query_df must be filtered to a
732
- single (sap_nmr_method, sap_basis, sap_geometry_type); si_s15_feature_correlations does that."""
733
  sub = query_df[(query_df["nucleus"] == nucleus) & (query_df["solvent"] == solvent)][list(cols)]
734
  sub = sub.dropna(how="all")
735
  if len(sub) < 2:
@@ -740,7 +740,7 @@ def feature_correlation_matrix(query_df, nucleus, solvent, cols=FEATURE_CORRELAT
740
 
741
  def average_feature_correlation_matrix(query_df, nucleus, solvents, cols=FEATURE_CORRELATION_COLUMNS,
742
  squared=False):
743
- """Cell-by-cell nanmean of feature_correlation_matrix over the solvents (SI Figure S15's
744
  solvent-averaged matrix). nanmean so a solvent with too few sites for one feature pair drops out
745
  of that cell alone, not the whole average. Same single-level-of-theory requirement."""
746
  mats = [feature_correlation_matrix(query_df, nucleus, s, cols, squared) for s in solvents]
@@ -750,7 +750,7 @@ def average_feature_correlation_matrix(query_df, nucleus, solvents, cols=FEATURE
750
  return pd.DataFrame(avg, index=mats[0].index, columns=mats[0].columns)
751
 
752
 
753
- def si_s15_feature_correlations(query_df_dft, nucleus, solvents=None):
754
  """Solvent-averaged feature correlations for one nucleus from the unfiltered
755
  load_query_df_dft(...) output: filters to the reference level
756
  (MAGNET_PCM_OUTPUT_METHODS[nucleus], pcSseg2, aimnet2), then returns {"r": Pearson R matrix,
@@ -765,9 +765,9 @@ def si_s15_feature_correlations(query_df_dft, nucleus, solvents=None):
765
 
766
 
767
  def pcm_desmond_correlation_by_solvent(query_df_dft, solvents=None):
768
- """SI Figure S15's PCM-vs-Desmond table: per nucleus and solvent, the Pearson correlation
769
  between the PCM (implicit) and Desmond (explicit) corrections. Filters to the reference level
770
- per nucleus itself (same as si_s15_feature_correlations). Returns a DataFrame indexed by
771
  nucleus, one column per solvent."""
772
  solvents = list(solvents) if solvents is not None else sorted(query_df_dft["solvent"].unique())
773
  rows = {}
 
721
  return pivot.corr()
722
 
723
 
724
+ # Composite-formula ablation workbook, "Correlations Between Features": correlations among the
725
+ # composite features within one nucleus and solvent.
726
  FEATURE_CORRELATION_COLUMNS = ("stationary", "pcm", "desmond", "desmond_vib", "qcd")
727
 
728
 
729
  def feature_correlation_matrix(query_df, nucleus, solvent, cols=FEATURE_CORRELATION_COLUMNS, squared=False):
730
  """Pearson correlation matrix (R^2 if squared=True) between the feature columns, within one
731
  nucleus and solvent, over sites where both features have a value. query_df must be filtered to a
732
+ single (sap_nmr_method, sap_basis, sap_geometry_type); ablations_feature_correlations does that."""
733
  sub = query_df[(query_df["nucleus"] == nucleus) & (query_df["solvent"] == solvent)][list(cols)]
734
  sub = sub.dropna(how="all")
735
  if len(sub) < 2:
 
740
 
741
  def average_feature_correlation_matrix(query_df, nucleus, solvents, cols=FEATURE_CORRELATION_COLUMNS,
742
  squared=False):
743
+ """Cell-by-cell nanmean of feature_correlation_matrix over the solvents (the ablation workbook's
744
  solvent-averaged matrix). nanmean so a solvent with too few sites for one feature pair drops out
745
  of that cell alone, not the whole average. Same single-level-of-theory requirement."""
746
  mats = [feature_correlation_matrix(query_df, nucleus, s, cols, squared) for s in solvents]
 
750
  return pd.DataFrame(avg, index=mats[0].index, columns=mats[0].columns)
751
 
752
 
753
+ def ablations_feature_correlations(query_df_dft, nucleus, solvents=None):
754
  """Solvent-averaged feature correlations for one nucleus from the unfiltered
755
  load_query_df_dft(...) output: filters to the reference level
756
  (MAGNET_PCM_OUTPUT_METHODS[nucleus], pcSseg2, aimnet2), then returns {"r": Pearson R matrix,
 
765
 
766
 
767
  def pcm_desmond_correlation_by_solvent(query_df_dft, solvents=None):
768
+ """The ablation workbook's PCM-vs-Desmond table: per nucleus and solvent, the Pearson correlation
769
  between the PCM (implicit) and Desmond (explicit) corrections. Filters to the reference level
770
+ per nucleus itself (same as ablations_feature_correlations). Returns a DataFrame indexed by
771
  nucleus, one column per solvent."""
772
  solvents = list(solvents) if solvents is not None else sorted(query_df_dft["solvent"].unique())
773
  rows = {}
analysis/code/leveling.py CHANGED
@@ -274,43 +274,42 @@ DELTA22_REF = 'DSD-PBEP86'
274
 
275
  def load_delta22(path):
276
  """Load the delta22 shielding matrix, sorted by family, for 1H and 13C sites."""
277
- f = h5py.File(path, 'r')
278
- mn = [m.decode() for m in f['conventional_nmr_method_names'][:]]
279
- order = ['hf', 'blyp_d3bj', 'bp86_d3bj', 'b97d3_d3bj', 'tpsstpss_d3bj',
280
- 'b3lyp_d3bj', 'pbe0_d3bj', 'm062x_d3', 'wb97xd', 'wp04', 'wc04',
281
- 'B2PLYP', 'mPW2PLYP', 'B2GP_PLYP', 'dsd_pbep86', 'revdsd_pbep86',
282
- 'mp2', 'dlpno_mp2']
283
- disp = {'hf': 'HF', 'blyp_d3bj': 'BLYP-D3', 'bp86_d3bj': 'BP86-D3',
284
- 'b97d3_d3bj': 'B97D3', 'tpsstpss_d3bj': 'TPSS-D3', 'b3lyp_d3bj': 'B3LYP-D3',
285
- 'pbe0_d3bj': 'PBE0-D3', 'm062x_d3': 'M06-2X-D3', 'wb97xd': 'ωB97X-D',
286
- 'wp04': 'WP04', 'wc04': 'WC04', 'B2PLYP': 'B2PLYP', 'mPW2PLYP': 'mPW2PLYP',
287
- 'B2GP_PLYP': 'B2GP-PLYP', 'dsd_pbep86': 'DSD-PBEP86',
288
- 'revdsd_pbep86': 'revDSD-PBEP86', 'mp2': 'MP2', 'dlpno_mp2': 'DLPNO-MP2'}
289
- fam = {'hf': 'WFT', 'blyp_d3bj': 'GGA', 'bp86_d3bj': 'GGA', 'b97d3_d3bj': 'GGA',
290
- 'tpsstpss_d3bj': 'mGGA', 'b3lyp_d3bj': 'GH', 'pbe0_d3bj': 'GH',
291
- 'm062x_d3': 'GH', 'wb97xd': 'RSH', 'wp04': 'GH', 'wc04': 'GH',
292
- 'B2PLYP': 'DH', 'mPW2PLYP': 'DH', 'B2GP_PLYP': 'DH', 'dsd_pbep86': 'DH',
293
- 'revdsd_pbep86': 'DH', 'mp2': 'WFT', 'dlpno_mp2': 'WFT'}
294
- gas = {}
295
- for i, m in enumerate(mn):
296
- func, basis, smodel, _ = m.split(',')
297
- if smodel == 'gas':
298
- gas.setdefault(func, {})[basis] = i
299
- sel, labels, families = [], [], []
300
- for func in order:
301
- b = gas[func]
302
- big = 'pcSseg3' if 'pcSseg3' in b else ('pcSseg2' if 'pcSseg2' in b else 'pcSseg1')
303
- sel.append(b[big]); labels.append(disp[func]); families.append(fam[func])
304
- GEOM = 1 # geometries axis: 0 = AIMNet2, 1 = PBE0/cc-pVTZ
305
- rows = {1: [], 6: []}
306
- for s in f['solutes']:
307
- an = f['solutes'][s]['atomic_numbers'][:]
308
- cs = _decode_fixed_point(
309
- f['solutes'][s]['stationary_and_pcm']['conventional_shieldings'][:, GEOM, :])
310
- for ai, z in enumerate(an):
311
- if z in (1, 6):
312
- rows[int(z)].append(cs[sel, ai])
313
- f.close()
314
  out = {}
315
  for nuc, z in (('1H', 1), ('13C', 6)):
316
  M = np.array(rows[z])
 
274
 
275
  def load_delta22(path):
276
  """Load the delta22 shielding matrix, sorted by family, for 1H and 13C sites."""
277
+ with h5py.File(path, 'r') as f:
278
+ mn = [m.decode() for m in f['conventional_nmr_method_names'][:]]
279
+ order = ['hf', 'blyp_d3bj', 'bp86_d3bj', 'b97d3_d3bj', 'tpsstpss_d3bj',
280
+ 'b3lyp_d3bj', 'pbe0_d3bj', 'm062x_d3', 'wb97xd', 'wp04', 'wc04',
281
+ 'B2PLYP', 'mPW2PLYP', 'B2GP_PLYP', 'dsd_pbep86', 'revdsd_pbep86',
282
+ 'mp2', 'dlpno_mp2']
283
+ disp = {'hf': 'HF', 'blyp_d3bj': 'BLYP-D3', 'bp86_d3bj': 'BP86-D3',
284
+ 'b97d3_d3bj': 'B97D3', 'tpsstpss_d3bj': 'TPSS-D3', 'b3lyp_d3bj': 'B3LYP-D3',
285
+ 'pbe0_d3bj': 'PBE0-D3', 'm062x_d3': 'M06-2X-D3', 'wb97xd': 'ωB97X-D',
286
+ 'wp04': 'WP04', 'wc04': 'WC04', 'B2PLYP': 'B2PLYP', 'mPW2PLYP': 'mPW2PLYP',
287
+ 'B2GP_PLYP': 'B2GP-PLYP', 'dsd_pbep86': 'DSD-PBEP86',
288
+ 'revdsd_pbep86': 'revDSD-PBEP86', 'mp2': 'MP2', 'dlpno_mp2': 'DLPNO-MP2'}
289
+ fam = {'hf': 'WFT', 'blyp_d3bj': 'GGA', 'bp86_d3bj': 'GGA', 'b97d3_d3bj': 'GGA',
290
+ 'tpsstpss_d3bj': 'mGGA', 'b3lyp_d3bj': 'GH', 'pbe0_d3bj': 'GH',
291
+ 'm062x_d3': 'GH', 'wb97xd': 'RSH', 'wp04': 'GH', 'wc04': 'GH',
292
+ 'B2PLYP': 'DH', 'mPW2PLYP': 'DH', 'B2GP_PLYP': 'DH', 'dsd_pbep86': 'DH',
293
+ 'revdsd_pbep86': 'DH', 'mp2': 'WFT', 'dlpno_mp2': 'WFT'}
294
+ gas = {}
295
+ for i, m in enumerate(mn):
296
+ func, basis, smodel, _ = m.split(',')
297
+ if smodel == 'gas':
298
+ gas.setdefault(func, {})[basis] = i
299
+ sel, labels, families = [], [], []
300
+ for func in order:
301
+ b = gas[func]
302
+ big = 'pcSseg3' if 'pcSseg3' in b else ('pcSseg2' if 'pcSseg2' in b else 'pcSseg1')
303
+ sel.append(b[big]); labels.append(disp[func]); families.append(fam[func])
304
+ GEOM = 1 # geometries axis: 0 = AIMNet2, 1 = PBE0/cc-pVTZ
305
+ rows = {1: [], 6: []}
306
+ for s in f['solutes']:
307
+ an = f['solutes'][s]['atomic_numbers'][:]
308
+ cs = _decode_fixed_point(
309
+ f['solutes'][s]['stationary_and_pcm']['conventional_shieldings'][:, GEOM, :])
310
+ for ai, z in enumerate(an):
311
+ if z in (1, 6):
312
+ rows[int(z)].append(cs[sel, ai])
 
313
  out = {}
314
  for nuc, z in (('1H', 1), ('13C', 6)):
315
  M = np.array(rows[z])
analysis/code/test_delta22_analysis.py CHANGED
@@ -790,7 +790,7 @@ def test_fig2a_reproduces_published_magnet_point():
790
  assert got == pytest.approx(expected, abs=1e-4), f"{nucleus}: {got} vs {expected}"
791
 
792
 
793
- # --- SI Figure S15's "Correlations Between Features" ---------------------------------------------
794
 
795
  def test_feature_correlation_matrix_synthetic():
796
  # a and b perfectly correlated, c independent noise uncorrelated with either
@@ -841,9 +841,10 @@ def test_average_feature_correlation_matrix_ignores_a_solvent_with_too_few_sites
841
  @pytest.mark.skipif(
842
  not (os.path.exists(REAL_H5) and os.path.exists(REAL_XLSX)),
843
  reason="real delta22.hdf5 / experimental xlsx not present")
844
- def test_si_s15_feature_correlations_reproduces_published_matrices():
845
  """Every entry of both nuclei's published Pearson R and R^2 "Correlations Between Features"
846
- matrices (SI Figure S15), reproduced from the released data to 3 decimal places. Guards against
 
847
  pooling all DFT methods/bases/geometries together instead of filtering to the MagNET-Zero
848
  reference level, which throws entries off by up to 0.03."""
849
  dft = D.load_query_df_dft(REAL_H5, REAL_XLSX, verbose=False)
@@ -866,7 +867,7 @@ def test_si_s15_feature_correlations_reproduces_published_matrices():
866
  ("qcd", "stationary"): 0.380, ("qcd", "pcm"): 0.404, ("qcd", "desmond"): 0.189, ("qcd", "desmond_vib"): 0.021},
867
  }
868
  for nucleus in ("H", "C"):
869
- corr = D.si_s15_feature_correlations(dft, nucleus)
870
  for (a, b), expected in published_r[nucleus].items():
871
  assert corr["r"].loc[a, b] == pytest.approx(expected, abs=5e-4), f"{nucleus} r[{a},{b}]"
872
  for (a, b), expected in published_r2[nucleus].items():
 
790
  assert got == pytest.approx(expected, abs=1e-4), f"{nucleus}: {got} vs {expected}"
791
 
792
 
793
+ # --- Composite-formula ablation workbook's "Correlations Between Features" -----------------------
794
 
795
  def test_feature_correlation_matrix_synthetic():
796
  # a and b perfectly correlated, c independent noise uncorrelated with either
 
841
  @pytest.mark.skipif(
842
  not (os.path.exists(REAL_H5) and os.path.exists(REAL_XLSX)),
843
  reason="real delta22.hdf5 / experimental xlsx not present")
844
+ def test_ablations_feature_correlations_reproduces_published_matrices():
845
  """Every entry of both nuclei's published Pearson R and R^2 "Correlations Between Features"
846
+ matrices (the composite-formula ablation workbook), reproduced from the released data to 3
847
+ decimal places. Guards against
848
  pooling all DFT methods/bases/geometries together instead of filtering to the MagNET-Zero
849
  reference level, which throws entries off by up to 0.03."""
850
  dft = D.load_query_df_dft(REAL_H5, REAL_XLSX, verbose=False)
 
867
  ("qcd", "stationary"): 0.380, ("qcd", "pcm"): 0.404, ("qcd", "desmond"): 0.189, ("qcd", "desmond_vib"): 0.021},
868
  }
869
  for nucleus in ("H", "C"):
870
+ corr = D.ablations_feature_correlations(dft, nucleus)
871
  for (a, b), expected in published_r[nucleus].items():
872
  assert corr["r"].loc[a, b] == pytest.approx(expected, abs=5e-4), f"{nucleus} r[{a},{b}]"
873
  for (a, b), expected in published_r2[nucleus].items():
analysis/manuscript_figures/fig5b_dft8k.ipynb CHANGED
@@ -144,13 +144,7 @@
144
  "cell_type": "markdown",
145
  "id": "5f4276e7",
146
  "metadata": {},
147
- "source": [
148
- "## Functional-group error breakdown\n",
149
- "\n",
150
- "Mean absolute residual for six functional groups (Carbonyls, Amines, Sulfonyl, Pyridines, Furans,\n",
151
- "Nitroso), via RDKit SMARTS matching over every molecule's SMILES\n",
152
- "(`dft8k_residuals.FUNCTIONAL_GROUP_SMARTS`)."
153
- ]
154
  },
155
  {
156
  "cell_type": "code",
 
144
  "cell_type": "markdown",
145
  "id": "5f4276e7",
146
  "metadata": {},
147
+ "source": "## Functional-group error breakdown\n\nMean absolute residual for six functional groups (Carbonyls, Amines, Sulfonyl, Pyridines, Furans,\nNitroso), via RDKit SMARTS matching over every molecule's SMILES."
 
 
 
 
 
 
148
  },
149
  {
150
  "cell_type": "code",
analysis/si_figures/si_composite_models_ablations.ipynb CHANGED
@@ -2,22 +2,22 @@
2
  "cells": [
3
  {
4
  "cell_type": "markdown",
5
- "id": "bfdd4bf5",
6
  "metadata": {},
7
  "source": [
8
- "# SI Figure S15: composite-formula ablation workbook (`ablations.xlsx`)\n",
9
  "\n",
10
- "Rebuilds the `ablations.xlsx` workbook behind ~32 of SI Figure S15's images: ~25 composite-formula\n",
11
- "variants (stationary geometry plus some combination of implicit PCM, explicit Desmond, and\n",
12
- "rovibrational QCD corrections) across all 12 delta-22 solvents, at two reference levels:\n",
13
- "DSD-PBEP86/pcSseg-3 (geometry PBE0/tz) and the MagNET-Zero training reference (WP04/pcSseg-2 for ¹H,\n",
14
- "wB97X-D/pcSseg-2 for ¹³C)."
15
  ]
16
  },
17
  {
18
  "cell_type": "code",
19
  "execution_count": null,
20
- "id": "cb68d1d6",
21
  "metadata": {},
22
  "outputs": [],
23
  "source": [
@@ -32,7 +32,7 @@
32
  {
33
  "cell_type": "code",
34
  "execution_count": null,
35
- "id": "20914013",
36
  "metadata": {},
37
  "outputs": [],
38
  "source": [
@@ -47,7 +47,7 @@
47
  {
48
  "cell_type": "code",
49
  "execution_count": null,
50
- "id": "26ab5657",
51
  "metadata": {},
52
  "outputs": [],
53
  "source": [
@@ -69,7 +69,7 @@
69
  {
70
  "cell_type": "code",
71
  "execution_count": null,
72
- "id": "76d31292",
73
  "metadata": {},
74
  "outputs": [],
75
  "source": [
@@ -80,7 +80,7 @@
80
  },
81
  {
82
  "cell_type": "markdown",
83
- "id": "2c351f43",
84
  "metadata": {},
85
  "source": [
86
  "## Preview: one formula's mean test RMSE at the DSD-PBEP86 reference level"
@@ -89,7 +89,7 @@
89
  {
90
  "cell_type": "code",
91
  "execution_count": null,
92
- "id": "eb03ec52",
93
  "metadata": {},
94
  "outputs": [],
95
  "source": [
@@ -103,7 +103,7 @@
103
  },
104
  {
105
  "cell_type": "markdown",
106
- "id": "ee9136d7",
107
  "metadata": {},
108
  "source": [
109
  "## Build the full ablations workbook (both reference levels, both nuclei)"
@@ -112,7 +112,7 @@
112
  {
113
  "cell_type": "code",
114
  "execution_count": null,
115
- "id": "5c9fbbd3",
116
  "metadata": {},
117
  "outputs": [],
118
  "source": [
@@ -122,7 +122,7 @@
122
  },
123
  {
124
  "cell_type": "markdown",
125
- "id": "78a693c7",
126
  "metadata": {},
127
  "source": [
128
  "## Correlations Between Features\n",
@@ -135,25 +135,25 @@
135
  {
136
  "cell_type": "code",
137
  "execution_count": null,
138
- "id": "9ef593a6",
139
  "metadata": {},
140
  "outputs": [],
141
  "source": [
142
  "for nucleus, label in [(\"H\", \"Proton\"), (\"C\", \"Carbon\")]:\n",
143
- " corr = delta22.si_s15_feature_correlations(query_df_dft, nucleus)\n",
144
  " nuc_label = \"1H\" if nucleus == \"H\" else \"13C\"\n",
145
  " nuc_title = \"$^{1}$H\" if nucleus == \"H\" else \"$^{13}$C\"\n",
146
  " composite_plots.plot_feature_correlation_heatmap(\n",
147
  " corr[\"r\"], vmin=-1, vmax=1, cmap=\"RdBu\",\n",
148
  " title=f\"Solvent-Averaged Pearson $r$ Correlation Matrix for {nuc_title}\",\n",
149
- " save_path=figure_path(f\"si_figure_s15_feature_corr_r_{nuc_label}.png\"))\n",
150
  "plt.show()"
151
  ]
152
  },
153
  {
154
  "cell_type": "code",
155
  "execution_count": null,
156
- "id": "5bc382f2",
157
  "metadata": {},
158
  "outputs": [],
159
  "source": [
@@ -161,7 +161,7 @@
161
  "print(\"PCM vs. Desmond Pearson R by nucleus and solvent:\")\n",
162
  "display(pcm_desmond_corr.round(3))\n",
163
  "composite_plots.plot_pcm_desmond_correlation_table(pcm_desmond_corr,\n",
164
- " save_path=figure_path(\"si_figure_s15_pcm_desmond_corr_table.png\"))\n",
165
  "plt.show()"
166
  ]
167
  }
 
2
  "cells": [
3
  {
4
  "cell_type": "markdown",
5
+ "id": "77f6c813",
6
  "metadata": {},
7
  "source": [
8
+ "# Composite-formula ablation workbook\n",
9
  "\n",
10
+ "Rebuilds the composite-formula ablation grid: ~25 composite-formula variants (stationary geometry\n",
11
+ "plus some combination of implicit PCM, explicit Desmond, and rovibrational QCD corrections) across\n",
12
+ "all 12 delta-22 solvents, at two reference levels: DSD-PBEP86/pcSseg-3 (geometry PBE0/tz) and the\n",
13
+ "MagNET-Zero training reference (WP04/pcSseg-2 for ¹H, ωB97X-D/pcSseg-2 for ¹³C). Also reports the\n",
14
+ "solvent-averaged correlations between the composite-model features."
15
  ]
16
  },
17
  {
18
  "cell_type": "code",
19
  "execution_count": null,
20
+ "id": "e00f8b39",
21
  "metadata": {},
22
  "outputs": [],
23
  "source": [
 
32
  {
33
  "cell_type": "code",
34
  "execution_count": null,
35
+ "id": "b3acc33b",
36
  "metadata": {},
37
  "outputs": [],
38
  "source": [
 
47
  {
48
  "cell_type": "code",
49
  "execution_count": null,
50
+ "id": "a909ba74",
51
  "metadata": {},
52
  "outputs": [],
53
  "source": [
 
69
  {
70
  "cell_type": "code",
71
  "execution_count": null,
72
+ "id": "5df608e9",
73
  "metadata": {},
74
  "outputs": [],
75
  "source": [
 
80
  },
81
  {
82
  "cell_type": "markdown",
83
+ "id": "b7bc63a7",
84
  "metadata": {},
85
  "source": [
86
  "## Preview: one formula's mean test RMSE at the DSD-PBEP86 reference level"
 
89
  {
90
  "cell_type": "code",
91
  "execution_count": null,
92
+ "id": "ad79f75e",
93
  "metadata": {},
94
  "outputs": [],
95
  "source": [
 
103
  },
104
  {
105
  "cell_type": "markdown",
106
+ "id": "7ae7d282",
107
  "metadata": {},
108
  "source": [
109
  "## Build the full ablations workbook (both reference levels, both nuclei)"
 
112
  {
113
  "cell_type": "code",
114
  "execution_count": null,
115
+ "id": "f9acdb9e",
116
  "metadata": {},
117
  "outputs": [],
118
  "source": [
 
122
  },
123
  {
124
  "cell_type": "markdown",
125
+ "id": "66102dfb",
126
  "metadata": {},
127
  "source": [
128
  "## Correlations Between Features\n",
 
135
  {
136
  "cell_type": "code",
137
  "execution_count": null,
138
+ "id": "208d343b",
139
  "metadata": {},
140
  "outputs": [],
141
  "source": [
142
  "for nucleus, label in [(\"H\", \"Proton\"), (\"C\", \"Carbon\")]:\n",
143
+ " corr = delta22.ablations_feature_correlations(query_df_dft, nucleus)\n",
144
  " nuc_label = \"1H\" if nucleus == \"H\" else \"13C\"\n",
145
  " nuc_title = \"$^{1}$H\" if nucleus == \"H\" else \"$^{13}$C\"\n",
146
  " composite_plots.plot_feature_correlation_heatmap(\n",
147
  " corr[\"r\"], vmin=-1, vmax=1, cmap=\"RdBu\",\n",
148
  " title=f\"Solvent-Averaged Pearson $r$ Correlation Matrix for {nuc_title}\",\n",
149
+ " save_path=figure_path(f\"ablations_feature_corr_r_{nuc_label}.png\"))\n",
150
  "plt.show()"
151
  ]
152
  },
153
  {
154
  "cell_type": "code",
155
  "execution_count": null,
156
+ "id": "4e3701cd",
157
  "metadata": {},
158
  "outputs": [],
159
  "source": [
 
161
  "print(\"PCM vs. Desmond Pearson R by nucleus and solvent:\")\n",
162
  "display(pcm_desmond_corr.round(3))\n",
163
  "composite_plots.plot_pcm_desmond_correlation_table(pcm_desmond_corr,\n",
164
+ " save_path=figure_path(\"ablations_pcm_desmond_corr_table.png\"))\n",
165
  "plt.show()"
166
  ]
167
  }
analysis/si_figures/si_figure_s03_leveling.ipynb CHANGED
@@ -2,7 +2,7 @@
2
  "cells": [
3
  {
4
  "cell_type": "markdown",
5
- "id": "b61775ea",
6
  "metadata": {},
7
  "source": [
8
  "# SI Figure S3: inter-method correlation and PCA of NS372 and delta-22 shieldings\n",
@@ -13,7 +13,7 @@
13
  },
14
  {
15
  "cell_type": "markdown",
16
- "id": "aeb46df4",
17
  "metadata": {},
18
  "source": [
19
  "Two independent NMR shielding datasets, analyzed separately (never pooled):\n",
@@ -21,12 +21,12 @@
21
  "| Dataset | Source | Methods | Nuclei | Reference |\n",
22
  "|---|---|---|---|---|\n",
23
  "| **NS372** | Schattenberg & Kaupp, *JCTC* **17**, 7602 (2021) | 44 DFT/WFT functionals | ¹H ¹¹B ¹³C ¹⁵N ¹⁷O ¹⁹F ³¹P ³³S | CCSD(T)/pcSseg-3 |\n",
24
- "| **delta22** | in-house `delta22.hdf5` | 18 gas-phase functionals, largest basis (pcSseg-3; mp2 → pcSseg-2), PBE0/cc-pVTZ geometry | ¹H ¹³C | DSD-PBEP86 (highest-rung in-set method, stands in for CCSD(T)) |"
25
  ]
26
  },
27
  {
28
  "cell_type": "markdown",
29
- "id": "d0ce838b",
30
  "metadata": {},
31
  "source": [
32
  "## 1. Configuration"
@@ -35,7 +35,7 @@
35
  {
36
  "cell_type": "code",
37
  "execution_count": null,
38
- "id": "62157a75",
39
  "metadata": {},
40
  "outputs": [],
41
  "source": [
@@ -49,7 +49,7 @@
49
  {
50
  "cell_type": "code",
51
  "execution_count": null,
52
- "id": "8ea46824",
53
  "metadata": {},
54
  "outputs": [],
55
  "source": [
@@ -65,7 +65,7 @@
65
  {
66
  "cell_type": "code",
67
  "execution_count": null,
68
- "id": "34796b33",
69
  "metadata": {},
70
  "outputs": [],
71
  "source": [
@@ -85,8 +85,11 @@
85
  " return os.path.join(\"figures\", name)\n",
86
  "\n",
87
  "# self-clean: this notebook builds PNG names dynamically (one per nucleus), so drop any\n",
88
- "# previously written figures before regenerating (glob on a missing folder returns [])\n",
89
- "for _stale in glob.glob(os.path.join(\"figures\", \"*.png\")):\n",
 
 
 
90
  " os.remove(_stale)\n",
91
  "\n",
92
  "pd.set_option('display.width', 150)\n",
@@ -96,7 +99,7 @@
96
  {
97
  "cell_type": "code",
98
  "execution_count": null,
99
- "id": "a402f2fc",
100
  "metadata": {},
101
  "outputs": [],
102
  "source": [
@@ -139,25 +142,25 @@
139
  },
140
  {
141
  "cell_type": "markdown",
142
- "id": "52b5cdac",
143
  "metadata": {},
144
  "source": [
145
  "## 2. NS372 (Kaupp) - definitions\n",
146
  "\n",
147
  "Conventional GIAO shieldings for 44 functionals across 8 main-group nuclei, with a\n",
148
  "CCSD(T)/pcSseg-3 reference. Kaupp Reduced-Set exclusions (F₃⁻, O₃, BH - multireference\n",
149
- "outliers) are applied. Input: `ct1c00919_si_002.xlsx`."
150
  ]
151
  },
152
  {
153
  "cell_type": "markdown",
154
- "id": "f87c7141",
155
  "metadata": {},
156
  "source": [
157
  "## 3. delta22 - definitions\n",
158
  "\n",
159
- "Gas-phase conventional GIAO shieldings from `delta22.hdf5`: 18 functionals at their\n",
160
- "largest available basis (pcSseg-3; plain `mp2` only to pcSseg-2), at the PBE0/cc-pVTZ\n",
161
  "geometry. Observations are pooled ¹H / ¹³C atom sites across all 22 solutes. There is no\n",
162
  "CCSD(T) reference in the file; **DSD-PBEP86 is used as the reference** for delta22 - it\n",
163
  "is the highest-rung double-hybrid available in this method set and stands in for CCSD(T)\n",
@@ -166,20 +169,20 @@
166
  },
167
  {
168
  "cell_type": "markdown",
169
- "id": "be87913f",
170
  "metadata": {},
171
  "source": [
172
  "## 4. Load datasets + global colour scale\n",
173
  "\n",
174
  "Run the loaders, analyse every nucleus, and compute the figure-wide\n",
175
- "`-log10(1-|r|)` maximum (`GLOBAL_VMAX`) used as the colour scale on every correlation matrix\n",
176
  "below, for NS372 and delta22 alike."
177
  ]
178
  },
179
  {
180
  "cell_type": "code",
181
  "execution_count": null,
182
- "id": "d0a24460",
183
  "metadata": {},
184
  "outputs": [],
185
  "source": [
@@ -203,7 +206,7 @@
203
  },
204
  {
205
  "cell_type": "markdown",
206
- "id": "0d07150e",
207
  "metadata": {},
208
  "source": [
209
  "## 5. NS372 results"
@@ -211,7 +214,7 @@
211
  },
212
  {
213
  "cell_type": "markdown",
214
- "id": "747989ff",
215
  "metadata": {},
216
  "source": [
217
  "### 5.1 Leveling diagnostics - NS372"
@@ -220,7 +223,7 @@
220
  {
221
  "cell_type": "code",
222
  "execution_count": null,
223
- "id": "52a133ee",
224
  "metadata": {},
225
  "outputs": [],
226
  "source": [
@@ -231,19 +234,20 @@
231
  },
232
  {
233
  "cell_type": "markdown",
234
- "id": "a385e6f5",
235
  "metadata": {},
236
  "source": [
237
  "### 5.2 Per-method scaled RMSE vs CCSD(T)\n",
238
  "\n",
239
- "`scaled_rmse` = RMSE of residuals after a per-method linear fit\n",
240
- "`sigma_method ~ a*sigma_CCSD(T) + b` - the error that survives empirical linear scaling."
 
241
  ]
242
  },
243
  {
244
  "cell_type": "code",
245
  "execution_count": null,
246
- "id": "319abc43",
247
  "metadata": {},
248
  "outputs": [],
249
  "source": [
@@ -256,7 +260,7 @@
256
  },
257
  {
258
  "cell_type": "markdown",
259
- "id": "59cb8672",
260
  "metadata": {},
261
  "source": [
262
  "### 5.3 Inter-method correlation matrices - NS372 (one nucleus per file)"
@@ -265,7 +269,7 @@
265
  {
266
  "cell_type": "code",
267
  "execution_count": null,
268
- "id": "73c90f1e",
269
  "metadata": {},
270
  "outputs": [],
271
  "source": [
@@ -282,7 +286,7 @@
282
  },
283
  {
284
  "cell_type": "markdown",
285
- "id": "33da4dd0",
286
  "metadata": {},
287
  "source": [
288
  "### 5.4 PC1/PC2 structure - NS372 (one nucleus per file)"
@@ -291,7 +295,7 @@
291
  {
292
  "cell_type": "code",
293
  "execution_count": null,
294
- "id": "034bf193",
295
  "metadata": {},
296
  "outputs": [],
297
  "source": [
@@ -307,7 +311,7 @@
307
  },
308
  {
309
  "cell_type": "markdown",
310
- "id": "3ec202cf",
311
  "metadata": {},
312
  "source": [
313
  "### 5.5 delta22 panels (lead the published figure: corr ¹H/¹³C + PCA)\n",
@@ -319,7 +323,7 @@
319
  {
320
  "cell_type": "code",
321
  "execution_count": null,
322
- "id": "fc1d57ab",
323
  "metadata": {},
324
  "outputs": [],
325
  "source": [
@@ -345,7 +349,7 @@
345
  },
346
  {
347
  "cell_type": "markdown",
348
- "id": "4c1e4350",
349
  "metadata": {},
350
  "source": [
351
  "## 6. Combined summary (both datasets)"
@@ -354,7 +358,7 @@
354
  {
355
  "cell_type": "code",
356
  "execution_count": null,
357
- "id": "185ce298",
358
  "metadata": {},
359
  "outputs": [],
360
  "source": [
 
2
  "cells": [
3
  {
4
  "cell_type": "markdown",
5
+ "id": "3a2b8d51",
6
  "metadata": {},
7
  "source": [
8
  "# SI Figure S3: inter-method correlation and PCA of NS372 and delta-22 shieldings\n",
 
13
  },
14
  {
15
  "cell_type": "markdown",
16
+ "id": "0fb6f798",
17
  "metadata": {},
18
  "source": [
19
  "Two independent NMR shielding datasets, analyzed separately (never pooled):\n",
 
21
  "| Dataset | Source | Methods | Nuclei | Reference |\n",
22
  "|---|---|---|---|---|\n",
23
  "| **NS372** | Schattenberg & Kaupp, *JCTC* **17**, 7602 (2021) | 44 DFT/WFT functionals | ¹H ¹¹B ¹³C ¹⁵N ¹⁷O ¹⁹F ³¹P ³³S | CCSD(T)/pcSseg-3 |\n",
24
+ "| **delta22** | in-house delta-22 set | 18 gas-phase functionals, largest basis (pcSseg-3; mp2 → pcSseg-2), PBE0/cc-pVTZ geometry | ¹H ¹³C | DSD-PBEP86 (highest-rung in-set method, stands in for CCSD(T)) |"
25
  ]
26
  },
27
  {
28
  "cell_type": "markdown",
29
+ "id": "3ff927f7",
30
  "metadata": {},
31
  "source": [
32
  "## 1. Configuration"
 
35
  {
36
  "cell_type": "code",
37
  "execution_count": null,
38
+ "id": "9e5a25b8",
39
  "metadata": {},
40
  "outputs": [],
41
  "source": [
 
49
  {
50
  "cell_type": "code",
51
  "execution_count": null,
52
+ "id": "06e0333d",
53
  "metadata": {},
54
  "outputs": [],
55
  "source": [
 
65
  {
66
  "cell_type": "code",
67
  "execution_count": null,
68
+ "id": "fbd3d68f",
69
  "metadata": {},
70
  "outputs": [],
71
  "source": [
 
85
  " return os.path.join(\"figures\", name)\n",
86
  "\n",
87
  "# self-clean: this notebook builds PNG names dynamically (one per nucleus), so drop any\n",
88
+ "# previously written figures before regenerating. Scope the delete to THIS figure's own prefix:\n",
89
+ "# the figures/ folder is shared with the other notebooks in this directory, and a full reproduce\n",
90
+ "# run executes them in name order, so a broad \"figures/*.png\" wipe would destroy the panels written\n",
91
+ "# by notebooks that sort before this one. (glob on a missing folder returns [])\n",
92
+ "for _stale in glob.glob(os.path.join(\"figures\", \"si_figure_s03_*.png\")):\n",
93
  " os.remove(_stale)\n",
94
  "\n",
95
  "pd.set_option('display.width', 150)\n",
 
99
  {
100
  "cell_type": "code",
101
  "execution_count": null,
102
+ "id": "84ddcb3f",
103
  "metadata": {},
104
  "outputs": [],
105
  "source": [
 
142
  },
143
  {
144
  "cell_type": "markdown",
145
+ "id": "4fbeeddb",
146
  "metadata": {},
147
  "source": [
148
  "## 2. NS372 (Kaupp) - definitions\n",
149
  "\n",
150
  "Conventional GIAO shieldings for 44 functionals across 8 main-group nuclei, with a\n",
151
  "CCSD(T)/pcSseg-3 reference. Kaupp Reduced-Set exclusions (F₃⁻, O₃, BH - multireference\n",
152
+ "outliers) are applied. Input: the Kaupp NS372 supporting-information spreadsheet."
153
  ]
154
  },
155
  {
156
  "cell_type": "markdown",
157
+ "id": "34326933",
158
  "metadata": {},
159
  "source": [
160
  "## 3. delta22 - definitions\n",
161
  "\n",
162
+ "Gas-phase conventional GIAO shieldings from the delta-22 set: 18 functionals at their\n",
163
+ "largest available basis (pcSseg-3; plain MP2 only to pcSseg-2), at the PBE0/cc-pVTZ\n",
164
  "geometry. Observations are pooled ¹H / ¹³C atom sites across all 22 solutes. There is no\n",
165
  "CCSD(T) reference in the file; **DSD-PBEP86 is used as the reference** for delta22 - it\n",
166
  "is the highest-rung double-hybrid available in this method set and stands in for CCSD(T)\n",
 
169
  },
170
  {
171
  "cell_type": "markdown",
172
+ "id": "eedf9b3d",
173
  "metadata": {},
174
  "source": [
175
  "## 4. Load datasets + global colour scale\n",
176
  "\n",
177
  "Run the loaders, analyse every nucleus, and compute the figure-wide\n",
178
+ "-log10(1-|r|) maximum used as the colour scale on every correlation matrix\n",
179
  "below, for NS372 and delta22 alike."
180
  ]
181
  },
182
  {
183
  "cell_type": "code",
184
  "execution_count": null,
185
+ "id": "4bec0cf4",
186
  "metadata": {},
187
  "outputs": [],
188
  "source": [
 
206
  },
207
  {
208
  "cell_type": "markdown",
209
+ "id": "667fef3c",
210
  "metadata": {},
211
  "source": [
212
  "## 5. NS372 results"
 
214
  },
215
  {
216
  "cell_type": "markdown",
217
+ "id": "c5f2cbda",
218
  "metadata": {},
219
  "source": [
220
  "### 5.1 Leveling diagnostics - NS372"
 
223
  {
224
  "cell_type": "code",
225
  "execution_count": null,
226
+ "id": "8b9b9449",
227
  "metadata": {},
228
  "outputs": [],
229
  "source": [
 
234
  },
235
  {
236
  "cell_type": "markdown",
237
+ "id": "2b666d04",
238
  "metadata": {},
239
  "source": [
240
  "### 5.2 Per-method scaled RMSE vs CCSD(T)\n",
241
  "\n",
242
+ "Scaled RMSE = RMSE of residuals after a per-method linear fit of each method's shieldings\n",
243
+ "against the CCSD(T) reference (slope and intercept) - the error that survives empirical\n",
244
+ "linear scaling."
245
  ]
246
  },
247
  {
248
  "cell_type": "code",
249
  "execution_count": null,
250
+ "id": "4020359a",
251
  "metadata": {},
252
  "outputs": [],
253
  "source": [
 
260
  },
261
  {
262
  "cell_type": "markdown",
263
+ "id": "ba763232",
264
  "metadata": {},
265
  "source": [
266
  "### 5.3 Inter-method correlation matrices - NS372 (one nucleus per file)"
 
269
  {
270
  "cell_type": "code",
271
  "execution_count": null,
272
+ "id": "133a5053",
273
  "metadata": {},
274
  "outputs": [],
275
  "source": [
 
286
  },
287
  {
288
  "cell_type": "markdown",
289
+ "id": "0fa55480",
290
  "metadata": {},
291
  "source": [
292
  "### 5.4 PC1/PC2 structure - NS372 (one nucleus per file)"
 
295
  {
296
  "cell_type": "code",
297
  "execution_count": null,
298
+ "id": "a37b38c6",
299
  "metadata": {},
300
  "outputs": [],
301
  "source": [
 
311
  },
312
  {
313
  "cell_type": "markdown",
314
+ "id": "bc460701",
315
  "metadata": {},
316
  "source": [
317
  "### 5.5 delta22 panels (lead the published figure: corr ¹H/¹³C + PCA)\n",
 
323
  {
324
  "cell_type": "code",
325
  "execution_count": null,
326
+ "id": "74e02974",
327
  "metadata": {},
328
  "outputs": [],
329
  "source": [
 
349
  },
350
  {
351
  "cell_type": "markdown",
352
+ "id": "49825011",
353
  "metadata": {},
354
  "source": [
355
  "## 6. Combined summary (both datasets)"
 
358
  {
359
  "cell_type": "code",
360
  "execution_count": null,
361
+ "id": "bffe5ff3",
362
  "metadata": {},
363
  "outputs": [],
364
  "source": [
analysis/si_tables/si_table_s10_s11_scaling.ipynb CHANGED
@@ -2,7 +2,7 @@
2
  "cells": [
3
  {
4
  "cell_type": "markdown",
5
- "id": "badbc639",
6
  "metadata": {},
7
  "source": [
8
  "# Tables S10 and S11: MagNET-Zero/MagNET-PCM scaling factors\n",
@@ -14,7 +14,7 @@
14
  {
15
  "cell_type": "code",
16
  "execution_count": null,
17
- "id": "672ff802",
18
  "metadata": {},
19
  "outputs": [],
20
  "source": [
@@ -30,7 +30,7 @@
30
  {
31
  "cell_type": "code",
32
  "execution_count": null,
33
- "id": "1a1fa320",
34
  "metadata": {},
35
  "outputs": [],
36
  "source": [
@@ -46,7 +46,7 @@
46
  {
47
  "cell_type": "code",
48
  "execution_count": null,
49
- "id": "deba4a1e",
50
  "metadata": {},
51
  "outputs": [],
52
  "source": [
@@ -61,7 +61,7 @@
61
  {
62
  "cell_type": "code",
63
  "execution_count": null,
64
- "id": "e408fac2",
65
  "metadata": {},
66
  "outputs": [],
67
  "source": [
@@ -78,7 +78,7 @@
78
  },
79
  {
80
  "cell_type": "markdown",
81
- "id": "b6094980",
82
  "metadata": {},
83
  "source": [
84
  "## Reproducibility check: re-derive from delta-22"
@@ -87,7 +87,7 @@
87
  {
88
  "cell_type": "code",
89
  "execution_count": null,
90
- "id": "19f08d14",
91
  "metadata": {},
92
  "outputs": [],
93
  "source": [
@@ -103,7 +103,7 @@
103
  },
104
  {
105
  "cell_type": "markdown",
106
- "id": "8edf48b3",
107
  "metadata": {},
108
  "source": [
109
  "## Deployment tables with reflection symmetrization\n",
@@ -116,7 +116,7 @@
116
  {
117
  "cell_type": "code",
118
  "execution_count": null,
119
- "id": "448f2e9f",
120
  "metadata": {},
121
  "outputs": [],
122
  "source": [
@@ -127,20 +127,20 @@
127
  },
128
  {
129
  "cell_type": "markdown",
130
- "id": "ca457bc6",
131
  "metadata": {},
132
  "source": [
133
  "## Composite-model coefficients (Figure 5C/5D, SI S15)\n",
134
  "\n",
135
- "The natural-products figures use a larger family of per-solvent coefficients fit on delta-22 (the\n",
136
- "`composite_model` group in `applications.hdf5`): OLS fits, 1000-seed bootstrap resamples, their RMSE\n",
137
- "distributions, and the PCM conversion factors. The group regenerates from released inputs alone."
138
  ]
139
  },
140
  {
141
  "cell_type": "code",
142
  "execution_count": null,
143
- "id": "47c3d1e7",
144
  "metadata": {},
145
  "outputs": [],
146
  "source": [
 
2
  "cells": [
3
  {
4
  "cell_type": "markdown",
5
+ "id": "41675f1b",
6
  "metadata": {},
7
  "source": [
8
  "# Tables S10 and S11: MagNET-Zero/MagNET-PCM scaling factors\n",
 
14
  {
15
  "cell_type": "code",
16
  "execution_count": null,
17
+ "id": "fb163d74",
18
  "metadata": {},
19
  "outputs": [],
20
  "source": [
 
30
  {
31
  "cell_type": "code",
32
  "execution_count": null,
33
+ "id": "177bdc88",
34
  "metadata": {},
35
  "outputs": [],
36
  "source": [
 
46
  {
47
  "cell_type": "code",
48
  "execution_count": null,
49
+ "id": "7f56b61d",
50
  "metadata": {},
51
  "outputs": [],
52
  "source": [
 
61
  {
62
  "cell_type": "code",
63
  "execution_count": null,
64
+ "id": "44a384c9",
65
  "metadata": {},
66
  "outputs": [],
67
  "source": [
 
78
  },
79
  {
80
  "cell_type": "markdown",
81
+ "id": "219c400a",
82
  "metadata": {},
83
  "source": [
84
  "## Reproducibility check: re-derive from delta-22"
 
87
  {
88
  "cell_type": "code",
89
  "execution_count": null,
90
+ "id": "b525b479",
91
  "metadata": {},
92
  "outputs": [],
93
  "source": [
 
103
  },
104
  {
105
  "cell_type": "markdown",
106
+ "id": "14d0ed95",
107
  "metadata": {},
108
  "source": [
109
  "## Deployment tables with reflection symmetrization\n",
 
116
  {
117
  "cell_type": "code",
118
  "execution_count": null,
119
+ "id": "111de099",
120
  "metadata": {},
121
  "outputs": [],
122
  "source": [
 
127
  },
128
  {
129
  "cell_type": "markdown",
130
+ "id": "31089f6c",
131
  "metadata": {},
132
  "source": [
133
  "## Composite-model coefficients (Figure 5C/5D, SI S15)\n",
134
  "\n",
135
+ "The natural-products figures use a larger family of per-solvent coefficients fit on delta-22: OLS\n",
136
+ "fits, 1000-seed bootstrap resamples, their RMSE distributions, and the PCM conversion factors. These\n",
137
+ "regenerate from released inputs alone."
138
  ]
139
  },
140
  {
141
  "cell_type": "code",
142
  "execution_count": null,
143
+ "id": "d2d9f7b4",
144
  "metadata": {},
145
  "outputs": [],
146
  "source": [
data/applications/applications_reader.py CHANGED
@@ -28,7 +28,7 @@ SHELL_SIZES = [50, 150, 250, 350, 450, 550, 650]
28
  def _decode(values):
29
  """Turn stored whole numbers back into real values; blanks where data is missing."""
30
  values = np.asarray(values)
31
- if values.dtype == np.int32:
32
  out = values.astype(np.float64) / _SCALE
33
  out[values == _MISSING_MARKER] = np.nan
34
  return out
 
28
  def _decode(values):
29
  """Turn stored whole numbers back into real values; blanks where data is missing."""
30
  values = np.asarray(values)
31
+ if np.issubdtype(values.dtype, np.integer):
32
  out = values.astype(np.float64) / _SCALE
33
  out[values == _MISSING_MARKER] = np.nan
34
  return out
data/applications/test_applications.py CHANGED
@@ -102,6 +102,17 @@ def test_decode_roundtrip_and_missing():
102
  assert np.isnan(_decode(enc_with_miss)[-1])
103
 
104
 
 
 
 
 
 
 
 
 
 
 
 
105
  def test_solutes_and_solvents_group_excluded(fake_release):
106
  assert set(fake_release.solutes()) == {"vomicine", "isomer_1E"}
107
 
 
102
  assert np.isnan(_decode(enc_with_miss)[-1])
103
 
104
 
105
+ def test_decode_int64_is_still_scaled():
106
+ # a rebuild that stored the columns as int64 (any integer width) must decode the same way as
107
+ # int32; the old exact `dtype == np.int32` check fell through and returned raw scaled integers.
108
+ vals = np.array([1.2345, -50.0, 0.0], dtype=np.float64)
109
+ enc = _enc(vals).astype(np.int64)
110
+ out = _decode(enc)
111
+ assert np.allclose(out, vals, atol=5e-5)
112
+ enc_with_miss = np.append(enc, np.int64(-2147483648))
113
+ assert np.isnan(_decode(enc_with_miss)[-1])
114
+
115
+
116
  def test_solutes_and_solvents_group_excluded(fake_release):
117
  assert set(fake_release.solutes()) == {"vomicine", "isomer_1E"}
118
 
data/delta22/delta22_reader.py CHANGED
@@ -39,7 +39,7 @@ _MISSING_MARKER = -2147483648
39
 
40
  def _decode_fixed_point(values):
41
  values = np.asarray(values)
42
- if values.dtype == np.int32:
43
  out = values.astype(np.float64) / _FIXED_POINT_SCALE
44
  out[values == _MISSING_MARKER] = np.nan
45
  return out
@@ -74,7 +74,7 @@ def _sort_and_parse_raw_shieldings(shield_df):
74
  value_list = [v for v in value_list if v]
75
  try:
76
  return [float(i) for i in value_list]
77
- except:
78
  return []
79
 
80
  def _to_float_list(x):
 
39
 
40
  def _decode_fixed_point(values):
41
  values = np.asarray(values)
42
+ if np.issubdtype(values.dtype, np.integer):
43
  out = values.astype(np.float64) / _FIXED_POINT_SCALE
44
  out[values == _MISSING_MARKER] = np.nan
45
  return out
 
74
  value_list = [v for v in value_list if v]
75
  try:
76
  return [float(i) for i in value_list]
77
+ except (ValueError, TypeError):
78
  return []
79
 
80
  def _to_float_list(x):
data/delta22/test_delta22.py CHANGED
@@ -144,6 +144,17 @@ def test_decode_passes_floats_through():
144
  np.testing.assert_array_equal(_decode_fixed_point(f), f)
145
 
146
 
 
 
 
 
 
 
 
 
 
 
 
147
  # ---- the full reader against the synthetic file ----
148
  EXPECTED_COLUMNS = ["experimental", "stationary", "qcd", "pcm",
149
  "desmond", "openMM", "desmond_vib", "openMM_vib"]
 
144
  np.testing.assert_array_equal(_decode_fixed_point(f), f)
145
 
146
 
147
+ def test_decode_int64_is_still_scaled():
148
+ # a rebuild that stored the fixed-point columns as int64 (any integer width) must decode the
149
+ # same way as int32; the old exact `dtype == np.int32` check fell through and returned the raw
150
+ # scaled integers as if they were ppm.
151
+ enc = np.array([2070700, _MISSING_MARKER, -150000], np.int64)
152
+ out = _decode_fixed_point(enc)
153
+ assert out[0] == pytest.approx(207.07)
154
+ assert np.isnan(out[1])
155
+ assert out[2] == pytest.approx(-15.0)
156
+
157
+
158
  # ---- the full reader against the synthetic file ----
159
  EXPECTED_COLUMNS = ["experimental", "stationary", "qcd", "pcm",
160
  "desmond", "openMM", "desmond_vib", "openMM_vib"]
data/delta50/delta50.hdf5 CHANGED
Binary files a/data/delta50/delta50.hdf5 and b/data/delta50/delta50.hdf5 differ
 
magnet/eqV2/edge_rot_mat.py CHANGED
@@ -5,15 +5,17 @@ def init_edge_rot_mat(edge_distance_vec):
5
  edge_vec_0 = edge_distance_vec
6
  edge_vec_0_distance = torch.sqrt(torch.sum(edge_vec_0**2, dim=1))
7
 
8
- # Make sure the atoms are far enough apart
9
- #assert torch.min(edge_vec_0_distance) < 0.0001
10
- if torch.min(edge_vec_0_distance) < 0.0001:
11
- print(
12
- "Error edge_vec_0_distance: {}".format(
13
- torch.min(edge_vec_0_distance)
14
- )
 
 
15
  )
16
-
17
  norm_x = edge_vec_0 / (edge_vec_0_distance.view(-1, 1))
18
 
19
  edge_vec_2 = torch.rand_like(edge_vec_0) - 0.5
 
5
  edge_vec_0 = edge_distance_vec
6
  edge_vec_0_distance = torch.sqrt(torch.sum(edge_vec_0**2, dim=1))
7
 
8
+ # Two atoms at (nearly) the same position cannot define a local frame: the normalization below
9
+ # would divide by ~zero and silently produce NaN or a garbage frame that propagates through the
10
+ # whole forward pass. Refuse the input instead. This is reachable from raw geometries, e.g.
11
+ # overlapping atoms in an unrelaxed explicit-solvent snapshot.
12
+ min_distance = torch.min(edge_vec_0_distance)
13
+ if min_distance < 0.0001:
14
+ raise ValueError(
15
+ f"overlapping atoms: smallest interatomic distance {float(min_distance):.2e} is too "
16
+ "small to define a local frame; check the input geometry for coincident atoms"
17
  )
18
+
19
  norm_x = edge_vec_0 / (edge_vec_0_distance.view(-1, 1))
20
 
21
  edge_vec_2 = torch.rand_like(edge_vec_0) - 0.5
magnet/eqV2/equiformer_v2_NMR.py CHANGED
@@ -4,7 +4,6 @@ import math
4
  import numpy as np
5
  import torch
6
  import torch.nn as nn
7
- from pyexpat.model import XML_CQUANT_OPT
8
 
9
  from .ocpmodels.common.registry import registry
10
  from .ocpmodels.common.utils import conditional_grad
@@ -422,8 +421,7 @@ class EquiformerV2_NMR(BaseModel):
422
  edge_index = edge_index[:, ~remove_solvent_edge]
423
  edge_distance = edge_distance[~remove_solvent_edge]
424
  edge_distance_vec = edge_distance_vec[~remove_solvent_edge]
425
- neighbors = torch.ones_like(neighbors) * edge_index.shape[1]
426
-
427
  #print(edge_index.shape)
428
 
429
 
 
4
  import numpy as np
5
  import torch
6
  import torch.nn as nn
 
7
 
8
  from .ocpmodels.common.registry import registry
9
  from .ocpmodels.common.utils import conditional_grad
 
421
  edge_index = edge_index[:, ~remove_solvent_edge]
422
  edge_distance = edge_distance[~remove_solvent_edge]
423
  edge_distance_vec = edge_distance_vec[~remove_solvent_edge]
424
+
 
425
  #print(edge_index.shape)
426
 
427
 
magnet/test_api.py CHANGED
@@ -14,8 +14,11 @@ import pytest
14
 
15
  warnings.filterwarnings("ignore")
16
 
 
 
17
  import magnet.api as api
18
  from magnet import scaling
 
19
 
20
  _HERE = os.path.dirname(os.path.abspath(__file__))
21
  _REPO = os.path.dirname(_HERE) # magnet/ -> repo root
@@ -461,3 +464,22 @@ def test_real_atoms_beyond_cutoff_crash():
461
  # two atoms 50 A apart: no edges within cutoff -> same zero-edge crash as one atom.
462
  api.predict_shieldings(np.array([6, 1]), np.array([[0., 0., 0.], [0., 0., 50.]]),
463
  n_passes=1, symmetrize=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
  warnings.filterwarnings("ignore")
16
 
17
+ import torch
18
+
19
  import magnet.api as api
20
  from magnet import scaling
21
+ from magnet.eqV2.edge_rot_mat import init_edge_rot_mat
22
 
23
  _HERE = os.path.dirname(os.path.abspath(__file__))
24
  _REPO = os.path.dirname(_HERE) # magnet/ -> repo root
 
464
  # two atoms 50 A apart: no edges within cutoff -> same zero-edge crash as one atom.
465
  api.predict_shieldings(np.array([6, 1]), np.array([[0., 0., 0.], [0., 0., 50.]]),
466
  n_passes=1, symmetrize=False)
467
+
468
+
469
+ # ---- degenerate-geometry guard in the local-frame construction (no weights needed) ----
470
+ def test_edge_rot_mat_rejects_coincident_atoms():
471
+ # an edge between two atoms at the same position has ~zero length; the old code printed a
472
+ # warning and then divided by ~zero, silently producing NaN frames that poison the whole
473
+ # forward pass. It must raise instead.
474
+ edge_vecs = torch.tensor([[1.0, 0.0, 0.0], [0.0, 0.0, 0.0]])
475
+ with pytest.raises(ValueError, match="overlapping atoms"):
476
+ init_edge_rot_mat(edge_vecs)
477
+
478
+
479
+ def test_edge_rot_mat_accepts_separated_atoms():
480
+ edge_vecs = torch.tensor([[1.0, 0.0, 0.0], [0.0, 1.5, 0.0], [0.0, 0.0, 2.0]])
481
+ rot = init_edge_rot_mat(edge_vecs)
482
+ assert rot.shape == (3, 3, 3)
483
+ # each frame is a proper rotation: R @ R^T == I
484
+ identity = torch.eye(3).expand(3, 3, 3)
485
+ assert torch.allclose(torch.bmm(rot, rot.transpose(1, 2)), identity, atol=1e-4)