From cfe56320a485f1a62443db78b28d61f9ababbb48 Mon Sep 17 00:00:00 2001 From: "Matthew W. Thompson" Date: Thu, 18 Jan 2024 15:52:35 -0600 Subject: [PATCH 1/4] Add `MoleculeStore.get_force_fields` --- ibstore/_store.py | 12 ++++++++++++ ibstore/_tests/unit_tests/test_store.py | 15 +++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/ibstore/_store.py b/ibstore/_store.py index add4316..c3e6966 100644 --- a/ibstore/_store.py +++ b/ibstore/_store.py @@ -248,6 +248,18 @@ def get_qm_conformers_by_molecule_id(self, id: int) -> list: .all() ] + def get_force_fields( + self, + ) -> list[str]: + """Return a list of all force fields with some conformers stored.""" + with self._get_session() as db: + return [ + force_field + for (force_field,) in db.db.query( + DBMMConformerRecord.force_field, + ).distinct() + ] + def get_mm_conformers_by_molecule_id( self, id: int, diff --git a/ibstore/_tests/unit_tests/test_store.py b/ibstore/_tests/unit_tests/test_store.py index 1f1895a..ef41f74 100644 --- a/ibstore/_tests/unit_tests/test_store.py +++ b/ibstore/_tests/unit_tests/test_store.py @@ -104,3 +104,18 @@ def test_get_conformers(small_store): force_field=force_field, )[-1], ) + + +def test_get_force_fields(): + force_fields = MoleculeStore( + get_data_file_path( + "_tests/data/ch.sqlite", + package_name="ibstore", + ), + ).get_force_fields() + + assert len(force_fields) == 9 + + assert "openff-2.1.0" in force_fields + assert "gaff-2.11" in force_fields + assert "openff-3.0.0" not in force_fields From 89bdfd729073c0c2b45dd1cde2fb4c0589508d2b Mon Sep 17 00:00:00 2001 From: "Matthew W. Thompson" Date: Thu, 18 Jan 2024 16:05:15 -0600 Subject: [PATCH 2/4] Add `MoleculeStore.get_mm_conformer_records_by_molecule_id` --- ibstore/_store.py | 23 ++++++++++++++++++++ ibstore/_tests/unit_tests/test_store.py | 28 ++++++++++++++++++++++++- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/ibstore/_store.py b/ibstore/_store.py index c3e6966..8fcf7be 100644 --- a/ibstore/_store.py +++ b/ibstore/_store.py @@ -321,6 +321,29 @@ def get_mm_energies_by_molecule_id( .all() ] + def get_mm_conformer_records_by_molecule_id( + store, + molecule_id: int, + force_field: str, + ): + with store._get_session() as db: + contents = [ + MMConformerRecord( + molecule_id=molecule_id, + qcarchive_id=x.qcarchive_id, + force_field=x.force_field, + mapped_smiles=x.mapped_smiles, + coordinates=x.coordinates, + energy=x.energy, + ) + for x in db.db.query(DBMMConformerRecord) + .filter_by(parent_id=molecule_id) + .filter_by(force_field=force_field) + .order_by(DBMMConformerRecord.qcarchive_id) + .all() + ] + return contents + @classmethod def from_qcsubmit_collection( cls, diff --git a/ibstore/_tests/unit_tests/test_store.py b/ibstore/_tests/unit_tests/test_store.py index ef41f74..afa2575 100644 --- a/ibstore/_tests/unit_tests/test_store.py +++ b/ibstore/_tests/unit_tests/test_store.py @@ -5,12 +5,19 @@ import numpy import pytest from openff.qcsubmit.results import OptimizationResultCollection +from openff.toolkit import Molecule from openff.utilities import get_data_file_path, temporary_cd from ibstore._store import MoleculeStore from ibstore.exceptions import DatabaseExistsError +@pytest.fixture() +def diphenylvinylbenzene(): + """Return 1,2-diphenylvinylbenzene""" + return Molecule.from_smiles("c1ccc(cc1)C=C(c2ccccc2)c3ccccc3") + + def test_from_qcsubmit(small_collection): db = "foo.sqlite" with temporary_cd(): @@ -32,7 +39,7 @@ def test_do_not_overwrite(small_collection): ) -def test_load_existing_databse(): +def test_load_existing_database(): # This file manually generated from data/01-processed-qm-ch.json store = MoleculeStore( get_data_file_path( @@ -119,3 +126,22 @@ def test_get_force_fields(): assert "openff-2.1.0" in force_fields assert "gaff-2.11" in force_fields assert "openff-3.0.0" not in force_fields + + +def test_get_mm_conformer_records_by_molecule_id(diphenylvinylbenzene): + records = MoleculeStore( + get_data_file_path( + "_tests/data/ch.sqlite", + package_name="ibstore", + ), + ).get_mm_conformer_records_by_molecule_id(1, force_field="openff-2.1.0") + + for record in records: + assert record.molecule_id == 1 + assert record.force_field == "openff-2.1.0" + assert record.coordinates.shape == (36, 3) + assert record.energy is not None + + assert Molecule.from_mapped_smiles(record.mapped_smiles).is_isomorphic_with( + diphenylvinylbenzene, + ) From 667cfd66158822c0b0fc180e51341d89e672ea9b Mon Sep 17 00:00:00 2001 From: "Matthew W. Thompson" Date: Thu, 18 Jan 2024 16:11:52 -0600 Subject: [PATCH 3/4] Add `MoleculeStore.get_qm_conformer_records_by_molecule_id` --- ibstore/_store.py | 27 ++++++++++-- ibstore/_tests/unit_tests/test_store.py | 57 ++++++++++++++----------- 2 files changed, 57 insertions(+), 27 deletions(-) diff --git a/ibstore/_store.py b/ibstore/_store.py index 8fcf7be..537995b 100644 --- a/ibstore/_store.py +++ b/ibstore/_store.py @@ -321,12 +321,33 @@ def get_mm_energies_by_molecule_id( .all() ] + def get_qm_conformer_records_by_molecule_id( + self, + molecule_id: int, + ) -> list[QMConformerRecord]: + with self._get_session() as db: + contents = [ + QMConformerRecord( + molecule_id=molecule_id, + qcarchive_id=x.qcarchive_id, + mapped_smiles=x.mapped_smiles, + coordinates=x.coordinates, + energy=x.energy, + ) + for x in db.db.query(DBQMConformerRecord) + .filter_by(parent_id=molecule_id) + .order_by(DBQMConformerRecord.qcarchive_id) + .all() + ] + + return contents + def get_mm_conformer_records_by_molecule_id( - store, + self, molecule_id: int, force_field: str, - ): - with store._get_session() as db: + ) -> list[MMConformerRecord]: + with self._get_session() as db: contents = [ MMConformerRecord( molecule_id=molecule_id, diff --git a/ibstore/_tests/unit_tests/test_store.py b/ibstore/_tests/unit_tests/test_store.py index afa2575..7ca913b 100644 --- a/ibstore/_tests/unit_tests/test_store.py +++ b/ibstore/_tests/unit_tests/test_store.py @@ -12,6 +12,17 @@ from ibstore.exceptions import DatabaseExistsError +@pytest.fixture() +def basic_ch_store(): + # This file manually generated from data/01-processed-qm-ch.json + return MoleculeStore( + get_data_file_path( + "_tests/data/ch.sqlite", + package_name="ibstore", + ), + ) + + @pytest.fixture() def diphenylvinylbenzene(): """Return 1,2-diphenylvinylbenzene""" @@ -39,16 +50,8 @@ def test_do_not_overwrite(small_collection): ) -def test_load_existing_database(): - # This file manually generated from data/01-processed-qm-ch.json - store = MoleculeStore( - get_data_file_path( - "_tests/data/01-processed-qm-ch.sqlite", - package_name="ibstore", - ), - ) - - assert len(store) == 40 +def test_load_existing_database(basic_ch_store): + assert len(basic_ch_store) == 40 def test_get_molecule_ids(small_store): @@ -113,13 +116,8 @@ def test_get_conformers(small_store): ) -def test_get_force_fields(): - force_fields = MoleculeStore( - get_data_file_path( - "_tests/data/ch.sqlite", - package_name="ibstore", - ), - ).get_force_fields() +def test_get_force_fields(basic_ch_store): + force_fields = basic_ch_store.get_force_fields() assert len(force_fields) == 9 @@ -128,13 +126,11 @@ def test_get_force_fields(): assert "openff-3.0.0" not in force_fields -def test_get_mm_conformer_records_by_molecule_id(diphenylvinylbenzene): - records = MoleculeStore( - get_data_file_path( - "_tests/data/ch.sqlite", - package_name="ibstore", - ), - ).get_mm_conformer_records_by_molecule_id(1, force_field="openff-2.1.0") +def test_get_mm_conformer_records_by_molecule_id(basic_ch_store, diphenylvinylbenzene): + records = basic_ch_store.get_mm_conformer_records_by_molecule_id( + 1, + force_field="openff-2.1.0", + ) for record in records: assert record.molecule_id == 1 @@ -145,3 +141,16 @@ def test_get_mm_conformer_records_by_molecule_id(diphenylvinylbenzene): assert Molecule.from_mapped_smiles(record.mapped_smiles).is_isomorphic_with( diphenylvinylbenzene, ) + + +def test_get_qm_conformer_records_by_molecule_id(basic_ch_store, diphenylvinylbenzene): + records = basic_ch_store.get_qm_conformer_records_by_molecule_id(1) + + for record in records: + assert record.molecule_id == 1 + assert record.coordinates.shape == (36, 3) + assert record.energy is not None + + assert Molecule.from_mapped_smiles(record.mapped_smiles).is_isomorphic_with( + diphenylvinylbenzene, + ) From 060e312dc28f5c1d22373f03e73edf3eb088c638 Mon Sep 17 00:00:00 2001 From: "Matthew W. Thompson" Date: Thu, 18 Jan 2024 16:30:15 -0600 Subject: [PATCH 4/4] Add instance check --- .github/workflows/ci.yaml | 2 +- ibstore/_tests/unit_tests/test_store.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index a214a2d..c1c3ad9 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -33,7 +33,7 @@ jobs: - name: Run tests run: | - pytest -v --cov=ibstore/ --cov-report=xml ibstore/ + pytest -v -nauto --cov=ibstore/ --cov-report=xml ibstore/ - name: CodeCov uses: codecov/codecov-action@v3 diff --git a/ibstore/_tests/unit_tests/test_store.py b/ibstore/_tests/unit_tests/test_store.py index 7ca913b..921be48 100644 --- a/ibstore/_tests/unit_tests/test_store.py +++ b/ibstore/_tests/unit_tests/test_store.py @@ -10,6 +10,7 @@ from ibstore._store import MoleculeStore from ibstore.exceptions import DatabaseExistsError +from ibstore.models import MMConformerRecord, QMConformerRecord @pytest.fixture() @@ -133,6 +134,7 @@ def test_get_mm_conformer_records_by_molecule_id(basic_ch_store, diphenylvinylbe ) for record in records: + assert isinstance(record, MMConformerRecord) assert record.molecule_id == 1 assert record.force_field == "openff-2.1.0" assert record.coordinates.shape == (36, 3) @@ -147,6 +149,7 @@ def test_get_qm_conformer_records_by_molecule_id(basic_ch_store, diphenylvinylbe records = basic_ch_store.get_qm_conformer_records_by_molecule_id(1) for record in records: + assert isinstance(record, QMConformerRecord) assert record.molecule_id == 1 assert record.coordinates.shape == (36, 3) assert record.energy is not None