MagNET / data /delta50 /test_delta50.py
ekwan16's picture
sync code from github@6f6c7d83845ee0307e16b2b9bb1b389c5bfc5007
23c6f41 verified
Raw
History Blame Contribute Delete
5.58 kB
"""Tests for data/delta50/decode_delta50.py.
A tiny synthetic file in the stored int32 format exercises the reader, the per-molecule slicing,
the fixed-point decode, and the PCM-correction helper, without the real file. An opt-in smoke test
runs against the real file when present.
"""
import os
import sys
import numpy as np
import pytest
import h5py
HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, HERE)
import decode_delta50 as D # noqa: E402
SCALE = 1e-4
MARKER = -2147483648
REAL = os.path.join(HERE, "delta50.hdf5")
def _encode(values):
v = np.asarray(values, dtype=np.float64)
finite = np.isfinite(v)
out = np.full(v.shape, MARKER, dtype=np.int64)
out[finite] = np.round(v[finite] / SCALE).astype(np.int64)
return out.astype(np.int32)
def _build_synthetic(path):
"""Two molecules (3 and 5 atoms) in the stored int32 format, plus the decoded truth to check against."""
rng = np.random.default_rng(0)
names = ["alpha", "beta"]
specs = [3, 5] # n_atoms
n_atoms = np.array(specs, dtype=np.int32)
an, coords, zero, b3, pcm, dwp, dwb, exp, truth = [], [], [], [], [], [], [], [], []
for na in specs:
a = rng.integers(1, 9, size=na).astype(np.uint8)
c = rng.normal(0, 2, size=(na, 3))
z = rng.normal(100, 30, size=na)
g = rng.normal(100, 30, size=na)
p = g + rng.normal(0, 0.2, size=na)
dw = z + rng.normal(0, 0.1, size=na)
db = z + rng.normal(0, 0.1, size=na)
e = rng.normal(5, 2, size=na)
e[a > 6] = np.nan # heteroatoms have no experimental shift
an.append(a); coords.append(_encode(c)); zero.append(_encode(z))
b3.append(_encode(g)); pcm.append(_encode(p))
dwp.append(_encode(dw)); dwb.append(_encode(db)); exp.append(_encode(e))
truth.append((a, c, z, g, p, dw, db, e))
opts = dict(compression="gzip", shuffle=True)
with h5py.File(path, "w") as f:
f.attrs["n_molecules"] = 2
f.attrs["scale"] = SCALE
f.attrs["missing_value_marker"] = MARKER
f.create_dataset("molecule_names", data=np.array(names, dtype=object),
dtype=h5py.string_dtype("utf-8"))
f.create_dataset("n_atoms", data=n_atoms, **opts)
f.create_dataset("atomic_numbers", data=np.concatenate(an), **opts)
f.create_dataset("coordinates", data=np.concatenate(coords), **opts)
f.create_dataset("nn_magnet_zero", data=np.concatenate(zero), **opts)
f.create_dataset("nn_b3lyp", data=np.concatenate(b3), **opts)
f.create_dataset("nn_b3lyp_pcm", data=np.concatenate(pcm), **opts)
f.create_dataset("shielding_wp04_pcSseg2", data=np.concatenate(dwp), **opts)
f.create_dataset("shielding_wb97xd_pcSseg2", data=np.concatenate(dwb), **opts)
f.create_dataset("experimental_shift", data=np.concatenate(exp), **opts)
return names, specs, truth
def test_reader_and_decode(tmp_path):
path = str(tmp_path / "tiny.hdf5")
names, specs, truth = _build_synthetic(path)
with D.Delta50(path) as ds:
assert len(ds) == 2
assert ds.molecule_names == names
for i, na in enumerate(specs):
m = ds.molecule(i)
a, c, z, g, p, dw, db, e = truth[i]
assert m["name"] == names[i]
assert np.array_equal(m["atomic_numbers"], a)
assert m["coordinates"].shape == (na, 3)
assert np.abs(m["coordinates"] - c).max() <= 5e-5
assert np.abs(m["nn_magnet_zero"] - z).max() <= 5e-5
assert np.abs(m["shielding_wp04_pcSseg2"] - dw).max() <= 5e-5
assert np.abs(m["shielding_wb97xd_pcSseg2"] - db).max() <= 5e-5
finite = np.isfinite(e)
assert np.abs(m["experimental_shift"][finite] - e[finite]).max() <= 5e-5
assert np.isnan(m["experimental_shift"][~finite]).all()
assert np.allclose(ds.pcm_correction(i), p - g, atol=1e-4)
def test_lookup_and_errors(tmp_path):
path = str(tmp_path / "tiny.hdf5")
names, _, _ = _build_synthetic(path)
with D.Delta50(path) as ds:
assert ds.molecule_by_name("beta")["name"] == "beta"
with pytest.raises(KeyError):
ds.index_of("missing")
with pytest.raises(IndexError):
ds.molecule(9)
def test_decode_marker_to_nan():
out = D._decode(np.array([0, 10000, MARKER], dtype=np.int32))
assert out[1] == pytest.approx(1.0) and np.isnan(out[2])
@pytest.mark.skipif(not os.path.exists(REAL), reason="real delta50.hdf5 not present")
def test_real_file_smoke():
with D.Delta50(REAL) as ds:
assert len(ds) == 50
assert "nitromethane" in ds.molecule_names
# the DELTA50 nitrate-ester misnomer is corrected to the C-nitro name here
assert "2-methyl-2-nitropropane" in ds.molecule_names
assert "t-butyl nitrate" not in ds.molecule_names
m = ds.molecule_by_name("nitromethane")
assert np.isfinite(m["nn_magnet_zero"]).all()
for key in ("shielding_wp04_pcSseg2", "shielding_wb97xd_pcSseg2", "experimental_shift"):
assert m[key].shape == m["atomic_numbers"].shape
# nitromethane: WP04 at H, wB97X-D at C, and an experimental shift on every H and C
z = m["atomic_numbers"]
assert np.isfinite(m["experimental_shift"][(z == 1) | (z == 6)]).all()
assert ds.pcm_correction(ds.index_of("nitromethane")).shape == m["atomic_numbers"].shape
atoms = ds.all_atoms()
assert atoms["atomic_numbers"].shape == atoms["experimental_shift"].shape