| """Turn MagNET-Zero / MagNET-PCM shieldings into the chemical shifts a chemist measures, using the |
| paper's published per-solvent linear scaling (SI Table S10 for proton, S11 for carbon). |
| |
| This is a tiny, self-contained copy (numpy only), so `from magnet import published_scaling_tables, |
| predict_shift` works with no data download and no extra path setup. The full derivation of these |
| tables from the delta-22 data lives in analysis/code/scaling_factors.py, and a test there checks the |
| two copies agree so they cannot drift. |
| |
| The prediction equation, for one nucleus and one solvent, is |
| |
| shift = intercept + stationary * sigma_zero + pcm * delta_pcm |
| |
| where sigma_zero is the MagNET-Zero gas-phase shielding (compute_MagNET_Zero_shieldings) and delta_pcm |
| is the MagNET-PCM chloroform correction (compute_MagNET_PCM_corrections). You always pass the |
| chloroform correction; the per-solvent scaling is already folded into the `pcm` coefficient. |
| """ |
| import csv |
| import io |
|
|
| import numpy as np |
|
|
| |
| |
| |
| _PUBLISHED_TABLE_CSV = { |
| "H": """solvent,intercept,stationary,pcm |
| tetrahydrofuran,31.319664,-0.980082,-0.781373 |
| dichloromethane,31.374485,-0.979760,-0.806215 |
| chloroform,31.291983,-0.975682,-0.851715 |
| toluene,31.714067,-0.996613,1.948607 |
| benzene,31.984616,-1.005170,2.239317 |
| chlorobenzene,31.677950,-0.993715,0.830908 |
| acetone,31.510823,-0.987188,-1.229850 |
| dimethylsulfoxide,31.597261,-0.991031,-1.348009 |
| acetonitrile,31.494061,-0.985628,-0.969617 |
| trifluoroethanol,30.873172,-0.959885,-0.955808 |
| methanol,31.253627,-0.976349,-1.246680 |
| TIP4P,31.356622,-0.978710,-1.475907 |
| """, |
| "C": """solvent,intercept,stationary,pcm |
| tetrahydrofuran,171.052274,-0.918997,-1.069506 |
| dichloromethane,171.507154,-0.921165,-1.115013 |
| chloroform,171.726919,-0.924228,-0.936801 |
| toluene,171.689276,-0.924983,-0.618407 |
| benzene,171.965715,-0.927029,-0.593714 |
| chlorobenzene,171.073820,-0.921235,-0.997638 |
| acetone,171.305850,-0.919608,-1.239500 |
| dimethylsulfoxide,170.596065,-0.918672,-1.296497 |
| acetonitrile,171.877492,-0.922626,-1.287661 |
| trifluoroethanol,174.235231,-0.939011,-1.290071 |
| methanol,172.423799,-0.927563,-1.288843 |
| TIP4P,173.692750,-0.937848,-1.342659 |
| """, |
| } |
|
|
|
|
| def published_scaling_tables(): |
| """The published scaling tables as {"H": table, "C": table}. Each table maps a solvent name to a |
| {"intercept", "stationary", "pcm"} dict of coefficients. Pass one table and a solvent name to |
| predict_shift. Water is named "TIP4P".""" |
| tables = {} |
| for nucleus, text in _PUBLISHED_TABLE_CSV.items(): |
| table = {} |
| for row in csv.DictReader(io.StringIO(text.strip())): |
| solvent = row.pop("solvent") |
| table[solvent] = {name: float(value) for name, value in row.items()} |
| tables[nucleus] = table |
| return tables |
|
|
|
|
| def predict_shift(table, solvent, magnet_zero_shielding, magnet_pcm_chloroform_correction): |
| """Chemical shift(s) from MagNET-Zero / MagNET-PCM outputs, for one nucleus and solvent. |
| |
| table: one nucleus's table from published_scaling_tables(). solvent: a solvent name (water is |
| "TIP4P"). magnet_zero_shielding and magnet_pcm_chloroform_correction are scalars or arrays. |
| """ |
| row = table[solvent] |
| return (row["intercept"] |
| + row["stationary"] * np.asarray(magnet_zero_shielding, dtype=float) |
| + row["pcm"] * np.asarray(magnet_pcm_chloroform_correction, dtype=float)) |
|
|