| """Tests for data/gdb_qcd/decode_gdb_qcd.py. |
| |
| A tiny synthetic file in the stored int32 format exercises the reader, the per-molecule block |
| reconstruction (variable atoms and trajectories, fixed 33 frames), and the fixed-point decode, |
| all without the multi-hundred-MB real file. An opt-in smoke test runs against the real file when |
| it is 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_gdb_qcd as D |
|
|
| SCALE = 1e-4 |
| MARKER = -2147483648 |
| N_FRAMES = 33 |
| REAL = os.path.join(HERE, "gdb_qcd.hdf5") |
|
|
|
|
| def _encode(values): |
| out = np.round(np.asarray(values, dtype=np.float64) / SCALE).astype(np.int64) |
| out[~np.isfinite(values)] = MARKER |
| return out.astype(np.int32) |
|
|
|
|
| def _build_synthetic(path): |
| """Two molecules: 3 atoms / 2 trajectories, and 4 atoms / 1 trajectory.""" |
| rng = np.random.default_rng(0) |
| specs = [(3, 2), (4, 1)] |
| mol_ids = np.array([11, 22], dtype=np.int64) |
| n_atoms = np.array([s[0] for s in specs], dtype=np.int32) |
| n_traj = np.array([s[1] for s in specs], dtype=np.int32) |
| atomic, coord_blocks, shield_blocks, truth = [], [], [], [] |
| for na, nt in specs: |
| an = rng.integers(1, 9, size=na).astype(np.uint8) |
| coords = rng.normal(0, 2, size=(nt, N_FRAMES, na, 3)) |
| shields = rng.normal(100, 30, size=(nt, N_FRAMES, na)) |
| atomic.append(an) |
| coord_blocks.append(_encode(coords.reshape(-1, 3))) |
| shield_blocks.append(_encode(shields.reshape(-1))) |
| truth.append((an, coords, shields)) |
| opts = dict(compression="gzip", shuffle=True) |
| with h5py.File(path, "w") as f: |
| f.attrs["n_molecules"] = 2 |
| f.attrs["n_trajectories"] = int(n_traj.sum()) |
| f.attrs["n_frames"] = N_FRAMES |
| f.attrs["nmr_level_of_theory"] = "PBE0/pcSseg-1" |
| f.attrs["scale"] = SCALE |
| f.attrs["missing_value_marker"] = MARKER |
| f.create_dataset("molecule_ids", data=mol_ids, **opts) |
| f.create_dataset("n_atoms", data=n_atoms, **opts) |
| f.create_dataset("n_trajectories", data=n_traj, **opts) |
| f.create_dataset("atomic_numbers", data=np.concatenate(atomic), **opts) |
| f.create_dataset("coordinates", data=np.concatenate(coord_blocks), **opts) |
| f.create_dataset("shieldings", data=np.concatenate(shield_blocks), **opts) |
| return mol_ids, specs, truth |
|
|
|
|
| def test_reader_reconstructs_blocks(tmp_path): |
| path = str(tmp_path / "tiny.hdf5") |
| mol_ids, specs, truth = _build_synthetic(path) |
| with D.GDBQCD(path) as ds: |
| assert len(ds) == 2 |
| assert ds.n_frames == 33 |
| assert list(ds.molecule_ids) == list(mol_ids) |
| for i, (na, nt) in enumerate(specs): |
| m = ds.molecule(i) |
| an, coords, shields = truth[i] |
| assert m["id"] == int(mol_ids[i]) |
| assert ds.n_trajectories(i) == nt |
| assert np.array_equal(m["atomic_numbers"], an) |
| assert m["coordinates"].shape == (nt, 33, na, 3) |
| assert m["shieldings"].shape == (nt, 33, na) |
| |
| assert np.abs(m["coordinates"] - coords).max() <= 5e-5 |
| assert np.abs(m["shieldings"] - shields).max() <= 5e-5 |
|
|
|
|
| def test_lookup_by_id_and_errors(tmp_path): |
| path = str(tmp_path / "tiny.hdf5") |
| mol_ids, _, _ = _build_synthetic(path) |
| with D.GDBQCD(path) as ds: |
| assert ds.molecule_by_id(22)["id"] == 22 |
| assert ds.index_of(11) == 0 |
| with pytest.raises(KeyError): |
| ds.index_of(999) |
| with pytest.raises(IndexError): |
| ds.molecule(5) |
|
|
|
|
| def test_decode_marker_to_nan(): |
| vals = np.array([0, 10000, MARKER, -25000], dtype=np.int32) |
| out = D._decode(vals) |
| assert out[0] == 0.0 and out[1] == pytest.approx(1.0) and out[3] == pytest.approx(-2.5) |
| assert np.isnan(out[2]) |
|
|
|
|
| @pytest.mark.skipif(not os.path.exists(REAL), reason="real gdb_qcd.hdf5 not present") |
| def test_real_file_smoke(): |
| with D.GDBQCD(REAL) as ds: |
| assert len(ds) == 2461 |
| assert int(ds.f.attrs["n_trajectories"]) == 56702 |
| m = ds.molecule(0) |
| assert m["coordinates"].shape[1] == 33 |
| assert m["coordinates"].shape[0] == ds.n_trajectories(0) |
| assert np.isfinite(m["shieldings"]).all() |
|
|