From 9c9f44942e6d6164e4a014491134937b36299d1e Mon Sep 17 00:00:00 2001 From: Matt Thompson Date: Thu, 13 Jul 2023 15:32:19 -0500 Subject: [PATCH] Re-run `black` (#219) --- .git-blame-ignore-revs | 2 + .pre-commit-config.yaml | 23 + devtools/conda-envs/basic.yaml | 2 +- openff/qcsubmit/common_structures.py | 4 +- openff/qcsubmit/tests/results/__init__.py | 10 +- openff/qcsubmit/tests/results/conftest.py | 16 +- openff/qcsubmit/tests/results/test_caching.py | 15 +- openff/qcsubmit/tests/results/test_filters.py | 41 +- openff/qcsubmit/tests/results/test_results.py | 18 +- .../qcsubmit/tests/specifications/test_pcm.py | 81 +- .../qcsubmit/tests/test_common_structures.py | 36 +- openff/qcsubmit/tests/test_datasets.py | 1643 ++++++++++++----- openff/qcsubmit/tests/test_factories.py | 261 ++- openff/qcsubmit/tests/test_serializers.py | 92 +- openff/qcsubmit/tests/test_submissions.py | 659 +++++-- .../tests/test_workflow_components.py | 405 +++- openff/qcsubmit/tests/utils/test_visualize.py | 2 - 17 files changed, 2313 insertions(+), 997 deletions(-) create mode 100644 .git-blame-ignore-revs create mode 100644 .pre-commit-config.yaml diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 00000000..c40cf31e --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,2 @@ +# Black 23 +94875c5bb13a5734b720f99b181e0d6b8639ba39 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..c55f50cf --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,23 @@ +ci: + autoupdate_schedule: "quarterly" +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: check-yaml + - id: debug-statements +- repo: https://github.com/psf/black + rev: 23.7.0 + hooks: + - id: black + files: ^openff +- repo: https://github.com/PyCQA/isort + rev: 5.12.0 + hooks: + - id: isort + files: ^openff +- repo: https://github.com/PyCQA/flake8 + rev: 6.0.0 + hooks: + - id: flake8 + files: ^openff diff --git a/devtools/conda-envs/basic.yaml b/devtools/conda-envs/basic.yaml index 42a4b7ee..adf45953 100644 --- a/devtools/conda-envs/basic.yaml +++ b/devtools/conda-envs/basic.yaml @@ -28,7 +28,7 @@ dependencies: # openmmforcefields brings in the full toolkit; if that is ever re-packaged # this should be changed to openff-toolkit-base - openff-toolkit >= 0.12 - - pydantic + - pydantic =1 - pyyaml - qcportal>=0.15.7 - torsiondrive diff --git a/openff/qcsubmit/common_structures.py b/openff/qcsubmit/common_structures.py index 4a194a92..75ea1116 100644 --- a/openff/qcsubmit/common_structures.py +++ b/openff/qcsubmit/common_structures.py @@ -6,7 +6,7 @@ import re from datetime import date, datetime from enum import Enum -from typing import Any, ClassVar, Dict, List, Optional, Set, Tuple, Union +from typing import Any, ClassVar, Dict, List, Optional, Set, Tuple, Union, TYPE_CHECKING import numpy as np import qcportal as ptl @@ -36,6 +36,8 @@ from openff.qcsubmit.utils.smirnoff import split_openff_molecule +if TYPE_CHECKING: + from pydantic import AbstractSetIntStr class DatasetConfig(BaseModel): """ The basic configurations for all datasets. diff --git a/openff/qcsubmit/tests/results/__init__.py b/openff/qcsubmit/tests/results/__init__.py index 7a1c2464..0dddeb63 100644 --- a/openff/qcsubmit/tests/results/__init__.py +++ b/openff/qcsubmit/tests/results/__init__.py @@ -28,16 +28,13 @@ class _FractalClient(BaseModel): - address: str def _mock_molecule(entry: _BaseResult, n_conformers: int = 1) -> Molecule: - molecule: Molecule = Molecule.from_smiles(entry.cmiles) for _ in range(n_conformers): - molecule.add_conformer( numpy.arange(molecule.n_atoms * 3).reshape((molecule.n_atoms, 3)) * unit.angstrom @@ -47,7 +44,6 @@ def _mock_molecule(entry: _BaseResult, n_conformers: int = 1) -> Molecule: def mock_basic_result_collection(molecules, monkeypatch) -> BasicResultCollection: - collection = BasicResultCollection( entries={ address: [ @@ -77,7 +73,7 @@ def mock_basic_result_collection(molecules, monkeypatch) -> BasicResultCollectio status=RecordStatusEnum.complete, client=_FractalClient(address=address), ), - molecules[address][int(entry.record_id) - 1] + molecules[address][int(entry.record_id) - 1], ) for address, entries in self.entries.items() for entry in entries @@ -90,7 +86,6 @@ def mock_basic_result_collection(molecules, monkeypatch) -> BasicResultCollectio def mock_optimization_result_collection( molecules, monkeypatch ) -> OptimizationResultCollection: - collection = OptimizationResultCollection( entries={ address: [ @@ -138,7 +133,6 @@ def mock_optimization_result_collection( def mock_torsion_drive_result_collection( molecules, monkeypatch ) -> TorsionDriveResultCollection: - collection = TorsionDriveResultCollection( entries={ address: [ @@ -190,11 +184,9 @@ def mock_torsion_drive_result_collection( ) def get_molecules(self): - return_value = [] for molecule_id in self.initial_molecule: - molecule = copy.deepcopy(molecules[self.client.address][int(self.id) - 1]) molecule._conformers = [molecule.conformers[int(molecule_id) - 1]] diff --git a/openff/qcsubmit/tests/results/conftest.py b/openff/qcsubmit/tests/results/conftest.py index ba0b2eab..098cd9ca 100644 --- a/openff/qcsubmit/tests/results/conftest.py +++ b/openff/qcsubmit/tests/results/conftest.py @@ -16,7 +16,6 @@ def _smiles_to_molecule(smiles: str) -> Molecule: - molecule: Molecule = Molecule.from_smiles(smiles, allow_undefined_stereo=True) molecule.generate_conformers(n_conformers=1) @@ -45,7 +44,8 @@ def tautomer_basic_result_collection(monkeypatch) -> BasicResultCollection: smiles = { "http://localhost:442": [ - _smiles_to_molecule(smiles) for smiles in ["Oc1nnccn1", "C1=NC(=O)NN=C1", "C1=CN=NC(=O)N1"] + _smiles_to_molecule(smiles) + for smiles in ["Oc1nnccn1", "C1=NC(=O)NN=C1", "C1=CN=NC(=O)N1"] ] } return mock_basic_result_collection(smiles, monkeypatch) @@ -116,16 +116,14 @@ def optimization_result_collection(monkeypatch) -> OptimizationResultCollection: @pytest.fixture() -def optimization_result_collection_duplicates(monkeypatch) -> OptimizationResultCollection: +def optimization_result_collection_duplicates( + monkeypatch, +) -> OptimizationResultCollection: """Create a collection with duplicate enetries accross different addresses which can be reduced to a single entry.""" smiles = { - "http://localhost:442": [ - _smiles_to_molecule(smiles="CCCO") - ], - "http://localhost:443": [ - _smiles_to_molecule(smiles="CCCO") - ] + "http://localhost:442": [_smiles_to_molecule(smiles="CCCO")], + "http://localhost:443": [_smiles_to_molecule(smiles="CCCO")], } return mock_optimization_result_collection(smiles, monkeypatch) diff --git a/openff/qcsubmit/tests/results/test_caching.py b/openff/qcsubmit/tests/results/test_caching.py index 43003eb0..7a00c27f 100644 --- a/openff/qcsubmit/tests/results/test_caching.py +++ b/openff/qcsubmit/tests/results/test_caching.py @@ -27,13 +27,11 @@ def test_batched_indices(): def test_cached_fractal_client_bad_address(): - with pytest.raises(ConnectionRefusedError): cached_fractal_client("http://localhost:1234/") def test_cached_fractal_client_snowflake(): - from qcfractal import FractalSnowflake snowflake = FractalSnowflake(start_server=False) @@ -43,7 +41,6 @@ def test_cached_fractal_client_snowflake(): def test_cached_query_procedures(public_client): - assert len(_record_cache) == 0 record_ids = ["32651863", "32651864"] @@ -62,7 +59,6 @@ def test_cached_query_procedures(public_client): def test_cached_query_molecule(public_client): - assert len(_molecule_cache) == 0 molecule_ids = ["25696236", "25696152"] @@ -101,10 +97,7 @@ def test_cached_query_molecule(public_client): ), ], ) -def test_record_to_molecule( - result, query_function, public_client -): - +def test_record_to_molecule(result, query_function, public_client): expected_molecule = Molecule.from_mapped_smiles(result.cmiles) records = query_function(public_client.address, [result]) @@ -123,7 +116,7 @@ def test_record_to_molecule( assert numpy.allclose( molecule.conformers[0].m_as(unit.bohr), - expected_qc_molecule.geometry.reshape((molecule.n_atoms, 3)) + expected_qc_molecule.geometry.reshape((molecule.n_atoms, 3)), ) are_isomorphic, _ = Molecule.are_isomorphic(molecule, expected_molecule) @@ -136,7 +129,6 @@ def test_record_to_molecule( def test_cached_query_torsion_drive_results(public_client): - assert len(_grid_id_cache) == 0 result = TorsionDriveResult( @@ -162,10 +154,9 @@ def test_cached_query_torsion_drive_results(public_client): } for grid_id, conformer in zip(molecule.properties["grid_ids"], molecule.conformers): - assert numpy.allclose( conformer.m_as(unit.bohr), - expected_qc_molecules[grid_id].geometry.reshape((molecule.n_atoms, 3)) + expected_qc_molecules[grid_id].geometry.reshape((molecule.n_atoms, 3)), ) assert len(molecule.properties["grid_ids"]) == 24 diff --git a/openff/qcsubmit/tests/results/test_filters.py b/openff/qcsubmit/tests/results/test_filters.py index 0acf2d99..c1955c33 100644 --- a/openff/qcsubmit/tests/results/test_filters.py +++ b/openff/qcsubmit/tests/results/test_filters.py @@ -36,7 +36,6 @@ def test_apply_filter(basic_result_collection, caplog): class DummyFilter(ResultFilter): def _apply(self, result_collection): - result_collection.entries = { "http://localhost:442": result_collection.entries[ "http://localhost:442" @@ -56,7 +55,6 @@ def _apply(self, result_collection): def test_apply_cmiles_filter(basic_result_collection): - class DummyFilter(CMILESResultFilter): def _filter_function(self, result) -> bool: return result.record_id == "1" @@ -66,7 +64,6 @@ def _filter_function(self, result) -> bool: assert filtered_collection.n_results == 2 for port in [442, 443]: - address = f"http://localhost:{port}" assert address in filtered_collection.entries @@ -88,19 +85,16 @@ def _filter_function(self, result, record, molecule) -> bool: def test_smiles_filter_mutual_inputs(): - with pytest.raises(ValidationError, match="exactly one of `smiles_to_include`"): SMILESFilter(smiles_to_include=["C"], smiles_to_exclude=["CC"]) def test_smarts_filter_mutual_inputs(): - with pytest.raises(ValidationError, match="exactly one of `smarts_to_include`"): SMARTSFilter(smarts_to_include=["C"], smarts_to_exclude=["CC"]) def test_charge_filter_mutual_inputs(): - with pytest.raises(ValidationError, match="exactly one of `charges_to_include`"): ChargeFilter(charges_to_include=[0], charges_to_exclude=[1, 2]) @@ -127,13 +121,11 @@ def test_charge_filter_mutual_inputs(): ], ) def test_molecule_filter_apply(result_filter, expected_ids, basic_result_collection): - filtered_collection = result_filter.apply(basic_result_collection) assert {*expected_ids} == {*filtered_collection.entries} for address, entry_ids in expected_ids.items(): - assert entry_ids == { entry.record_id for entry in filtered_collection.entries[address] } @@ -163,13 +155,11 @@ def test_molecule_filter_tautomers(tautomer_basic_result_collection): def test_basic_record_filter_apply( result_filter, expected_ids, h_bond_basic_result_collection ): - filtered_collection = result_filter.apply(h_bond_basic_result_collection) assert {*expected_ids} == {*filtered_collection.entries} for address, entry_ids in expected_ids.items(): - assert entry_ids == { entry.record_id for entry in filtered_collection.entries[address] } @@ -187,13 +177,11 @@ def test_basic_record_filter_apply( def test_optimization_record_filter_apply( result_filter, expected_ids, optimization_result_collection ): - filtered_collection = result_filter.apply(optimization_result_collection) assert {*expected_ids} == {*filtered_collection.entries} for address, entry_ids in expected_ids.items(): - assert entry_ids == { entry.record_id for entry in filtered_collection.entries[address] } @@ -208,20 +196,17 @@ def test_optimization_record_filter_apply( def test_torsion_drive_record_filter_apply( result_filter, expected_ids, torsion_drive_result_collection ): - filtered_collection = result_filter.apply(torsion_drive_result_collection) assert {*expected_ids} == {*filtered_collection.entries} for address, entry_ids in expected_ids.items(): - assert entry_ids == { entry.record_id for entry in filtered_collection.entries[address] } def test_connectivity_filter(): - result = BasicResult( record_id=ObjectId("1"), cmiles="[Cl:1][Cl:2]", @@ -253,7 +238,6 @@ def test_connectivity_filter(): def test_record_status_filter(): - record = ResultRecord( id=ObjectId("1"), program="psi4", @@ -272,8 +256,11 @@ def test_record_status_filter(): def test_charge_filter(): - - record = BasicResult(record_id=ObjectId("1"), cmiles="[N+:1](=[O:2])([O-:3])[O-:4]", inchi_key="NHNBFGGVMKEFGY-UHFFFAOYSA-N") + record = BasicResult( + record_id=ObjectId("1"), + cmiles="[N+:1](=[O:2])([O-:3])[O-:4]", + inchi_key="NHNBFGGVMKEFGY-UHFFFAOYSA-N", + ) charge_filter = ChargeFilter(charges_to_include=[-1, 0]) assert charge_filter._filter_function(entry=record) is True @@ -284,7 +271,6 @@ def test_charge_filter(): def test_element_filter(basic_result_collection): - # use mixed ints and str element_filter = ElementFilter(allowed_elements=[1, 6, "O"]) @@ -301,12 +287,13 @@ def test_element_filter(basic_result_collection): def test_lowest_energy_filter(optimization_result_collection_duplicates): - energy_filter = LowestEnergyFilter() # should have 2 results assert optimization_result_collection_duplicates.n_results == 2 - result = energy_filter.apply(result_collection=optimization_result_collection_duplicates) + result = energy_filter.apply( + result_collection=optimization_result_collection_duplicates + ) # make sure we only have one result assert result.n_molecules == 1 @@ -317,7 +304,6 @@ def test_lowest_energy_filter(optimization_result_collection_duplicates): def test_min_conformers_filter( optimization_result_collection_duplicates, min_conformers, n_expected_results ): - min_conformers_filter = MinimumConformersFilter(min_conformers=min_conformers) assert optimization_result_collection_duplicates.n_results == 2 @@ -346,7 +332,6 @@ def test_min_conformers_filter( def test_rmsd_conformer_filter( max_conformers, rmsd_tolerance, heavy_atoms_only, expected_record_ids, monkeypatch ): - molecules = [] for conformer in [ @@ -354,7 +339,6 @@ def test_rmsd_conformer_filter( numpy.array([[0.0, 0.0, 0.0], [2.0, 0.0, 0.0]]) * unit.angstrom, numpy.array([[0.0, 0.0, 0.0], [4.0, 0.0, 0.0]]) * unit.angstrom, ]: - molecule = Molecule.from_smiles(smiles="Cl[H]") molecule._conformers = [conformer] @@ -383,7 +367,6 @@ def test_rmsd_conformer_filter( def test_rmsd_conformer_filter_canonical_order(monkeypatch): - molecule_a = Molecule.from_mapped_smiles("[Cl:1][H:2]") molecule_a._conformers = [ numpy.array([[0.0, 0.0, 0.0], [1.0, 0.0, 0.0]]) * unit.angstrom @@ -412,7 +395,6 @@ def test_rmsd_conformer_filter_canonical_order(monkeypatch): ], ) def test_rmsd_conformer_filter_rmsd_matrix(rmsd_function_name): - molecule = Molecule.from_mapped_smiles("[O:1]=[C:2]=[O:3]") molecule._conformers = [ numpy.array([[-1.0, 0.0, 0.0], [0.0, 0.0, 0.0], [1.0, 0.0, 0.0]]) @@ -450,7 +432,6 @@ def test_rmsd_conformer_filter_rmsd_matrix(rmsd_function_name): def test_rmsd_conformer_filter_rmsd_matrix_heavy_only( rmsd_function_name, heavy_atoms_only, expected_rmsd_matrix ): - molecule = Molecule.from_smiles("Cl[H]") molecule._conformers = [ numpy.array([[0.0, 0.0, 0.0], [1.0, 0.0, 0.0]]) * unit.angstrom, @@ -485,7 +466,6 @@ def test_rmsd_conformer_filter_rmsd_matrix_heavy_only( def test_rmsd_conformer_filter_rmsd_matrix_automorphs( rmsd_function_name, check_automorphs, expected_rmsd_matrix ): - molecule = Molecule.from_mapped_smiles("[Br:3][C:1]([Cl:4])=[C:2]([Cl:6])[Cl:5]") molecule._conformers = [ numpy.array( @@ -528,10 +508,9 @@ def test_rmsd_conformer_filter_rmsd_matrix_automorphs( (["rdkit"], 1), (["openeye"], 0), (["openeye", "rdkit"], 0), - ] + ], ) def test_unperceivable_stereo_filter(toolkits, n_expected, public_client): - collection = OptimizationResultCollection( entries={ "https://api.qcarchive.molssi.org:443/": [ @@ -548,7 +527,7 @@ def test_unperceivable_stereo_filter(toolkits, n_expected, public_client): "[H:65])([H:47])[H:48])([H:43])[H:44])[H:56])[H:38])[H:41])" "[H:39]" ), - inchi_key="GMRICROFHKBHBU-MRXNPFEDSA-N" + inchi_key="GMRICROFHKBHBU-MRXNPFEDSA-N", ) ] } diff --git a/openff/qcsubmit/tests/results/test_results.py b/openff/qcsubmit/tests/results/test_results.py index 7af67f37..22cdfb1f 100644 --- a/openff/qcsubmit/tests/results/test_results.py +++ b/openff/qcsubmit/tests/results/test_results.py @@ -42,7 +42,6 @@ class MockServerInfo: - def dict(self): return { "name": "Mock", @@ -54,7 +53,6 @@ def dict(self): def test_base_molecule_property(): - record = BasicResult( record_id=ObjectId("1"), cmiles="[Cl:2][H:1]", @@ -93,7 +91,6 @@ def test_base_molecule_property(): ], ) def test_base_validate_entries(entries, expected_raises): - with expected_raises: collection = BasicResultCollection(entries={"http://localhost:443": entries}) @@ -102,7 +99,6 @@ def test_base_validate_entries(entries, expected_raises): def test_base_n_results_property(): - collection = BasicResultCollection( entries={ "http://localhost:442": [ @@ -126,7 +122,6 @@ def test_base_n_results_property(): def test_base_n_molecules_property(): - collection = BasicResultCollection( entries={ "http://localhost:442": [ @@ -155,7 +150,6 @@ def test_base_n_molecules_property(): def test_base_validate_record_types(): - records = [ ResultRecord( program="psi4", @@ -185,7 +179,6 @@ def test_base_validate_record_types(): def test_base_filter(basic_result_collection): class DummyFilter(ResultFilter): def _apply(self, result_collection): - result_collection.entries = { "http://localhost:442": result_collection.entries[ "http://localhost:442" @@ -208,7 +201,6 @@ def _apply(self, result_collection): def test_base_smirnoff_coverage(): - collection = TorsionDriveResultCollection( entries={ "http://localhost:442": [ @@ -360,7 +352,6 @@ def mock_query_procedures(*args, **kwargs): return [record] def mock_query_molecules(*args, **kwargs): - molecule: Molecule = Molecule.from_smiles("[Cl:1][Cl:2]") molecule.add_conformer(numpy.arange(6).reshape((2, 3)) * unit.angstrom) @@ -392,9 +383,9 @@ def test_optimization_create_basic_dataset(optimization_result_collection): dataset = optimization_result_collection.create_basic_dataset( dataset_name="new basicdataset", description="test new optimizationdataset", - tagline='new optimization dataset', + tagline="new optimization dataset", driver="energy", - qc_specs=[QCSpec(spec_name="some-name", basis="6-31G")] + qc_specs=[QCSpec(spec_name="some-name", basis="6-31G")], ) assert len(dataset.qc_specifications) == 1 @@ -405,16 +396,14 @@ def test_optimization_create_basic_dataset(optimization_result_collection): assert dataset.n_molecules == 4 assert dataset.n_records == 5 # the collection contains 1 duplicate - + def test_optimization_to_basic_result_collection( optimization_result_collection, monkeypatch ): - def mock_automodel_request(*args, **kwargs): return MockServerInfo() def mock_query_results(*args, **kwargs): - assert "program" in kwargs and kwargs["program"] == "psi4" assert "method" in kwargs and kwargs["method"] == "scf" assert "basis" in kwargs and kwargs["basis"] == "sto-3g" @@ -475,7 +464,6 @@ def mock_query_results(*args, **kwargs): def test_torsion_smirnoff_coverage(public_client, monkeypatch): - molecule: Molecule = Molecule.from_mapped_smiles( "[H:1][C:2]([H:7])([H:8])" "[C:3]([H:9])([H:10])" diff --git a/openff/qcsubmit/tests/specifications/test_pcm.py b/openff/qcsubmit/tests/specifications/test_pcm.py index 54a8fa58..f9186788 100644 --- a/openff/qcsubmit/tests/specifications/test_pcm.py +++ b/openff/qcsubmit/tests/specifications/test_pcm.py @@ -9,10 +9,13 @@ from openff.qcsubmit.exceptions import PCMSettingError, QCSpecificationError -@pytest.mark.parametrize("data", [ - pytest.param(("ANGSTROM", None), id="angstrom"), - pytest.param(("bohr", PCMSettingError), id="Bhor not supported") -]) +@pytest.mark.parametrize( + "data", + [ + pytest.param(("ANGSTROM", None), id="angstrom"), + pytest.param(("bohr", PCMSettingError), id="Bhor not supported"), + ], +) def test_pcm_units(data): """ Make sure proper units are validated. @@ -27,10 +30,13 @@ def test_pcm_units(data): assert pcm.medium_Solvent == "H2O" -@pytest.mark.parametrize("data", [ - pytest.param((1998, None), id="1998"), - pytest.param((2020, PCMSettingError), id="2020 not valid.") -]) +@pytest.mark.parametrize( + "data", + [ + pytest.param((1998, None), id="1998"), + pytest.param((2020, PCMSettingError), id="2020 not valid."), + ], +) def test_pcm_codata(data): """ Make sure an accptable codata value is passed and an error is raised if not. @@ -58,10 +64,13 @@ def test_pcm_cavity(): assert pcm.cavity_Type == "GePol" -@pytest.mark.parametrize("data", [ - pytest.param(("uff", None), id="UFF"), - pytest.param(("openff", PCMSettingError), id="Openff error") -]) +@pytest.mark.parametrize( + "data", + [ + pytest.param(("uff", None), id="UFF"), + pytest.param(("openff", PCMSettingError), id="Openff error"), + ], +) def test_pcm_radiisets(data): """ Make sure only valid radii are allowed @@ -89,10 +98,13 @@ def test_pcm_cavity_mode(): assert pcm.cavity_Mode == "Implicit" -@pytest.mark.parametrize("data", [ - pytest.param(("IEFPCM", None), id="IEFPCM"), - pytest.param(("BadSolver", PCMSettingError), id="BadSolver error") -]) +@pytest.mark.parametrize( + "data", + [ + pytest.param(("IEFPCM", None), id="IEFPCM"), + pytest.param(("BadSolver", PCMSettingError), id="BadSolver error"), + ], +) def test_pcm_solver(data): """ Make sure only IEFPCM and CPCM solvers are allowed. @@ -100,19 +112,24 @@ def test_pcm_solver(data): solver, error = data if error is not None: with pytest.raises(error): - _ = PCMSettings(units="au", medium_Solvent="water", medium_SolverType=solver) + _ = PCMSettings( + units="au", medium_Solvent="water", medium_SolverType=solver + ) else: pcm = PCMSettings(units="au", medium_Solvent="water", medium_SolverType=solver) assert pcm.medium_SolverType == solver -@pytest.mark.parametrize("solvent_data", [ - pytest.param(("Water", "H2O", None), id="Water"), - pytest.param(("DMSO", "DMSO", None), id="DMSO"), - pytest.param(("1,2-dichloroethane", "C2H4CL2", None), id="1,2-dichloroethane"), - pytest.param(("THF", "THF", None), id="THF"), - pytest.param(("explicit", "explicit", PCMSettingError), id="Bad solvent") -]) +@pytest.mark.parametrize( + "solvent_data", + [ + pytest.param(("Water", "H2O", None), id="Water"), + pytest.param(("DMSO", "DMSO", None), id="DMSO"), + pytest.param(("1,2-dichloroethane", "C2H4CL2", None), id="1,2-dichloroethane"), + pytest.param(("THF", "THF", None), id="THF"), + pytest.param(("explicit", "explicit", PCMSettingError), id="Bad solvent"), + ], +) def test_pcm_solvent(solvent_data): """ Make sure solvents can be accepted as either names or chemical formula but are always converted to formula. @@ -139,7 +156,7 @@ def test_pcm_unit_conversion_defaults(): pcm2 = PCMSettings(units="angstrom", medium_Solvent="water") assert pcm2.medium_ProbeRadius == pcm.medium_ProbeRadius * constants.bohr2angstroms - assert pcm2.cavity_Area == pcm.cavity_Area * constants.bohr2angstroms ** 2 + assert pcm2.cavity_Area == pcm.cavity_Area * constants.bohr2angstroms**2 assert pcm2.cavity_MinRadius == pcm.cavity_MinRadius * constants.bohr2angstroms @@ -161,7 +178,10 @@ def test_pcm_default_string(): pcm = PCMSettings(units="au", medium_Solvent="Water") - assert pcm.to_string() == '\n Units = au\n CODATA = 2010\n Medium {\n SolverType = IEFPCM\n Nonequilibrium = False\n Solvent = H2O\n MatrixSymm = True\n Correction = 0.0\n DiagonalScaling = 1.07\n ProbeRadius = 1.0}\n Cavity {\n Type = GePol\n Area = 0.3\n Scaling = True\n RadiiSet = Bondi\n MinRadius = 100\n Mode = Implicit}' + assert ( + pcm.to_string() + == "\n Units = au\n CODATA = 2010\n Medium {\n SolverType = IEFPCM\n Nonequilibrium = False\n Solvent = H2O\n MatrixSymm = True\n Correction = 0.0\n DiagonalScaling = 1.07\n ProbeRadius = 1.0}\n Cavity {\n Type = GePol\n Area = 0.3\n Scaling = True\n RadiiSet = Bondi\n MinRadius = 100\n Mode = Implicit}" + ) def test_qcspec_with_solvent(): @@ -171,7 +191,14 @@ def test_qcspec_with_solvent(): # make sure an error is raised with any program that is not psi4 with pytest.raises(QCSpecificationError): - _ = QCSpec(method="ani2x", basis=None, program="torchani", spec_name="ani2x", spec_description="testing ani with solvent", implicit_solvent=PCMSettings(units="au", medium_Solvent="water")) + _ = QCSpec( + method="ani2x", + basis=None, + program="torchani", + spec_name="ani2x", + spec_description="testing ani with solvent", + implicit_solvent=PCMSettings(units="au", medium_Solvent="water"), + ) # now try with PSI4 qc_spec = QCSpec(implicit_solvent=PCMSettings(units="au", medium_Solvent="water")) diff --git a/openff/qcsubmit/tests/test_common_structures.py b/openff/qcsubmit/tests/test_common_structures.py index ba337e25..7b4a5845 100644 --- a/openff/qcsubmit/tests/test_common_structures.py +++ b/openff/qcsubmit/tests/test_common_structures.py @@ -7,16 +7,21 @@ def test_attributes_from_openff_molecule(): - mol = Molecule.from_smiles("CC") attributes = MoleculeAttributes.from_openff_molecule(mol) # now make our own cmiles test_cmiles = { - "canonical_smiles": mol.to_smiles(isomeric=False, explicit_hydrogens=False, mapped=False), - "canonical_isomeric_smiles": mol.to_smiles(isomeric=True, explicit_hydrogens=False, mapped=False), - "canonical_explicit_hydrogen_smiles": mol.to_smiles(isomeric=False, explicit_hydrogens=True, mapped=False), + "canonical_smiles": mol.to_smiles( + isomeric=False, explicit_hydrogens=False, mapped=False + ), + "canonical_isomeric_smiles": mol.to_smiles( + isomeric=True, explicit_hydrogens=False, mapped=False + ), + "canonical_explicit_hydrogen_smiles": mol.to_smiles( + isomeric=False, explicit_hydrogens=True, mapped=False + ), "canonical_isomeric_explicit_hydrogen_smiles": mol.to_smiles( isomeric=True, explicit_hydrogens=True, mapped=False ), @@ -28,7 +33,7 @@ def test_attributes_from_openff_molecule(): "inchi_key": mol.to_inchikey(fixed_hydrogens=False), "fixed_hydrogen_inchi": mol.to_inchi(fixed_hydrogens=True), "fixed_hydrogen_inchi_key": mol.to_inchikey(fixed_hydrogens=True), - "unique_fixed_hydrogen_inchi_keys": {mol.to_inchikey(fixed_hydrogens=True)} + "unique_fixed_hydrogen_inchi_keys": {mol.to_inchikey(fixed_hydrogens=True)}, } assert test_cmiles == attributes @@ -50,7 +55,9 @@ def test_attributes_from_openff_multi_component(): """ Make sure the unique inchi keys are updated correctly. """ - mol = Molecule.from_smiles("CC1=C(C=C(C=C1)NC(=O)C2=CC=C(C=C2)CN3CCN(CC3)C)NC4=NC=CC(=N4)C5=CN=CC=C5.CS(=O)(=O)O") + mol = Molecule.from_smiles( + "CC1=C(C=C(C=C1)NC(=O)C2=CC=C(C=C2)CN3CCN(CC3)C)NC4=NC=CC(=N4)C5=CN=CC=C5.CS(=O)(=O)O" + ) attributes = MoleculeAttributes.from_openff_molecule(mol) assert len(attributes.unique_fixed_hydrogen_inchi_keys) == 2 @@ -81,9 +88,9 @@ def test_attributes_to_openff_molecule(): short_description="abcdefgh", long_description_url="https://github.com/openforcefield", long_description="abcdefgh", - elements={"C", "H"} + elements={"C", "H"}, ), - does_not_raise() + does_not_raise(), ), ( Metadata( @@ -91,21 +98,20 @@ def test_attributes_to_openff_molecule(): dataset_name="ABC", short_description="abcdefgh", long_description="abcdefgh", - elements={"C", "H"} + elements={"C", "H"}, ), - does_not_raise() + does_not_raise(), ), ( Metadata(), pytest.raises( DatasetInputError, - match="The metadata has the following incomplete fields" - ) - ) - ] + match="The metadata has the following incomplete fields", + ), + ), + ], ) def test_validate_metadata(metadata, expected_raises): - with expected_raises: metadata.validate_metadata(raise_errors=True) diff --git a/openff/qcsubmit/tests/test_datasets.py b/openff/qcsubmit/tests/test_datasets.py index db2aefd3..e14424ae 100644 --- a/openff/qcsubmit/tests/test_datasets.py +++ b/openff/qcsubmit/tests/test_datasets.py @@ -86,16 +86,25 @@ def test_list_datasets(): assert len(datasets) == 3 -@pytest.mark.parametrize("dataset", [ - pytest.param(BasicDataset, id="basic dataset"), - pytest.param(OptimizationDataset, id="optimization dataset"), - pytest.param(TorsiondriveDataset, id="torsiondrive dataset") -]) +@pytest.mark.parametrize( + "dataset", + [ + pytest.param(BasicDataset, id="basic dataset"), + pytest.param(OptimizationDataset, id="optimization dataset"), + pytest.param(TorsiondriveDataset, id="torsiondrive dataset"), + ], +) def test_load_dataset_obj(dataset): """ Test the dataset util function can load any of the registered datasets """ - new_dataset = load_dataset(dataset(dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX").dict()) + new_dataset = load_dataset( + dataset( + dataset_name="Test dataset", + dataset_tagline="XXXXXXXX", + description="XXXXXXXX", + ).dict() + ) assert new_dataset.type == dataset.__fields__["type"].default @@ -135,11 +144,17 @@ def test_componentresult_repr(): """ Make sure the __repr__ is working. """ - result = ComponentResult(component_name="Test deduplication", component_description={}, - component_provenance={}, - molecules=duplicated_molecules(include_conformers=False, duplicates=1)) + result = ComponentResult( + component_name="Test deduplication", + component_description={}, + component_provenance={}, + molecules=duplicated_molecules(include_conformers=False, duplicates=1), + ) - assert repr(result) == "ComponentResult(name=Test deduplication, molecules=4, filtered=0)" + assert ( + repr(result) + == "ComponentResult(name=Test deduplication, molecules=4, filtered=0)" + ) def test_componentresult_str(): @@ -147,11 +162,17 @@ def test_componentresult_str(): Make sure the __str__ is working. """ - result = ComponentResult(component_name="Test deduplication", component_description={}, - component_provenance={}, - molecules=duplicated_molecules(include_conformers=False, duplicates=1)) + result = ComponentResult( + component_name="Test deduplication", + component_description={}, + component_provenance={}, + molecules=duplicated_molecules(include_conformers=False, duplicates=1), + ) - assert str(result) == "" + assert ( + str(result) + == "" + ) def test_componetresult_deduplication_standard(): @@ -159,8 +180,11 @@ def test_componetresult_deduplication_standard(): Test the components results ability to deduplicate molecules. """ - result = ComponentResult(component_name="Test deduplication", component_description={}, - component_provenance={}) + result = ComponentResult( + component_name="Test deduplication", + component_description={}, + component_provenance={}, + ) assert result.component_name == "Test deduplication" @@ -192,27 +216,34 @@ def test_componetresult_directory(): butane.to_file("butane.pdb", "pdb") # now make a result to load these molecules - result = ComponentResult(component_name="test", - component_description={}, - component_provenance={}, - input_directory=".") + result = ComponentResult( + component_name="test", + component_description={}, + component_provenance={}, + input_directory=".", + ) assert result.n_molecules == 2 -@pytest.mark.parametrize("file_name", [ - pytest.param("benzene.sdf", id="SDF file"), - pytest.param("butane_conformers.pdb", id="PDB file"), - pytest.param("tautomers_small.smi", id="SMI file"), - pytest.param("hdf5-example.hdf5", id="HDF5 file") -]) +@pytest.mark.parametrize( + "file_name", + [ + pytest.param("benzene.sdf", id="SDF file"), + pytest.param("butane_conformers.pdb", id="PDB file"), + pytest.param("tautomers_small.smi", id="SMI file"), + pytest.param("hdf5-example.hdf5", id="HDF5 file"), + ], +) def test_componetresult_input_file(file_name): """ Test loading up some molecules from an input file """ - result = ComponentResult(component_name="Test", - component_description={}, - component_provenance={}, - input_file=get_data(file_name)) + result = ComponentResult( + component_name="Test", + component_description={}, + component_provenance={}, + input_file=get_data(file_name), + ) assert result.n_molecules > 0 @@ -222,8 +253,11 @@ def test_componentresult_deduplication_coordinates(): The conformers on the duplicated molecules should be the same and will not be transfered. """ - result = ComponentResult(component_name="Test deduplication", component_description={}, - component_provenance={}) + result = ComponentResult( + component_name="Test deduplication", + component_description={}, + component_provenance={}, + ) # test using conformers, conformers that are the same will be condensed duplicates = 2 @@ -241,7 +275,11 @@ def test_componentresult_deduplication_coordinates(): @pytest.mark.parametrize( "duplicates", - [pytest.param(2, id="two duplicates"), pytest.param(4, id="four duplicates"), pytest.param(6, id="six duplicates")], + [ + pytest.param(2, id="two duplicates"), + pytest.param(4, id="four duplicates"), + pytest.param(6, id="six duplicates"), + ], ) def test_componentresult_deduplication_diff_coords(duplicates): """ @@ -249,8 +287,11 @@ def test_componentresult_deduplication_diff_coords(duplicates): same molecule. """ - result = ComponentResult(component_name="Test deduplication", component_description={}, - component_provenance={}) + result = ComponentResult( + component_name="Test deduplication", + component_description={}, + component_provenance={}, + ) # test using conformers that are different molecules = duplicated_molecules(include_conformers=False, duplicates=duplicates) @@ -266,7 +307,10 @@ def test_componentresult_deduplication_diff_coords(duplicates): for i in range(molecule.n_conformers): for j in range(molecule.n_conformers): if i != j: - assert molecule.conformers[i].tolist() != molecule.conformers[j].tolist() + assert ( + molecule.conformers[i].tolist() + != molecule.conformers[j].tolist() + ) def test_componentresult_deduplication_iso(): @@ -274,13 +318,22 @@ def test_componentresult_deduplication_iso(): Make sure that duplicates are correctly handled when the inchikey matches but standard isomorphism fails due to bond order and formal charge differences. """ - result = ComponentResult(component_name="Test iso deduplication", component_description={}, - component_provenance={}) + result = ComponentResult( + component_name="Test iso deduplication", + component_description={}, + component_provenance={}, + ) # build two molecules which has the same inchikey - mol1 = Molecule.from_smiles(smiles="[H]c1c(c(c(c(c1[H])[H])[S+2](N([H])[H])([O-])[O-])[H])[H]", hydrogens_are_explicit=True) + mol1 = Molecule.from_smiles( + smiles="[H]c1c(c(c(c(c1[H])[H])[S+2](N([H])[H])([O-])[O-])[H])[H]", + hydrogens_are_explicit=True, + ) mol1.generate_conformers(n_conformers=1) - mol2 = Molecule.from_smiles(smiles="[H]c1c(c(c(c(c1[H])[H])S(=O)(=O)N([H])[H])[H])[H]", hydrogens_are_explicit=True) + mol2 = Molecule.from_smiles( + smiles="[H]c1c(c(c(c(c1[H])[H])S(=O)(=O)N([H])[H])[H])[H]", + hydrogens_are_explicit=True, + ) mol2.generate_conformers(n_conformers=1) # add the molecules @@ -295,8 +348,11 @@ def test_componentresult_remove_molecule(): """ Test removing a molecule not in a dataset. """ - result = ComponentResult(component_name="Test deduplication", component_description={}, - component_provenance={}) + result = ComponentResult( + component_name="Test deduplication", + component_description={}, + component_provenance={}, + ) methanol = Molecule.from_file(get_data("methanol.sdf"), "sdf") result.filter_molecule(molecule=methanol) @@ -308,14 +364,19 @@ def test_componentresult_deduplication_torsions_same_bond_same_coords(): Make sure that the same rotatable bond is not highlighted more than once when deduplicating molecules. """ - result = ComponentResult(component_name="Test deduplication", component_description={}, - component_provenance={}) + result = ComponentResult( + component_name="Test deduplication", + component_description={}, + component_provenance={}, + ) - molecules = [Molecule.from_file(get_data("methanol.sdf"), 'sdf')] * 3 + molecules = [Molecule.from_file(get_data("methanol.sdf"), "sdf")] * 3 ethanol_dihedrals = [(5, 1, 0, 2), (5, 1, 0, 3), (5, 1, 0, 4)] for molecule, dihedral in zip(molecules, ethanol_dihedrals): torsion_indexer = TorsionIndexer() - torsion_indexer.add_torsion(torsion=dihedral, scan_range=None, symmetry_group=(1, 1)) + torsion_indexer.add_torsion( + torsion=dihedral, scan_range=None, symmetry_group=(1, 1) + ) molecule.properties["dihedrals"] = torsion_indexer result.add_molecule(molecule) @@ -326,33 +387,56 @@ def test_componentresult_deduplication_torsions_same_bond_same_coords(): assert (0, 1) in result.molecules[0].properties["dihedrals"].torsions -@pytest.mark.parametrize("ethanol_data", [ - pytest.param(("ethanol.sdf", [(8, 2, 1, 0)], None), id="correct torsion ethanol"), - pytest.param(("ethanol.sdf", [(22, 23, 24, 100)], DihedralConnectionError), - id="incorrect torsion ethanol"), - pytest.param(("ethanol.sdf", [(7, 2, 1, 4)], DihedralConnectionError), - id="incorrect torsion ethanol"), - pytest.param(("ethanol.sdf", [(8, 2, 1, 0), (4, 0, 1, 2)], None), - id="correct double torsion ethanol"), - pytest.param(("ethanol.sdf", [(7, 2, 1, 4), (4, 0, 1, 2)], - DihedralConnectionError), id="incorrect double torsion ethanol"), - pytest.param(("ethanol.sdf", [(3, 0, 4, 5)], None), - id="correct improper ethanol"), - pytest.param(("ethanol.sdf", [(100, 0, 4, 5)], DihedralConnectionError), - id="incorrect improper ethanol index error"), - pytest.param(("ethanol.sdf", [(7, 0, 4, 5)], DihedralConnectionError), - id="incorrect improper ethanol"), - pytest.param(("benzene.sdf", [(0, 1, 2, 7)], None), - id="correct improper benzene"), - pytest.param(("benzene.sdf", [(4, 0, 1, 2)], DihedralConnectionError), - id="incorrect improper benzene"), -]) +@pytest.mark.parametrize( + "ethanol_data", + [ + pytest.param( + ("ethanol.sdf", [(8, 2, 1, 0)], None), id="correct torsion ethanol" + ), + pytest.param( + ("ethanol.sdf", [(22, 23, 24, 100)], DihedralConnectionError), + id="incorrect torsion ethanol", + ), + pytest.param( + ("ethanol.sdf", [(7, 2, 1, 4)], DihedralConnectionError), + id="incorrect torsion ethanol", + ), + pytest.param( + ("ethanol.sdf", [(8, 2, 1, 0), (4, 0, 1, 2)], None), + id="correct double torsion ethanol", + ), + pytest.param( + ("ethanol.sdf", [(7, 2, 1, 4), (4, 0, 1, 2)], DihedralConnectionError), + id="incorrect double torsion ethanol", + ), + pytest.param( + ("ethanol.sdf", [(3, 0, 4, 5)], None), id="correct improper ethanol" + ), + pytest.param( + ("ethanol.sdf", [(100, 0, 4, 5)], DihedralConnectionError), + id="incorrect improper ethanol index error", + ), + pytest.param( + ("ethanol.sdf", [(7, 0, 4, 5)], DihedralConnectionError), + id="incorrect improper ethanol", + ), + pytest.param( + ("benzene.sdf", [(0, 1, 2, 7)], None), id="correct improper benzene" + ), + pytest.param( + ("benzene.sdf", [(4, 0, 1, 2)], DihedralConnectionError), + id="incorrect improper benzene", + ), + ], +) def test_dataset_dihedral_validation(ethanol_data): """ Test adding molecules to a dataset with different dihedrals, this will make sure that the dataset validators work. """ - dataset = TorsiondriveDataset(dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") + dataset = TorsiondriveDataset( + dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) molecule_file, dihedrals, error = ethanol_data ethanol = Molecule.from_file(get_data(molecule_file)) index = "test1" @@ -370,9 +454,16 @@ def test_molecular_complex_validator(imatinib_mesylate): """ from openff.qcsubmit.exceptions import MolecularComplexError - dataset = TorsiondriveDataset(dataset_name="test dataset", dataset_tagline="XXXXXXXXXX", description="XXXXXXXX") + + dataset = TorsiondriveDataset( + dataset_name="test dataset", + dataset_tagline="XXXXXXXXXX", + description="XXXXXXXX", + ) with pytest.raises(MolecularComplexError): - dataset.add_molecule(index="1", molecule=imatinib_mesylate, dihedrals=[(0, 1, 2, 3)]) + dataset.add_molecule( + index="1", molecule=imatinib_mesylate, dihedrals=[(0, 1, 2, 3)] + ) def test_molecular_complex_fragment_reorder(imatinib_mesylate): @@ -381,22 +472,26 @@ def test_molecular_complex_fragment_reorder(imatinib_mesylate): """ attributes = MoleculeAttributes.from_openff_molecule(molecule=imatinib_mesylate) entry = DatasetEntry( - off_molecule=imatinib_mesylate, - attributes=attributes, - index="1", - extras={} + off_molecule=imatinib_mesylate, attributes=attributes, index="1", extras={} ) # make sure the attributes are regenerated assert attributes.json() != entry.attributes.json() - assert attributes.canonical_isomeric_explicit_hydrogen_mapped_smiles != entry.attributes.canonical_isomeric_explicit_hydrogen_mapped_smiles + assert ( + attributes.canonical_isomeric_explicit_hydrogen_mapped_smiles + != entry.attributes.canonical_isomeric_explicit_hydrogen_mapped_smiles + ) def test_dataset_linear_dihedral_validator(): """ Make sure that dataset rejects molecules with tagged linear bonds. """ - dataset = TorsiondriveDataset(dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") - molecules = Molecule.from_file(get_data("linear_molecules.sdf"), allow_undefined_stereo=True) + dataset = TorsiondriveDataset( + dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) + molecules = Molecule.from_file( + get_data("linear_molecules.sdf"), allow_undefined_stereo=True + ) linear_smarts = "[*!D1:1]~[$(*#*)&D2,$(C=*)&D2:2]" # for each molecule we want to tag each linear dihedral @@ -405,34 +500,63 @@ def test_dataset_linear_dihedral_validator(): bond = molecule.get_bond_between(*matches[0]) dihedral = get_torsion(bond) with pytest.raises(LinearTorsionError): - dataset.add_molecule(index="linear test", molecule=molecule, dihedrals=[dihedral, ]) + dataset.add_molecule( + index="linear test", + molecule=molecule, + dihedrals=[ + dihedral, + ], + ) def test_torsiondrive_unqiue_settings(): """ Test adding unqiue settings to a torsiondrive entry. """ - dataset = TorsiondriveDataset(dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") + dataset = TorsiondriveDataset( + dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) molecule = Molecule.from_smiles("CO") bond = molecule.find_rotatable_bonds()[0] - dataset.add_molecule(index="test", molecule=molecule, dihedrals=[get_torsion(bond), ], keywords={"grid_spacing": [5], "dihedral_ranges": [(-50, 50), ]}) + dataset.add_molecule( + index="test", + molecule=molecule, + dihedrals=[ + get_torsion(bond), + ], + keywords={ + "grid_spacing": [5], + "dihedral_ranges": [ + (-50, 50), + ], + }, + ) # make sure the object was made and the values are set - assert dataset.dataset["test"].keywords.grid_spacing == [5, ] - assert dataset.dataset["test"].keywords.dihedral_ranges == [(-50, 50), ] + assert dataset.dataset["test"].keywords.grid_spacing == [ + 5, + ] + assert dataset.dataset["test"].keywords.dihedral_ranges == [ + (-50, 50), + ] assert dataset.dataset["test"].keywords.grid_spacing != dataset.grid_spacing assert dataset.dataset["test"].keywords.dihedral_ranges != dataset.dihedral_ranges -@pytest.mark.parametrize("constraint_settings", [ - pytest.param(("distance", [0, 1], None), id="distance correct order"), - pytest.param(("angle", [0, 1, 2], None), id="angle correct order"), - pytest.param(("dihedral", [0, 1, 2, 3], None), id="dihedral correct order"), - pytest.param(("xyz", [0], None), id="position correct"), - pytest.param(("xyz", [0, 1, 2, 3, 4, 5, 6], None), id="position many"), - pytest.param(("xyz", [0, 0, 1, 2], ConstraintError), id="position not unique"), - pytest.param(("distance", [0, 1, 2], ConstraintError), id="distance too many indices"), - pytest.param(("bond", [0, 1], ConstraintError), id="invalid constraint type.") -]) +@pytest.mark.parametrize( + "constraint_settings", + [ + pytest.param(("distance", [0, 1], None), id="distance correct order"), + pytest.param(("angle", [0, 1, 2], None), id="angle correct order"), + pytest.param(("dihedral", [0, 1, 2, 3], None), id="dihedral correct order"), + pytest.param(("xyz", [0], None), id="position correct"), + pytest.param(("xyz", [0, 1, 2, 3, 4, 5, 6], None), id="position many"), + pytest.param(("xyz", [0, 0, 1, 2], ConstraintError), id="position not unique"), + pytest.param( + ("distance", [0, 1, 2], ConstraintError), id="distance too many indices" + ), + pytest.param(("bond", [0, 1], ConstraintError), id="invalid constraint type."), + ], +) def test_add_freeze_constraints_validator(constraint_settings): """ Test out adding multiple types of constraints and their validators. @@ -446,7 +570,9 @@ def test_add_freeze_constraints_validator(constraint_settings): else: cons.add_freeze_constraint(constraint_type=con_type, indices=indices) # now make sure that the indices are being ordered by trying to add a constraint in reverse - cons.add_freeze_constraint(constraint_type=con_type, indices=list(reversed(indices))) + cons.add_freeze_constraint( + constraint_type=con_type, indices=list(reversed(indices)) + ) assert len(cons.freeze) == 1 # make sure the class knows it know has constraints assert cons.has_constraints is True @@ -455,15 +581,25 @@ def test_add_freeze_constraints_validator(constraint_settings): assert "set" not in cons_dict -@pytest.mark.parametrize("constraint_settings", [ - pytest.param(("distance", [0, 1], 1, None), id="distance correct order"), - pytest.param(("angle", [0, 1, 2], 100, None), id="angle correct order"), - pytest.param(("dihedral", [0, 1, 2, 3], 50, None), id="dihedral correct order"), - pytest.param(("xyz", [0], [0, 1, 2], None), id="position correct"), - pytest.param(("xyz", [0, 1, 2, 3, 4, 5, 6], [0, 1, 0], ConstraintError), id="position too many atoms"), - pytest.param(("distance", [0, 1, 2], 1, ConstraintError), id="distance too many indices"), - pytest.param(("bond", [0, 1], 1, ConstraintError), id="invalid constraint type.") -]) +@pytest.mark.parametrize( + "constraint_settings", + [ + pytest.param(("distance", [0, 1], 1, None), id="distance correct order"), + pytest.param(("angle", [0, 1, 2], 100, None), id="angle correct order"), + pytest.param(("dihedral", [0, 1, 2, 3], 50, None), id="dihedral correct order"), + pytest.param(("xyz", [0], [0, 1, 2], None), id="position correct"), + pytest.param( + ("xyz", [0, 1, 2, 3, 4, 5, 6], [0, 1, 0], ConstraintError), + id="position too many atoms", + ), + pytest.param( + ("distance", [0, 1, 2], 1, ConstraintError), id="distance too many indices" + ), + pytest.param( + ("bond", [0, 1], 1, ConstraintError), id="invalid constraint type." + ), + ], +) def test_add_set_constraints_validator(constraint_settings): """ Test out adding multiple types of constraints and their validators. @@ -473,11 +609,15 @@ def test_add_set_constraints_validator(constraint_settings): con_type, indices, value, error = constraint_settings if error is not None: with pytest.raises(error): - cons.add_set_constraint(constraint_type=con_type, indices=indices, value=value) + cons.add_set_constraint( + constraint_type=con_type, indices=indices, value=value + ) else: cons.add_set_constraint(constraint_type=con_type, indices=indices, value=value) # now make sure that the indices are being ordered by trying to add a constraint in reverse - cons.add_set_constraint(constraint_type=con_type, indices=list(reversed(indices)), value=value) + cons.add_set_constraint( + constraint_type=con_type, indices=list(reversed(indices)), value=value + ) assert len(cons.set) == 1 # make sure the class knows it know has constraints assert cons.has_constraints is True @@ -486,83 +626,129 @@ def test_add_set_constraints_validator(constraint_settings): assert "freeze" not in cons_dict -@pytest.mark.parametrize("constraint_settings", [ - pytest.param(("0, -1, -2", None), id="correct space list"), - pytest.param(("0 0 0 ", None), id="correct space list"), - pytest.param(("0-0-0", ConstraintError), id="incorrect hyphen list"), - pytest.param(("C, a, b", ConstraintError), id="none float position"), - pytest.param(("0,0,0", None), id="correct comma list"), - pytest.param(([0, 0, 0], None), id="correct list") - -]) +@pytest.mark.parametrize( + "constraint_settings", + [ + pytest.param(("0, -1, -2", None), id="correct space list"), + pytest.param(("0 0 0 ", None), id="correct space list"), + pytest.param(("0-0-0", ConstraintError), id="incorrect hyphen list"), + pytest.param(("C, a, b", ConstraintError), id="none float position"), + pytest.param(("0,0,0", None), id="correct comma list"), + pytest.param(([0, 0, 0], None), id="correct list"), + ], +) def test_position_set_constraint_validation(constraint_settings): """ Test each of the valid inputs for the position set constraint and make sure the value is converted correctly. """ value, error = constraint_settings if error is None: - constraint = PositionConstraintSet(indices=(0, ), value=value) + constraint = PositionConstraintSet(indices=(0,), value=value) # make sure a single string is made assert len(constraint.value.split()) == 3 else: with pytest.raises(error): - PositionConstraintSet(indices=(0, ), value=value) + PositionConstraintSet(indices=(0,), value=value) -@pytest.mark.parametrize("constraint, constraint_type, indices, value", [ - pytest.param("freeze", "dihedral", [0, 1, 2, 3], None, id="freeze dihedral valid"), - pytest.param("set", "dihedral", [0, 1, 2, 3], 50, id="set dihedral valid"), - pytest.param("scan", "dihedral", [0, 1, 2, 3], 50, id="invalid scan constraint"), -]) +@pytest.mark.parametrize( + "constraint, constraint_type, indices, value", + [ + pytest.param( + "freeze", "dihedral", [0, 1, 2, 3], None, id="freeze dihedral valid" + ), + pytest.param("set", "dihedral", [0, 1, 2, 3], 50, id="set dihedral valid"), + pytest.param( + "scan", "dihedral", [0, 1, 2, 3], 50, id="invalid scan constraint" + ), + ], +) def test_add_constraints_to_entry_errors(constraint, constraint_type, indices, value): """ Test adding new constraints to a valid dataset entry, make sure errors are raised if we say the constraint is bonded but it is not. """ mol = Molecule.from_smiles("CC") - entry = OptimizationEntry(off_molecule=mol, index=mol.to_smiles(), attributes=MoleculeAttributes.from_openff_molecule(mol), keywords={}, extras={}) + entry = OptimizationEntry( + off_molecule=mol, + index=mol.to_smiles(), + attributes=MoleculeAttributes.from_openff_molecule(mol), + keywords={}, + extras={}, + ) # get the constraint info with pytest.raises(ConstraintError): - entry.add_constraint(constraint=constraint, constraint_type=constraint_type, indices=indices, value=value) + entry.add_constraint( + constraint=constraint, + constraint_type=constraint_type, + indices=indices, + value=value, + ) -@pytest.mark.parametrize("constraint, constraint_type, indices, value", [ - pytest.param("freeze", "dihedral", [3, 0, 1, 5], None, id="freeze dihedral"), - pytest.param("set", "angle", [3, 0, 1], 50, id="set angle"), - pytest.param("freeze", "distance", [0, 1], None, id="freeze distance"), -]) +@pytest.mark.parametrize( + "constraint, constraint_type, indices, value", + [ + pytest.param("freeze", "dihedral", [3, 0, 1, 5], None, id="freeze dihedral"), + pytest.param("set", "angle", [3, 0, 1], 50, id="set angle"), + pytest.param("freeze", "distance", [0, 1], None, id="freeze distance"), + ], +) def test_add_constraints_to_entry(constraint, constraint_type, indices, value): """ Test adding new constraints to a valid dataset entry make sure no warnings are raised as the constraints are bonded. """ mol = Molecule.from_smiles("CC") - entry = OptimizationEntry(off_molecule=mol, index=mol.to_smiles(), attributes=MoleculeAttributes.from_openff_molecule(mol), keywords={}, extras={}) + entry = OptimizationEntry( + off_molecule=mol, + index=mol.to_smiles(), + attributes=MoleculeAttributes.from_openff_molecule(mol), + keywords={}, + extras={}, + ) # add the bonded constraint - entry.add_constraint(constraint=constraint, constraint_type=constraint_type, indices=indices, bonded=True, value=value) + entry.add_constraint( + constraint=constraint, + constraint_type=constraint_type, + indices=indices, + bonded=True, + value=value, + ) assert entry.constraints.has_constraints is True assert "constraints" in entry.formatted_keywords -@pytest.mark.parametrize("constraint_setting", [ - pytest.param("constraints", id="constraint"), - pytest.param("keywords", id="keywords"), - pytest.param(None, id="no constraints") -]) +@pytest.mark.parametrize( + "constraint_setting", + [ + pytest.param("constraints", id="constraint"), + pytest.param("keywords", id="keywords"), + pytest.param(None, id="no constraints"), + ], +) def test_add_entry_with_constraints(constraint_setting): """ Add an entry to a dataset with constraints in the keywords and make sure they converted to the constraints field. """ - dataset = OptimizationDataset(dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") + dataset = OptimizationDataset( + dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) # now add a molecule with constraints in the keywords mol = Molecule.from_file(get_data("ethane.sdf"), "sdf") - constraints = {"set": [{"type": "dihedral", "indices": [0, 1, 2, 3], "value": 50, "bonded": False}, - {"type": "angle", "indices": [0, 1, 2], "value": 50, "bonded": False}, - {"type": "distance", "indices": [1, 2], "value": 1, "bonded": False}]} + constraints = { + "set": [ + {"type": "dihedral", "indices": [0, 1, 2, 3], "value": 50, "bonded": False}, + {"type": "angle", "indices": [0, 1, 2], "value": 50, "bonded": False}, + {"type": "distance", "indices": [1, 2], "value": 1, "bonded": False}, + ] + } index = mol.to_smiles() if constraint_setting == "constraints": dataset.add_molecule(index=index, molecule=mol, constraints=constraints) elif constraint_setting == "keywords": - dataset.add_molecule(index=index, molecule=mol, keywords={"constraints": constraints}) + dataset.add_molecule( + index=index, molecule=mol, keywords={"constraints": constraints} + ) elif constraint_setting is None: dataset.add_molecule(index=index, molecule=mol) @@ -575,11 +761,23 @@ def test_add_entry_with_constraints(constraint_setting): assert entry.constraints.has_constraints is False -@pytest.mark.parametrize("atom_data", [ - pytest.param(([1, 2], check_bond_connection, BondConnectionError), id="Bond connection error"), - pytest.param(([0, 1, 2], check_angle_connection, AngleConnectionError), id="Angle connection error"), - pytest.param(([5, 0, 1, 2], check_torsion_connection, DihedralConnectionError), id="Dihedral connection error") -]) +@pytest.mark.parametrize( + "atom_data", + [ + pytest.param( + ([1, 2], check_bond_connection, BondConnectionError), + id="Bond connection error", + ), + pytest.param( + ([0, 1, 2], check_angle_connection, AngleConnectionError), + id="Angle connection error", + ), + pytest.param( + ([5, 0, 1, 2], check_torsion_connection, DihedralConnectionError), + id="Dihedral connection error", + ), + ], +) def test_check_connection_types_error(atom_data): """ Make sure the connection checks raise the correct errors. @@ -615,14 +813,16 @@ def test_constraints_are_equal(): @pytest.mark.parametrize( "keywords", - [None, {"keyword-1": True, "keyword-2": 1, "keyword-3": 1.5, "keyword-4": "1.5"}] + [None, {"keyword-1": True, "keyword-2": 1, "keyword-3": 1.5, "keyword-4": "1.5"}], ) def test_keywords_detected(keywords): """ Make sure unsupported scf properties are not allowed into a dataset. """ dataset = BasicDataset( - dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX", + dataset_name="Test dataset", + dataset_tagline="XXXXXXXX", + description="XXXXXXXX", ) dataset.add_qc_spec( "hf", "6-31G", "psi4", "XXXXXXXX", "XXXXXXXX", keywords=keywords @@ -634,7 +834,6 @@ def test_keywords_detected(keywords): portal_keywords = dataset._get_spec_keywords(qc_spec) for key, expected_value in {} if keywords is None else keywords.items(): - assert key in portal_keywords.values assert portal_keywords.values[key] == expected_value @@ -643,7 +842,9 @@ def test_add_molecule_no_extras(): """ Test that adding a molecule with no extras automatically updates the extras to include the cmiles and c1 symmetry. """ - dataset = BasicDataset(dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") + dataset = BasicDataset( + dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) mols = duplicated_molecules(include_conformers=True, duplicates=1) for mol in mols: index = mol.to_smiles() @@ -669,26 +870,52 @@ def test_add_molecule_from_entry_data(): entry = DatasetEntry( off_molecule=ethane, attributes=MoleculeAttributes.from_openff_molecule(ethane), - index="1" + index="1", ) dataset.add_molecule(molecule=None, **entry.dict()) -@pytest.mark.parametrize("dataset_data", [ - pytest.param((BasicDataset, "OpenFF Theory Benchmarking Single Point Energies v1.0", ["default"]), id="Dataset no metadata"), - pytest.param((OptimizationDataset, "OpenFF NCI250K Boron 1", ["default"]), id="OptimizationDataset no metadata"), - pytest.param((TorsiondriveDataset, "OpenFF Fragmenter Phenyl Benchmark", ["UFF", "B3LYP-D3"]), id="TorsiondriveDataset no metadata"), - pytest.param((TorsiondriveDataset, "OpenFF Rowley Biaryl v1.0", ["default"]), id="Torsiondrive with metadata") -]) +@pytest.mark.parametrize( + "dataset_data", + [ + pytest.param( + ( + BasicDataset, + "OpenFF Theory Benchmarking Single Point Energies v1.0", + ["default"], + ), + id="Dataset no metadata", + ), + pytest.param( + (OptimizationDataset, "OpenFF NCI250K Boron 1", ["default"]), + id="OptimizationDataset no metadata", + ), + pytest.param( + ( + TorsiondriveDataset, + "OpenFF Fragmenter Phenyl Benchmark", + ["UFF", "B3LYP-D3"], + ), + id="TorsiondriveDataset no metadata", + ), + pytest.param( + (TorsiondriveDataset, "OpenFF Rowley Biaryl v1.0", ["default"]), + id="Torsiondrive with metadata", + ), + ], +) def test_dataset_update(dataset_data): """ Make sure the utils function can update elements and pull the correct specs. """ import qcportal as ptl + dataset_type, dataset_name, specs = dataset_data client = ptl.FractalClient() # set up the dataset - dataset = dataset_type(dataset_name=dataset_name, dataset_tagline="XXXXXXXX", description="XXXXXXXX") + dataset = dataset_type( + dataset_name=dataset_name, dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) assert bool(dataset.metadata.elements) is False dataset = update_specification_and_metadata(dataset, client) # now make sure the elements have been updated and the spec added @@ -697,44 +924,66 @@ def test_dataset_update(dataset_data): assert spec in dataset.qc_specifications -@pytest.mark.parametrize("dataset_types", [ - pytest.param((BasicDataset, OptimizationDataset), id="basic + optimization error"), - pytest.param((OptimizationDataset, TorsiondriveDataset), id="optimization + torsiondrive error"), - pytest.param((TorsiondriveDataset, BasicDataset), id="torsiondrive + basic error"), - -]) +@pytest.mark.parametrize( + "dataset_types", + [ + pytest.param( + (BasicDataset, OptimizationDataset), id="basic + optimization error" + ), + pytest.param( + (OptimizationDataset, TorsiondriveDataset), + id="optimization + torsiondrive error", + ), + pytest.param( + (TorsiondriveDataset, BasicDataset), id="torsiondrive + basic error" + ), + ], +) def test_dataset_adding(dataset_types): """ Test combining two different datasets this should raise an error as they have different conditions to be considered equal. """ data_type1, data_type2 = dataset_types - dataset1, dataset2 = data_type1(dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX"), data_type2(dataset_name="Test dataset2", dataset_tagline="XXXXXXXX", description="XXXXXXXX") + dataset1, dataset2 = data_type1( + dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ), data_type2( + dataset_name="Test dataset2", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) with pytest.raises(DatasetCombinationError): _ = dataset1 + dataset2 -@pytest.mark.parametrize("dataset_type", [ - pytest.param(BasicDataset, id="BasicDataset"), - pytest.param(OptimizationDataset, id="Optimizationdataset"), -]) +@pytest.mark.parametrize( + "dataset_type", + [ + pytest.param(BasicDataset, id="BasicDataset"), + pytest.param(OptimizationDataset, id="Optimizationdataset"), + ], +) def test_basic_and_opt_dataset_addition(dataset_type): """ Test adding two basic and opt datasets together. Opt has no constraints and behaves like basic in this case. """ - dataset1 = dataset_type(dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") - dataset2 = dataset_type(dataset_name="Test dataset2", dataset_tagline="XXXXXXXX", description="XXXXXXXX") + dataset1 = dataset_type( + dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) + dataset2 = dataset_type( + dataset_name="Test dataset2", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) # get some butane conformers and split them between the datasets - molecules = Molecule.from_file(get_data("butane_conformers.pdb"), 'pdb') + molecules = Molecule.from_file(get_data("butane_conformers.pdb"), "pdb") butane1 = condense_molecules(molecules[:4]) butane2 = condense_molecules(molecules[4:]) - dataset1.add_molecule(index=butane1.to_smiles(), - molecule=butane1, - ) - dataset2.add_molecule(index=butane2.to_smiles(), - molecule=butane2, - ) + dataset1.add_molecule( + index=butane1.to_smiles(), + molecule=butane1, + ) + dataset2.add_molecule( + index=butane2.to_smiles(), + molecule=butane2, + ) # now check the number of molecules in each dataset assert dataset1.n_molecules == 1 assert dataset1.n_records == 4 @@ -748,9 +997,10 @@ def test_basic_and_opt_dataset_addition(dataset_type): # now add another molecule to dataset2 ethanol = Molecule.from_file(get_data("ethanol.sdf"), "sdf") - dataset2.add_molecule(index=ethanol.to_smiles(), - molecule=ethanol, - ) + dataset2.add_molecule( + index=ethanol.to_smiles(), + molecule=ethanol, + ) new_dataset = dataset1 + dataset2 assert "O" in new_dataset.metadata.elements @@ -762,21 +1012,27 @@ def test_optimization_dataset_addition_constraints_equal(): """ Test adding two optimizationdatasets with constraints. """ - dataset1 = OptimizationDataset(dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") - dataset2 = OptimizationDataset(dataset_name="Test dataset2", dataset_tagline="XXXXXXXX", description="XXXXXXXX") + dataset1 = OptimizationDataset( + dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) + dataset2 = OptimizationDataset( + dataset_name="Test dataset2", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) # add molecules to each dataset - molecules = Molecule.from_file(get_data("butane_conformers.pdb"), 'pdb') + molecules = Molecule.from_file(get_data("butane_conformers.pdb"), "pdb") butane1 = condense_molecules(molecules[:4]) butane2 = condense_molecules(molecules[4:]) constraints = Constraints() - constraints.add_set_constraint(constraint_type="dihedral", indices=[0, 1, 2, 3], value=60) + constraints.add_set_constraint( + constraint_type="dihedral", indices=[0, 1, 2, 3], value=60 + ) constraints.add_freeze_constraint(constraint_type="distance", indices=[0, 1]) - dataset1.add_molecule(index=butane1.to_smiles(), - molecule=butane1, - constraints=constraints) - dataset2.add_molecule(index=butane2.to_smiles(), - molecule=butane2, - constraints=constraints) + dataset1.add_molecule( + index=butane1.to_smiles(), molecule=butane1, constraints=constraints + ) + dataset2.add_molecule( + index=butane2.to_smiles(), molecule=butane2, constraints=constraints + ) # make sure the records are added assert dataset1.n_molecules == 1 assert dataset1.n_records == 4 @@ -800,24 +1056,30 @@ def test_optimization_dataset_addition_constraints_not_equall(): """ Test adding two optimizationdatasets with constraints that are not the same. """ - dataset1 = OptimizationDataset(dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") - dataset2 = OptimizationDataset(dataset_name="Test dataset2", dataset_tagline="XXXXXXXX", description="XXXXXXXX") + dataset1 = OptimizationDataset( + dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) + dataset2 = OptimizationDataset( + dataset_name="Test dataset2", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) # add molecules to each dataset - molecules = Molecule.from_file(get_data("butane_conformers.pdb"), 'pdb') + molecules = Molecule.from_file(get_data("butane_conformers.pdb"), "pdb") butane1 = condense_molecules(molecules[:4]) butane2 = condense_molecules(molecules[4:]) # make different constraints constraints1 = Constraints() constraints1.add_freeze_constraint(constraint_type="angle", indices=[0, 1, 2]) constraints2 = Constraints() - constraints2.add_set_constraint(constraint_type="distance", indices=[0, 1], value=1.5) - - dataset1.add_molecule(index=butane1.to_smiles(), - molecule=butane1, - constraints=constraints1) - dataset2.add_molecule(index=butane2.to_smiles(), - molecule=butane2, - constraints=constraints2) + constraints2.add_set_constraint( + constraint_type="distance", indices=[0, 1], value=1.5 + ) + + dataset1.add_molecule( + index=butane1.to_smiles(), molecule=butane1, constraints=constraints1 + ) + dataset2.add_molecule( + index=butane2.to_smiles(), molecule=butane2, constraints=constraints2 + ) # make sure the records are added assert dataset1.n_molecules == 1 assert dataset1.n_records == 4 @@ -845,22 +1107,27 @@ def test_torsiondrive_dataset_addition_same_dihedral(): Test adding together two different torsiondrive datasets. """ from openff.qcsubmit.factories import TorsiondriveDatasetFactory + factory = TorsiondriveDatasetFactory() - dataset1 = TorsiondriveDataset(dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") - dataset2 = TorsiondriveDataset(dataset_name="Test dataset2", dataset_tagline="XXXXXXXX", description="XXXXXXXX") - molecules = Molecule.from_file(get_data("butane_conformers.pdb"), 'pdb') + dataset1 = TorsiondriveDataset( + dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) + dataset2 = TorsiondriveDataset( + dataset_name="Test dataset2", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) + molecules = Molecule.from_file(get_data("butane_conformers.pdb"), "pdb") butane1 = condense_molecules(molecules[:4]) butane2 = condense_molecules(molecules[4:]) # select the same dihedral in both molecules dihedral = [(0, 1, 2, 3)] atom_map = {0: 0, 1: 1, 2: 2, 3: 3} butane1.properties["atom_map"] = atom_map - dataset1.add_molecule(index=factory.create_index(butane1), - molecule=butane1, - dihedrals=dihedral) - dataset2.add_molecule(index=factory.create_index(butane1), - molecule=butane2, - dihedrals=dihedral) + dataset1.add_molecule( + index=factory.create_index(butane1), molecule=butane1, dihedrals=dihedral + ) + dataset2.add_molecule( + index=factory.create_index(butane1), molecule=butane2, dihedrals=dihedral + ) assert dataset1.n_molecules == 1 assert dataset1.n_records == 1 assert dataset2.n_molecules == 1 @@ -879,10 +1146,15 @@ def test_torsiondrive_dataset_addition_different_dihedral(): Test adding together two different torsiondrive datasets. """ from openff.qcsubmit.factories import TorsiondriveDatasetFactory + factory = TorsiondriveDatasetFactory() - dataset1 = TorsiondriveDataset(dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") - dataset2 = TorsiondriveDataset(dataset_name="Test dataset2", dataset_tagline="XXXXXXXX", description="XXXXXXXX") - molecules = Molecule.from_file(get_data("butane_conformers.pdb"), 'pdb') + dataset1 = TorsiondriveDataset( + dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) + dataset2 = TorsiondriveDataset( + dataset_name="Test dataset2", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) + molecules = Molecule.from_file(get_data("butane_conformers.pdb"), "pdb") butane1 = condense_molecules(molecules[:4]) butane2 = condense_molecules(molecules[4:]) # select different dihedrals in the molecules @@ -899,12 +1171,8 @@ def test_torsiondrive_dataset_addition_different_dihedral(): index2 = factory.create_index(butane2) del butane2.properties["atom_map"] - dataset1.add_molecule(index=index1, - molecule=butane1, - dihedrals=dihedral1) - dataset2.add_molecule(index=index2, - molecule=butane2, - dihedrals=dihedral2) + dataset1.add_molecule(index=index1, molecule=butane1, dihedrals=dihedral1) + dataset2.add_molecule(index=index2, molecule=butane2, dihedrals=dihedral2) assert dataset1.n_molecules == 1 assert dataset1.n_records == 1 assert dataset2.n_molecules == 1 @@ -916,15 +1184,35 @@ def test_torsiondrive_dataset_addition_different_dihedral(): assert new_dataset.n_records == 2 -@pytest.mark.parametrize("dataset_data", [ - pytest.param(("valid_torsion_dataset.json", None), id="valid torsion dataset"), - pytest.param(("invalid_torsion_dataset.json", DihedralConnectionError), id="invalid torsion dataset"), - pytest.param(("valid_double_torsion_dataset.json", None), id="valid double torsion dataset"), - pytest.param(("invalid_double_torsion_dataset.json", DihedralConnectionError), id="invalid double torsion dataset"), - pytest.param(("valid_improper_dataset.json", None), id="valid improper dataset"), - pytest.param(("invalid_improper_dataset.json", DihedralConnectionError), id="invalid improper dataset"), - pytest.param(("invalid_linear_dataset.json", LinearTorsionError), id="invalid linear dataset"), -]) +@pytest.mark.parametrize( + "dataset_data", + [ + pytest.param(("valid_torsion_dataset.json", None), id="valid torsion dataset"), + pytest.param( + ("invalid_torsion_dataset.json", DihedralConnectionError), + id="invalid torsion dataset", + ), + pytest.param( + ("valid_double_torsion_dataset.json", None), + id="valid double torsion dataset", + ), + pytest.param( + ("invalid_double_torsion_dataset.json", DihedralConnectionError), + id="invalid double torsion dataset", + ), + pytest.param( + ("valid_improper_dataset.json", None), id="valid improper dataset" + ), + pytest.param( + ("invalid_improper_dataset.json", DihedralConnectionError), + id="invalid improper dataset", + ), + pytest.param( + ("invalid_linear_dataset.json", LinearTorsionError), + id="invalid linear dataset", + ), + ], +) def test_importing_dihedral_dataset(dataset_data): """ Make sure that the dataset validators run when importing datasets. @@ -944,14 +1232,19 @@ def test_componenetresult_deduplication_torsions_same_bond_different_coords(): Make sure that similar molecules with different coords but the same selected rotatable bonds are correctly handled. """ - result = ComponentResult(component_name="Test deduplication", component_description={}, - component_provenance={}) + result = ComponentResult( + component_name="Test deduplication", + component_description={}, + component_provenance={}, + ) - molecules = Molecule.from_file(get_data("butane_conformers.pdb"), 'pdb') + molecules = Molecule.from_file(get_data("butane_conformers.pdb"), "pdb") butane_dihedral = (0, 1, 2, 3) for molecule in molecules: torsion_indexer = TorsionIndexer() - torsion_indexer.add_torsion(torsion=butane_dihedral, scan_range=None, symmetry_group=(1, 1)) + torsion_indexer.add_torsion( + torsion=butane_dihedral, scan_range=None, symmetry_group=(1, 1) + ) molecule.properties["dihedrals"] = torsion_indexer result.add_molecule(molecule) @@ -968,8 +1261,11 @@ def test_componentresult_deduplication_torsions_1d(): Make sure that any torsion index results are correctly transferred when deduplicating molecules. """ - result = ComponentResult(component_name="Test deduplication", component_description={}, - component_provenance={}) + result = ComponentResult( + component_name="Test deduplication", + component_description={}, + component_provenance={}, + ) duplicates = 2 molecules = duplicated_molecules(include_conformers=False, duplicates=duplicates) @@ -992,13 +1288,21 @@ def test_componentresult_deduplication_torsions_2d(): Make sure that any torsion index results are correctly transferred when deduplicating molecules. """ - result = ComponentResult(component_name="Test deduplication", component_description={}, - component_provenance={}) + result = ComponentResult( + component_name="Test deduplication", + component_description={}, + component_provenance={}, + ) duplicates = 2 - molecules = duplicated_molecules(include_conformers=False, duplicates=duplicates)[:2] + molecules = duplicated_molecules(include_conformers=False, duplicates=duplicates)[ + :2 + ] dihedrals = [((0, 1, 2, 3), (-165, 150)), ((1, 2, 3, 4), (0, 180))] - double_dihedrals = [((0, 1, 2, 3), (1, 2, 3, 4), (-165, 150), (0, 180)), ((1, 2, 3, 4), (5, 6, 7, 8), (125, 150), (-10, 180))] + double_dihedrals = [ + ((0, 1, 2, 3), (1, 2, 3, 4), (-165, 150), (0, 180)), + ((1, 2, 3, 4), (5, 6, 7, 8), (125, 150), (-10, 180)), + ] for i, molecule in enumerate(molecules): torsion_indexer = TorsionIndexer() torsion_indexer.add_torsion(*dihedrals[i % 2]) @@ -1022,23 +1326,33 @@ def test_torsion_indexing_torsion(): torsion_indexer = TorsionIndexer() # add a 1-D torsion - torsion_indexer.add_torsion(torsion=(3, 2, 1, 0), scan_range=(180, -165), symmetry_group=(1, 1)) + torsion_indexer.add_torsion( + torsion=(3, 2, 1, 0), scan_range=(180, -165), symmetry_group=(1, 1) + ) # make sure they have been ordered assert (1, 2) in torsion_indexer.torsions single_torsion = torsion_indexer.torsions[(1, 2)] assert single_torsion.scan_range1 == (-165, 180) - assert single_torsion.get_dihedrals == [(0, 1, 2, 3), ] - assert single_torsion.get_scan_range == [(-165, 180), ] + assert single_torsion.get_dihedrals == [ + (0, 1, 2, 3), + ] + assert single_torsion.get_scan_range == [ + (-165, 180), + ] assert single_torsion.get_atom_map == {0: 0, 1: 1, 2: 2, 3: 3} assert torsion_indexer.n_torsions == 1 # test overwrite - torsion_indexer.add_torsion(torsion=(3, 2, 1, 0), scan_range=None, overwrite=True, symmetry_group=(1, 2)) + torsion_indexer.add_torsion( + torsion=(3, 2, 1, 0), scan_range=None, overwrite=True, symmetry_group=(1, 2) + ) assert torsion_indexer.n_torsions == 1 single_torsion = torsion_indexer.torsions[(1, 2)] assert single_torsion.get_scan_range is None # test symmetry deduplication - torsion_indexer.add_torsion(torsion=(4, 5, 6, 7), scan_range=None, symmetry_group=(1, 2)) + torsion_indexer.add_torsion( + torsion=(4, 5, 6, 7), scan_range=None, symmetry_group=(1, 2) + ) # the torsion should not be replaced as it is not symmetry unique assert torsion_indexer.n_torsions == 1 @@ -1050,8 +1364,14 @@ def test_torsion_indexing_double(): torsion_indexer = TorsionIndexer() # add a 2-D scan - torsion_indexer.add_double_torsion(torsion1=(9, 8, 7, 6), torsion2=(0, 1, 2, 3), scan_range1=[40, -40], - scan_range2=[-165, 180], symmetry_group2=(1, 2), symmetry_group1=(1, 1)) + torsion_indexer.add_double_torsion( + torsion1=(9, 8, 7, 6), + torsion2=(0, 1, 2, 3), + scan_range1=[40, -40], + scan_range2=[-165, 180], + symmetry_group2=(1, 2), + symmetry_group1=(1, 1), + ) # check the central bond was ordered assert ((1, 2), (7, 8)) in torsion_indexer.double_torsions double_torsion = torsion_indexer.double_torsions[((1, 2), (7, 8))] @@ -1059,12 +1379,28 @@ def test_torsion_indexing_double(): assert double_torsion.scan_range2 == (-165, 180) assert double_torsion.get_dihedrals == [(6, 7, 8, 9), (0, 1, 2, 3)] assert double_torsion.get_scan_range == [(-40, 40), (-165, 180)] - assert double_torsion.get_atom_map == {0: 4, 1: 5, 2: 6, 3: 7, 6: 0, 7: 1, 8: 2, 9: 3} + assert double_torsion.get_atom_map == { + 0: 4, + 1: 5, + 2: 6, + 3: 7, + 6: 0, + 7: 1, + 8: 2, + 9: 3, + } assert torsion_indexer.n_double_torsions == 1 # test overwrite - torsion_indexer.add_double_torsion(torsion1=(9, 8, 7, 6), torsion2=(0, 1, 2, 3), scan_range1=None, - scan_range2=None, overwrite=True, symmetry_group1=(1, 3), symmetry_group2=(2, 3)) + torsion_indexer.add_double_torsion( + torsion1=(9, 8, 7, 6), + torsion2=(0, 1, 2, 3), + scan_range1=None, + scan_range2=None, + overwrite=True, + symmetry_group1=(1, 3), + symmetry_group2=(2, 3), + ) assert torsion_indexer.n_double_torsions == 1 double_torsion = torsion_indexer.double_torsions[((1, 2), (7, 8))] assert double_torsion.get_scan_range is None @@ -1076,17 +1412,28 @@ def test_torsion_indexing_improper(): """ torsion_indexer = TorsionIndexer() - torsion_indexer.add_improper(central_atom=1, improper=(0, 1, 2, 3), scan_range=[40, -40], symmetry_group=(2, 1, 1, 1)) + torsion_indexer.add_improper( + central_atom=1, + improper=(0, 1, 2, 3), + scan_range=[40, -40], + symmetry_group=(2, 1, 1, 1), + ) assert 1 in torsion_indexer.impropers assert torsion_indexer.n_impropers == 1 improper = torsion_indexer.impropers[1] - assert improper.get_scan_range == [(-40, 40), ] - torsion_indexer.add_improper(1, (3, 2, 1, 0), scan_range=None, overwrite=True, symmetry_group=(2, 1, 1, 1)) + assert improper.get_scan_range == [ + (-40, 40), + ] + torsion_indexer.add_improper( + 1, (3, 2, 1, 0), scan_range=None, overwrite=True, symmetry_group=(2, 1, 1, 1) + ) # make sure it was over writen assert 1 in torsion_indexer.impropers assert torsion_indexer.n_impropers == 1 improper = torsion_indexer.impropers[1] - assert improper.get_dihedrals == [(3, 2, 1, 0), ] + assert improper.get_dihedrals == [ + (3, 2, 1, 0), + ] assert improper.get_scan_range is None assert improper.get_atom_map == {3: 0, 2: 1, 1: 2, 0: 3} @@ -1100,11 +1447,20 @@ def test_torsion_index_iterator(): ImproperTorsion, SingleTorsion, ) + torsion_indexer = TorsionIndexer() torsion_indexer.add_torsion((3, 2, 1, 0), (180, -165)) - torsion_indexer.add_double_torsion(torsion1=(9, 8, 7, 6), torsion2=(0, 1, 2, 3), scan_range1=[40, -40], - scan_range2=[-165, 180], symmetry_group1=(1, 1), symmetry_group2=(1, 1)) - torsion_indexer.add_improper(1, (0, 1, 2, 3), scan_range=[40, -40], symmetry_group=(2, 1, 1, 1)) + torsion_indexer.add_double_torsion( + torsion1=(9, 8, 7, 6), + torsion2=(0, 1, 2, 3), + scan_range1=[40, -40], + scan_range2=[-165, 180], + symmetry_group1=(1, 1), + symmetry_group2=(1, 1), + ) + torsion_indexer.add_improper( + 1, (0, 1, 2, 3), scan_range=[40, -40], symmetry_group=(2, 1, 1, 1) + ) assert torsion_indexer.n_torsions == 1 assert torsion_indexer.n_double_torsions == 1 assert torsion_indexer.n_impropers == 1 @@ -1122,16 +1478,30 @@ def test_torsion_indexer_update_no_mapping(): torsion_indexer1 = TorsionIndexer() torsion_indexer1.add_torsion(torsion=(0, 1, 2, 3), symmetry_group=(1, 1)) - torsion_indexer1.add_double_torsion(torsion1=(0, 1, 2, 3), torsion2=(9, 8, 7, 6), symmetry_group1=(1, 1), symmetry_group2=(1, 1)) - torsion_indexer1.add_improper(central_atom=1, improper=(0, 1, 2, 3), symmetry_group=(2, 1, 1, 1)) + torsion_indexer1.add_double_torsion( + torsion1=(0, 1, 2, 3), + torsion2=(9, 8, 7, 6), + symmetry_group1=(1, 1), + symmetry_group2=(1, 1), + ) + torsion_indexer1.add_improper( + central_atom=1, improper=(0, 1, 2, 3), symmetry_group=(2, 1, 1, 1) + ) assert torsion_indexer1.n_torsions == 1 assert torsion_indexer1.n_double_torsions == 1 assert torsion_indexer1.n_impropers == 1 torsion_indexer2 = TorsionIndexer() torsion_indexer2.add_torsion(torsion=(9, 8, 7, 6), symmetry_group=(1, 2)) - torsion_indexer2.add_double_torsion(torsion1=(9, 8, 7, 6), torsion2=(10, 11, 12, 13), symmetry_group1=(1, 2), symmetry_group2=(1, 1)) - torsion_indexer2.add_improper(central_atom=5, improper=(5, 6, 7, 8), symmetry_group=(3, 1, 1, 1)) + torsion_indexer2.add_double_torsion( + torsion1=(9, 8, 7, 6), + torsion2=(10, 11, 12, 13), + symmetry_group1=(1, 2), + symmetry_group2=(1, 1), + ) + torsion_indexer2.add_improper( + central_atom=5, improper=(5, 6, 7, 8), symmetry_group=(3, 1, 1, 1) + ) assert torsion_indexer2.n_torsions == 1 assert torsion_indexer2.n_double_torsions == 1 assert torsion_indexer2.n_impropers == 1 @@ -1178,33 +1548,45 @@ def test_dataset_add_filter_molecules(): """ Test adding extra molecules to filter. """ - dataset = BasicDataset(dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") + dataset = BasicDataset( + dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) methane = Molecule.from_smiles("C") ethanol = Molecule.from_smiles("CC") - dataset.filter_molecules(molecules=methane, - component="test", - component_settings={}, - component_provenance={}) + dataset.filter_molecules( + molecules=methane, + component="test", + component_settings={}, + component_provenance={}, + ) # now we want to add this extra molecule to this group - dataset.filter_molecules(molecules=ethanol, - component="test", - component_settings={}, - component_provenance={}) + dataset.filter_molecules( + molecules=ethanol, + component="test", + component_settings={}, + component_provenance={}, + ) filtered = dataset.filtered_molecules["test"] assert len(filtered.molecules) == 2 -@pytest.mark.parametrize("dataset_type", [ - pytest.param(BasicDataset, id="BasicDataset"), pytest.param(OptimizationDataset, id="OptimizationDataset"), - pytest.param(TorsiondriveDataset, id="TorsiondriveDataset") -]) +@pytest.mark.parametrize( + "dataset_type", + [ + pytest.param(BasicDataset, id="BasicDataset"), + pytest.param(OptimizationDataset, id="OptimizationDataset"), + pytest.param(TorsiondriveDataset, id="TorsiondriveDataset"), + ], +) def test_dataset_metadata(dataset_type): """ Test that the metadata for each dataset type s correctly assigned. """ # make a basic dataset - dataset = dataset_type(dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") + dataset = dataset_type( + dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) dataset.metadata.short_description = None # check the metadata @@ -1217,39 +1599,57 @@ def test_dataset_metadata(dataset_type): assert dataset.metadata.collection_type == dataset.type -@pytest.mark.parametrize("dataset_type", [ - pytest.param(BasicDataset, id="BasicDataset"), pytest.param(OptimizationDataset, id="OptimizationDataset"), - pytest.param(TorsiondriveDataset, id="TorsiondriveDataset") -]) +@pytest.mark.parametrize( + "dataset_type", + [ + pytest.param(BasicDataset, id="BasicDataset"), + pytest.param(OptimizationDataset, id="OptimizationDataset"), + pytest.param(TorsiondriveDataset, id="TorsiondriveDataset"), + ], +) def test_wrong_metadata_collection_type(dataset_type): """ Test passing in the wrong collection type into the metadata this should be corrected during the init. """ from openff.qcsubmit.common_structures import Metadata + meta = Metadata(collection_type="INVALID") - dataset = dataset_type(metadata=meta, dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") + dataset = dataset_type( + metadata=meta, + dataset_name="Test dataset", + dataset_tagline="XXXXXXXX", + description="XXXXXXXX", + ) # make sure the init of the dataset corrects the collection type assert dataset.metadata.collection_type != "INVALID" assert dataset.metadata.collection_type == dataset.type -@pytest.mark.parametrize("dataset_type", [ - pytest.param(BasicDataset, id="BasicDataset"), - pytest.param(OptimizationDataset, id="OptimizationDataset"), - pytest.param(TorsiondriveDataset, id="TorsiondriveDataset") -]) +@pytest.mark.parametrize( + "dataset_type", + [ + pytest.param(BasicDataset, id="BasicDataset"), + pytest.param(OptimizationDataset, id="OptimizationDataset"), + pytest.param(TorsiondriveDataset, id="TorsiondriveDataset"), + ], +) def test_dataset_exporting_same_type(dataset_type): """ Test making the given dataset from the json of another instance of the same dataset type. """ with temp_directory(): - dataset = dataset_type(compute_tag="test tag", dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") - dataset.export_dataset('dataset.json') + dataset = dataset_type( + compute_tag="test tag", + dataset_name="Test dataset", + dataset_tagline="XXXXXXXX", + description="XXXXXXXX", + ) + dataset.export_dataset("dataset.json") - dataset2 = dataset_type.parse_file('dataset.json') + dataset2 = dataset_type.parse_file("dataset.json") assert dataset2.compute_tag == "test tag" assert dataset.metadata == dataset2.metadata @@ -1259,21 +1659,30 @@ def test_dataset_unsupported_exports(): Test trying to export to unsupported file types. """ with temp_directory(): - dataset = BasicDataset(dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") + dataset = BasicDataset( + dataset_name="Test dataset", + dataset_tagline="XXXXXXXX", + description="XXXXXXXX", + ) with pytest.raises(UnsupportedFiletypeError): dataset.export_dataset("dataset.yaml") -@pytest.mark.parametrize("molecule_data", [ - pytest.param((Molecule.from_smiles("CC"), 0), id="Molecule 0 entries"), - pytest.param(("CC", 0), id="Smiles 0 entries"), - pytest.param(("CCC", 1), id="Smiles 1 entries"), -]) +@pytest.mark.parametrize( + "molecule_data", + [ + pytest.param((Molecule.from_smiles("CC"), 0), id="Molecule 0 entries"), + pytest.param(("CC", 0), id="Smiles 0 entries"), + pytest.param(("CCC", 1), id="Smiles 1 entries"), + ], +) def test_get_molecule_entry(molecule_data): """ Test getting a molecule entry from dataset. """ - dataset = BasicDataset(dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") + dataset = BasicDataset( + dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) query_molecule, entries_no = molecule_data molecules = duplicated_molecules(include_conformers=True, duplicates=1) for molecule in molecules: @@ -1289,7 +1698,9 @@ def test_get_entry_molecule(): """ Test getting a molecule with and without conformers from a dataset. """ - dataset = BasicDataset(dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") + dataset = BasicDataset( + dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) molecules = duplicated_molecules(include_conformers=True, duplicates=1) for molecule in molecules: index = molecule.to_smiles() @@ -1309,7 +1720,9 @@ def test_dataset_nmolecules_tautomers(): We use inchikey to find the unique molecules however some tautomers can have the same inchikey so make sure we can still find the unique molecules by forceing the inchi_key to be the same. """ - dataset = BasicDataset(dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") + dataset = BasicDataset( + dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) molecules = duplicated_molecules(include_conformers=True, duplicates=1) for molecule in molecules: index = molecule.to_smiles() @@ -1323,7 +1736,9 @@ def test_basicdataset_add_molecules_single_conformer(): Test creating a basic dataset. """ - dataset = BasicDataset(dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") + dataset = BasicDataset( + dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) # get some molecules molecules = duplicated_molecules(include_conformers=True, duplicates=1) # store the molecules in the dataset under a common index @@ -1345,9 +1760,11 @@ def test_basicdataset_add_molecules_conformers(): Test adding a molecule with conformers which should each be expanded into their own qcportal.models.Molecule. """ - dataset = BasicDataset(dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") + dataset = BasicDataset( + dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) # create a molecule with multipule conformers - molecules = Molecule.from_file(get_data('butane_conformers.pdb')) + molecules = Molecule.from_file(get_data("butane_conformers.pdb")) # collapse the conformers down butane = molecules.pop(0) for conformer in molecules: @@ -1364,9 +1781,8 @@ def test_basicdataset_add_molecules_conformers(): for mol in dataset.molecules: assert butane.is_isomorphic_with(mol) for i in range(butane.n_conformers): - assert ( - mol.conformers[i].m_as(unit.angstrom) - == pytest.approx(butane.conformers[i].m_as(unit.angstrom)) + assert mol.conformers[i].m_as(unit.angstrom) == pytest.approx( + butane.conformers[i].m_as(unit.angstrom) ) @@ -1375,7 +1791,9 @@ def test_basic_dataset_coverage_reporter(): Test generating coverage reports for openforcefield force fields. """ - dataset = BasicDataset(dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") + dataset = BasicDataset( + dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) # get some molecules molecules = duplicated_molecules(include_conformers=True, duplicates=1) # add them to the dataset @@ -1395,8 +1813,10 @@ def test_basicdataset_add_molecule_no_conformer(): Test adding molecules with no conformers which should cause the validtor to generate one. """ - dataset = BasicDataset(dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") - ethane = Molecule.from_smiles('CC') + dataset = BasicDataset( + dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) + ethane = Molecule.from_smiles("CC") # add the molecule to the dataset with no conformer index = ethane.to_smiles() dataset.add_molecule(index=index, molecule=ethane) @@ -1406,17 +1826,33 @@ def test_basicdataset_add_molecule_no_conformer(): assert molecule.n_conformers != 0 -@pytest.mark.parametrize("file_data", [ - pytest.param(("molecules.smi", "SMI", "to_smiles", {"isomeric": True, "explicit_hydrogens": False}, False), id="smiles"), - pytest.param(("molecules.inchi", "INCHI", "to_inchi", {}, False), id="inchi"), - pytest.param(("molecules.inchikey", "inchikey", "to_inchikey", {}, False), id="inchikey"), - pytest.param(("molecules.hash", "hash", "to_hash", {}, True), id="hash error") -]) +@pytest.mark.parametrize( + "file_data", + [ + pytest.param( + ( + "molecules.smi", + "SMI", + "to_smiles", + {"isomeric": True, "explicit_hydrogens": False}, + False, + ), + id="smiles", + ), + pytest.param(("molecules.inchi", "INCHI", "to_inchi", {}, False), id="inchi"), + pytest.param( + ("molecules.inchikey", "inchikey", "to_inchikey", {}, False), id="inchikey" + ), + pytest.param(("molecules.hash", "hash", "to_hash", {}, True), id="hash error"), + ], +) def test_basicdataset_molecules_to_file(file_data): """ Test exporting only the molecules in a dataset to file for each of the supported types. """ - dataset = BasicDataset(dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") + dataset = BasicDataset( + dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) molecules = duplicated_molecules(include_conformers=True, duplicates=1) # add them to the dataset for molecule in molecules: @@ -1437,20 +1873,27 @@ def test_basicdataset_molecules_to_file(file_data): assert data[i].strip() == result else: with pytest.raises(UnsupportedFiletypeError): - dataset.molecules_to_file(file_name=file_data[0], file_type=file_data[1]) + dataset.molecules_to_file( + file_name=file_data[0], file_type=file_data[1] + ) -@pytest.mark.parametrize("toolkit_data", [ - pytest.param(("openeye", None), id="openeye no highlights"), - pytest.param(("rdkit", None), id="rdkit, no highlights"), - pytest.param((None, None), id="Openeye by default."), - pytest.param(("bad_toolkit", ValueError), id="Bad toolkit name.") -]) +@pytest.mark.parametrize( + "toolkit_data", + [ + pytest.param(("openeye", None), id="openeye no highlights"), + pytest.param(("rdkit", None), id="rdkit, no highlights"), + pytest.param((None, None), id="Openeye by default."), + pytest.param(("bad_toolkit", ValueError), id="Bad toolkit name."), + ], +) def test_dataset_to_pdf_no_torsions(toolkit_data): """ Test exporting molecules to pdf with no torsions. """ - dataset = BasicDataset(dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") + dataset = BasicDataset( + dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) molecules = duplicated_molecules(include_conformers=True, duplicates=1) # add them to the dataset toolkit, error = toolkit_data @@ -1467,15 +1910,20 @@ def test_dataset_to_pdf_no_torsions(toolkit_data): dataset.visualize(file_name="molecules.pdf", toolkit=toolkit) -@pytest.mark.parametrize("toolkit", [ - pytest.param("openeye", id="openeye"), - pytest.param("rdkit", id="rdkit"), -]) +@pytest.mark.parametrize( + "toolkit", + [ + pytest.param("openeye", id="openeye"), + pytest.param("rdkit", id="rdkit"), + ], +) def test_dataset_to_pdf_with_torsions(toolkit): """ Test exporting molecules to PDF with torsions. """ - dataset = TorsiondriveDataset(dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") + dataset = TorsiondriveDataset( + dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) molecules = duplicated_molecules(include_conformers=True, duplicates=1) for molecule in molecules: index = molecule.to_smiles() @@ -1488,16 +1936,32 @@ def test_dataset_to_pdf_with_torsions(toolkit): dataset.visualize(file_name="molecules.pdf", toolkit=toolkit) -@pytest.mark.parametrize("dataset_type", [ - pytest.param(BasicDataset, id="BasicDataset"), pytest.param(OptimizationDataset, id="OptimizationDataset"), - pytest.param(TorsiondriveDataset, id="TorsiondriveDataset") -]) +@pytest.mark.parametrize( + "dataset_type", + [ + pytest.param(BasicDataset, id="BasicDataset"), + pytest.param(OptimizationDataset, id="OptimizationDataset"), + pytest.param(TorsiondriveDataset, id="TorsiondriveDataset"), + ], +) def test_dataset_export_full_dataset_json(dataset_type): """ Test round tripping a full dataset via json. """ - spec = QCSpec(method="hf", basis="6-31G", program="psi4", spec_name="default", spec_description="testing", keywords={"PERTURB_DIPOLE": [0.1, 0.1, -0.2]}) - dataset = dataset_type(dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX", qc_specifications={"default": spec}) + spec = QCSpec( + method="hf", + basis="6-31G", + program="psi4", + spec_name="default", + spec_description="testing", + keywords={"PERTURB_DIPOLE": [0.1, 0.1, -0.2]}, + ) + dataset = dataset_type( + dataset_name="Test dataset", + dataset_tagline="XXXXXXXX", + description="XXXXXXXX", + qc_specifications={"default": spec}, + ) molecules = duplicated_molecules(include_conformers=True, duplicates=1) # add them to the dataset for molecule in molecules: @@ -1505,7 +1969,9 @@ def test_dataset_export_full_dataset_json(dataset_type): try: dataset.add_molecule(index=index, molecule=molecule) except ValidationError: - dihedrals = [get_dihedral(molecule), ] + dihedrals = [ + get_dihedral(molecule), + ] dataset.add_molecule(index=index, molecule=molecule, dihedrals=dihedrals) with temp_directory(): dataset.export_dataset("dataset.json") @@ -1517,26 +1983,48 @@ def test_dataset_export_full_dataset_json(dataset_type): assert dataset.dataset == dataset.dataset assert dataset.metadata == dataset2.metadata # make sure the list survives a round trip - assert type(dataset2.qc_specifications["default"].keywords["PERTURB_DIPOLE"]) == list + assert ( + type(dataset2.qc_specifications["default"].keywords["PERTURB_DIPOLE"]) + == list + ) -@pytest.mark.parametrize("dataset_type", [ - pytest.param((BasicDataset, OptimizationDataset), id="BasicDataset to OptimizationDataset"), - pytest.param((OptimizationDataset, BasicDataset), id="OptimizationDataset to BasicDataSet"), - pytest.param((BasicDataset, TorsiondriveDataset), id="BasicDataSet to TorsiondriveDataset"), - pytest.param((OptimizationDataset, TorsiondriveDataset), id="OptimizationDataset to TorsiondriveDataset"), -]) +@pytest.mark.parametrize( + "dataset_type", + [ + pytest.param( + (BasicDataset, OptimizationDataset), + id="BasicDataset to OptimizationDataset", + ), + pytest.param( + (OptimizationDataset, BasicDataset), + id="OptimizationDataset to BasicDataSet", + ), + pytest.param( + (BasicDataset, TorsiondriveDataset), + id="BasicDataSet to TorsiondriveDataset", + ), + pytest.param( + (OptimizationDataset, TorsiondriveDataset), + id="OptimizationDataset to TorsiondriveDataset", + ), + ], +) def test_dataset_export_full_dataset_json_mixing(dataset_type): """ Test round tripping a full dataset via json from one type to another this should fail as the dataset_types do not match. """ - dataset = dataset_type[0](dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") + dataset = dataset_type[0]( + dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) molecules = duplicated_molecules(include_conformers=True, duplicates=1) # add them to the dataset for molecule in molecules: index = molecule.to_smiles() - dihedrals = [get_dihedral(molecule), ] + dihedrals = [ + get_dihedral(molecule), + ] dataset.add_molecule(index=index, molecule=molecule, dihedrals=dihedrals) with temp_directory(): dataset.export_dataset("dataset.json") @@ -1545,28 +2033,40 @@ def test_dataset_export_full_dataset_json_mixing(dataset_type): _ = dataset_type[1].parse_file("dataset.json") -@pytest.mark.parametrize("dataset_type", [ - pytest.param(BasicDataset, id="BasicDataset"), pytest.param(OptimizationDataset, id="OptimizationDataset"), - pytest.param(TorsiondriveDataset, id="TorsiondriveDataset") -]) +@pytest.mark.parametrize( + "dataset_type", + [ + pytest.param(BasicDataset, id="BasicDataset"), + pytest.param(OptimizationDataset, id="OptimizationDataset"), + pytest.param(TorsiondriveDataset, id="TorsiondriveDataset"), + ], +) def test_dataset_export_dict(dataset_type): """ Test making a new dataset from the dict of another of the same type. """ - dataset = dataset_type(dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") + dataset = dataset_type( + dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) molecules = duplicated_molecules(include_conformers=True, duplicates=1) # add them to the dataset for molecule in molecules: index = molecule.to_smiles() - dihedrals = [get_dihedral(molecule), ] + dihedrals = [ + get_dihedral(molecule), + ] dataset.add_molecule(index=index, molecule=molecule, dihedrals=dihedrals) # add one failure fail = Molecule.from_smiles("C") - dataset.filter_molecules(molecules=[fail, ], - component="TestFailure", - component_settings={}, - component_provenance={"test": "v1.0"}) + dataset.filter_molecules( + molecules=[ + fail, + ], + component="TestFailure", + component_settings={}, + component_provenance={"test": "v1.0"}, + ) dataset2 = dataset_type(**dataset.dict()) @@ -1577,29 +2077,40 @@ def test_dataset_export_dict(dataset_type): assert record in dataset2.dataset -@pytest.mark.parametrize("dataset_type", [ - pytest.param(BasicDataset, id="BasicDataset"), - pytest.param(OptimizationDataset, id="OptimizationDataset"), - pytest.param(TorsiondriveDataset, id="TorsiondriveDataset") -]) +@pytest.mark.parametrize( + "dataset_type", + [ + pytest.param(BasicDataset, id="BasicDataset"), + pytest.param(OptimizationDataset, id="OptimizationDataset"), + pytest.param(TorsiondriveDataset, id="TorsiondriveDataset"), + ], +) def test_dataset_export_json(dataset_type): """ Test that the json serialisation works. """ - dataset = dataset_type(dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") + dataset = dataset_type( + dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) molecules = duplicated_molecules(include_conformers=True, duplicates=1) # add them to the dataset for molecule in molecules: index = molecule.to_smiles() - dihedrals = [get_dihedral(molecule), ] + dihedrals = [ + get_dihedral(molecule), + ] dataset.add_molecule(index=index, molecule=molecule, dihedrals=dihedrals) # add one failure fail = Molecule.from_smiles("C") - dataset.filter_molecules(molecules=[fail, ], - component="TestFailure", - component_settings={}, - component_provenance={"test": "v1.0"}) + dataset.filter_molecules( + molecules=[ + fail, + ], + component="TestFailure", + component_settings={}, + component_provenance={"test": "v1.0"}, + ) # try parse the json string to build the dataset dataset2 = dataset_type.parse_raw(dataset.json()) @@ -1609,35 +2120,49 @@ def test_dataset_export_json(dataset_type): assert record in dataset2.dataset -@pytest.mark.parametrize("dataset_type", [ - pytest.param(BasicDataset, id="BasicDataset"), - pytest.param(OptimizationDataset, id="OptimizationDataset"), - pytest.param(TorsiondriveDataset, id="TorsiondriveDataset") -]) -@pytest.mark.parametrize("compression", [ - pytest.param("xz", id="lzma"), - pytest.param("bz2", id="bz2"), - pytest.param("gz", id="gz"), - pytest.param("", id="No compression") -]) +@pytest.mark.parametrize( + "dataset_type", + [ + pytest.param(BasicDataset, id="BasicDataset"), + pytest.param(OptimizationDataset, id="OptimizationDataset"), + pytest.param(TorsiondriveDataset, id="TorsiondriveDataset"), + ], +) +@pytest.mark.parametrize( + "compression", + [ + pytest.param("xz", id="lzma"), + pytest.param("bz2", id="bz2"), + pytest.param("gz", id="gz"), + pytest.param("", id="No compression"), + ], +) def test_dataset_roundtrip_compression(dataset_type, compression): """ Test that the json serialisation works. """ - dataset = dataset_type(dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") + dataset = dataset_type( + dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) molecules = duplicated_molecules(include_conformers=True, duplicates=1) # add them to the dataset for molecule in molecules: index = molecule.to_smiles() - dihedrals = [get_dihedral(molecule), ] + dihedrals = [ + get_dihedral(molecule), + ] dataset.add_molecule(index=index, molecule=molecule, dihedrals=dihedrals) # add one failure fail = Molecule.from_smiles("C") - dataset.filter_molecules(molecules=[fail, ], - component="TestFailure", - component_settings={}, - component_provenance={"test": "v1.0"}) + dataset.filter_molecules( + molecules=[ + fail, + ], + component="TestFailure", + component_settings={}, + component_provenance={"test": "v1.0"}, + ) with temp_directory(): # export the dataset with compression @@ -1648,41 +2173,146 @@ def test_dataset_roundtrip_compression(dataset_type, compression): assert record in dataset2.dataset -@pytest.mark.parametrize("basis_data", [ - pytest.param(({"method": "ani1x", "basis": None, "program": "torchani"}, {"P"}, True), id="Ani1x with Error"), - pytest.param(({"method": "ani1ccx", "basis": None, "program": "torchani"}, {"C", "H", "N"}, False), id="Ani1ccx Pass"), - pytest.param(({"method": "b3lyp-d3bj", "basis": "dzvp", "program": "psi4"}, {"C", "H", "O"}, False), id="DZVP psi4 convert Pass"), - pytest.param(({"method": "hf", "basis": "6-311++G", "program": "psi4"}, {"Br", "C", "O", "N"}, True), id="6-311++G Error"), - pytest.param(({"method": "hf", "basis": "def2-qzvp", "program": "psi4"}, {"H", "C", "B", "N", "O", "F", "Cl", "Si", "P", "S", "I", "Br"}, False), id="Def2-QZVP Pass"), - pytest.param(({"method": "wb97x-d", "basis": "aug-cc-pV(5+d)Z", "program": "psi4"}, {"I", "C", "H"}, True), id="aug-cc-pV(5+d)Z Error"), - pytest.param(({"method": "openff-1.0.0", "basis": "smirnoff", "program": "openmm"}, {"C", "H", "N"}, False), id="Smirnoff Pass"), - pytest.param(({"method": "GFN2-xTB", "basis": None, "program": "xtb"}, {"C", "N", "Se"}, False), id="XTB Pass"), - pytest.param(({"method": "uff", "basis": None, "program": "rdkit"}, {"C", "N", "O"}, False), id="Rdkit UFF Pass"), - pytest.param(({"method": "hf", "basis": "6-311++G**", "program": "psi4"}, {"Br", "C", "O", "N"}, True), id="6-311++G** regex sub Error"), - pytest.param(({"method": "hf", "basis": "cc-pV5Z(fi/sf/fw)", "program": "psi4"}, {"Br", "C", "O", "N"}, False), id="cc-pV5Z(fi/sf/fw) regex Pass"), - pytest.param(({"method": "hf", "basis": "heavy-aug-cc-pvtz", "program": "psi4"}, {"Br", "C", "O", "N"}, False), - id="heavy-aug-cc-pvtz heavy regex Pass"), - pytest.param(({"method": "hf", "basis": "apr-cc-pV(5+d)Z", "program": "psi4"}, {"Br", "C", "O", "N"}, False), - id="apr-cc-pV(5+d)Z regex Pass"), - pytest.param(({"method": "hf", "basis": "jun-cc-pV(Q+d)Z", "program": "psi4"}, {"Cl", "C", "O", "N", "Si"}, False), - id="jun-cc-pV(Q+d)Z Pass no regex"), - -]) +@pytest.mark.parametrize( + "basis_data", + [ + pytest.param( + ({"method": "ani1x", "basis": None, "program": "torchani"}, {"P"}, True), + id="Ani1x with Error", + ), + pytest.param( + ( + {"method": "ani1ccx", "basis": None, "program": "torchani"}, + {"C", "H", "N"}, + False, + ), + id="Ani1ccx Pass", + ), + pytest.param( + ( + {"method": "b3lyp-d3bj", "basis": "dzvp", "program": "psi4"}, + {"C", "H", "O"}, + False, + ), + id="DZVP psi4 convert Pass", + ), + pytest.param( + ( + {"method": "hf", "basis": "6-311++G", "program": "psi4"}, + {"Br", "C", "O", "N"}, + True, + ), + id="6-311++G Error", + ), + pytest.param( + ( + {"method": "hf", "basis": "def2-qzvp", "program": "psi4"}, + {"H", "C", "B", "N", "O", "F", "Cl", "Si", "P", "S", "I", "Br"}, + False, + ), + id="Def2-QZVP Pass", + ), + pytest.param( + ( + {"method": "wb97x-d", "basis": "aug-cc-pV(5+d)Z", "program": "psi4"}, + {"I", "C", "H"}, + True, + ), + id="aug-cc-pV(5+d)Z Error", + ), + pytest.param( + ( + {"method": "openff-1.0.0", "basis": "smirnoff", "program": "openmm"}, + {"C", "H", "N"}, + False, + ), + id="Smirnoff Pass", + ), + pytest.param( + ( + {"method": "GFN2-xTB", "basis": None, "program": "xtb"}, + {"C", "N", "Se"}, + False, + ), + id="XTB Pass", + ), + pytest.param( + ( + {"method": "uff", "basis": None, "program": "rdkit"}, + {"C", "N", "O"}, + False, + ), + id="Rdkit UFF Pass", + ), + pytest.param( + ( + {"method": "hf", "basis": "6-311++G**", "program": "psi4"}, + {"Br", "C", "O", "N"}, + True, + ), + id="6-311++G** regex sub Error", + ), + pytest.param( + ( + {"method": "hf", "basis": "cc-pV5Z(fi/sf/fw)", "program": "psi4"}, + {"Br", "C", "O", "N"}, + False, + ), + id="cc-pV5Z(fi/sf/fw) regex Pass", + ), + pytest.param( + ( + {"method": "hf", "basis": "heavy-aug-cc-pvtz", "program": "psi4"}, + {"Br", "C", "O", "N"}, + False, + ), + id="heavy-aug-cc-pvtz heavy regex Pass", + ), + pytest.param( + ( + {"method": "hf", "basis": "apr-cc-pV(5+d)Z", "program": "psi4"}, + {"Br", "C", "O", "N"}, + False, + ), + id="apr-cc-pV(5+d)Z regex Pass", + ), + pytest.param( + ( + {"method": "hf", "basis": "jun-cc-pV(Q+d)Z", "program": "psi4"}, + {"Cl", "C", "O", "N", "Si"}, + False, + ), + id="jun-cc-pV(Q+d)Z Pass no regex", + ), + ], +) def test_basis_coverage_single(basis_data): """ Make sure that the datasets can work out if the elements in the basis are covered. """ qc_spec, elements, error = basis_data - dataset = BasicDataset(metadata={"elements": elements}, dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") - dataset.add_qc_spec(**qc_spec, spec_name="default", spec_description="testing the basis", overwrite=True) + dataset = BasicDataset( + metadata={"elements": elements}, + dataset_name="Test dataset", + dataset_tagline="XXXXXXXX", + description="XXXXXXXX", + ) + dataset.add_qc_spec( + **qc_spec, + spec_name="default", + spec_description="testing the basis", + overwrite=True, + ) if error: with pytest.raises(MissingBasisCoverageError): dataset._get_missing_basis_coverage(raise_errors=error) else: - - assert bool(dataset._get_missing_basis_coverage(raise_errors=error)["default"]) is False + assert ( + bool(dataset._get_missing_basis_coverage(raise_errors=error)["default"]) + is False + ) def test_basis_coverage_multiple(): @@ -1691,18 +2321,25 @@ def test_basis_coverage_multiple(): """ # make a dataset covered by xtb, psi4, rdkit, smirnoff, but not ani - dataset = BasicDataset(metadata={"elements": {"I", "C", "H", "N", "O"}}, dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") + dataset = BasicDataset( + metadata={"elements": {"I", "C", "H", "N", "O"}}, + dataset_name="Test dataset", + dataset_tagline="XXXXXXXX", + description="XXXXXXXX", + ) specs = [ {"method": "hf", "basis": "DZVP", "program": "psi4"}, {"method": "gfn2xtb", "basis": None, "program": "xtb"}, {"method": "openff-1.0.0", "basis": "smirnoff", "program": "openmm"}, {"method": "uff", "basis": None, "program": "rdkit"}, - {"method": "ani1ccx", "basis": None, "program": "torchani"} + {"method": "ani1ccx", "basis": None, "program": "torchani"}, ] # clear out all qcspecs dataset.clear_qcspecs() for spec in specs: - dataset.add_qc_spec(**spec, spec_name=spec["program"], spec_description="testing basis report") + dataset.add_qc_spec( + **spec, spec_name=spec["program"], spec_description="testing basis report" + ) # get the coverage basis_coverage = dataset._get_missing_basis_coverage(raise_errors=False) @@ -1718,10 +2355,20 @@ def test_basis_coberage_qm_none(): """Test the basis coverage check with the basis is None for Psi4. Here the basis has been combinded with the method and a warning should be raised that it is not checked. """ - dataset = BasicDataset(metadata={"elements": {"I", "C", "H", "N", "O"}}, dataset_name="Test dataset", - dataset_tagline="XXXXXXXX", description="XXXXXXXX") + dataset = BasicDataset( + metadata={"elements": {"I", "C", "H", "N", "O"}}, + dataset_name="Test dataset", + dataset_tagline="XXXXXXXX", + description="XXXXXXXX", + ) dataset.clear_qcspecs() - dataset.add_qc_spec(method="mp2/cc-pv[tq]z + D:ccsd(t)/cc-pvdz", basis=None, spec_name="cbs test", program="psi4", spec_description="testing the cbs spec") + dataset.add_qc_spec( + method="mp2/cc-pv[tq]z + D:ccsd(t)/cc-pvdz", + basis=None, + spec_name="cbs test", + program="psi4", + spec_description="testing the cbs spec", + ) with pytest.warns(UserWarning): dataset._get_missing_basis_coverage() @@ -1731,41 +2378,70 @@ def test_remove_qcspec(): """ Test removing qcspecs. """ - dataset = BasicDataset(dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") + dataset = BasicDataset( + dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) dataset.remove_qcspec("default") assert dataset.qc_specifications == {} -@pytest.mark.parametrize("qc_spec", [ - pytest.param(({"method": "gfn1xtb", "basis": "bad", "program": "xtb"}, True), id="Bad basis xtb Error"), - pytest.param(({"method": "ani1x", "basis": "ani1x", "program": "torchani"}, True), id="Bad basis torchani Error"), - pytest.param(({"method": "parsley", "basis": "smirnoff", "program": "openmm"}, True), id="Bad method smirnoff Error"), -]) +@pytest.mark.parametrize( + "qc_spec", + [ + pytest.param( + ({"method": "gfn1xtb", "basis": "bad", "program": "xtb"}, True), + id="Bad basis xtb Error", + ), + pytest.param( + ({"method": "ani1x", "basis": "ani1x", "program": "torchani"}, True), + id="Bad basis torchani Error", + ), + pytest.param( + ({"method": "parsley", "basis": "smirnoff", "program": "openmm"}, True), + id="Bad method smirnoff Error", + ), + ], +) def test_adding_qc_specs(qc_spec): """ Test adding different qc_specs to a dataset/factory they are controlled by the mixin so just test that. """ - dataset = BasicDataset(dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") + dataset = BasicDataset( + dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) qc_spec_data, error = qc_spec if error: with pytest.raises(QCSpecificationError): - dataset.add_qc_spec(**qc_spec_data, spec_name="test", spec_description="test spec") + dataset.add_qc_spec( + **qc_spec_data, spec_name="test", spec_description="test spec" + ) else: - dataset.add_qc_spec(**qc_spec_data, spec_name="test", spec_description="test spec") - assert dataset.qc_specifications["test"].dict(include={"method", "basis", "program"}) == qc_spec_data + dataset.add_qc_spec( + **qc_spec_data, spec_name="test", spec_description="test spec" + ) + assert ( + dataset.qc_specifications["test"].dict( + include={"method", "basis", "program"} + ) + == qc_spec_data + ) def test_qcspec_dict(): """ Test making a dict with the qcspec. """ - dataset = BasicDataset(dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") - dataset.add_qc_spec(method="b3lyp", - basis="dzvp", - program="psi4", - spec_name="test", - spec_description="test qcspec", - store_wavefunction="orbitals_and_eigenvalues") + dataset = BasicDataset( + dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) + dataset.add_qc_spec( + method="b3lyp", + basis="dzvp", + program="psi4", + spec_name="test", + spec_description="test qcspec", + store_wavefunction="orbitals_and_eigenvalues", + ) data = dataset.qc_specifications["test"].dict() assert "store_wavefunction" in data data = dataset.qc_specifications["test"].dict(exclude={"store_wavefunction"}) @@ -1776,7 +2452,9 @@ def test_qc_spec_overwrite(): """ Test adding new qcspecs and overwiting. """ - dataset = BasicDataset(dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") + dataset = BasicDataset( + dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) # remove all qcspecs dataset.clear_qcspecs() # raise an error for no specs @@ -1784,28 +2462,35 @@ def test_qc_spec_overwrite(): dataset._check_qc_specs() assert dataset.n_qc_specs == 0 # add a default spec - dataset.add_qc_spec(method="B3LYP-D3BJ", basis='DZVP', - program="psi4", - spec_name="default", - spec_description="testing overwrite", - store_wavefunction="none") + dataset.add_qc_spec( + method="B3LYP-D3BJ", + basis="DZVP", + program="psi4", + spec_name="default", + spec_description="testing overwrite", + store_wavefunction="none", + ) assert dataset.n_qc_specs == 1 # now try and add another default spec with pytest.raises(QCSpecificationError): - dataset.add_qc_spec(method="ani1x", - basis=None, - program="torchani", - spec_name="default", - spec_description="testing ani1", - store_wavefunction="none") + dataset.add_qc_spec( + method="ani1x", + basis=None, + program="torchani", + spec_name="default", + spec_description="testing ani1", + store_wavefunction="none", + ) # now add with overwite - dataset.add_qc_spec(method="ani1x", - basis=None, - program="torchani", - spec_name="default", - spec_description="testing ani1", - store_wavefunction="none", - overwrite=True) + dataset.add_qc_spec( + method="ani1x", + basis=None, + program="torchani", + spec_name="default", + spec_description="testing ani1", + store_wavefunction="none", + overwrite=True, + ) # now make sure it has been updated spec = dataset.qc_specifications["default"] assert spec.method == "ani1x" @@ -1818,23 +2503,31 @@ def test_basicdataset_schema(): Test that producing the schema still works. """ - dataset = BasicDataset(dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") + dataset = BasicDataset( + dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) # make a schema schema = dataset.schema() assert schema["properties"]["type"]["default"] == dataset.type assert schema["properties"]["compute_tag"]["type"] == "string" -@pytest.mark.parametrize("input_data", [ - pytest.param(("CCC", 0), id="basic core and tag=0"), pytest.param(("CC@@/_-1CC", 10), id="complex core and tag=10") -]) +@pytest.mark.parametrize( + "input_data", + [ + pytest.param(("CCC", 0), id="basic core and tag=0"), + pytest.param(("CC@@/_-1CC", 10), id="complex core and tag=10"), + ], +) def test_basicdataset_clean_index(input_data): """ Test that index cleaning is working, this checks if an index already has a numeric counter and strips it, this allows us to submit molecule indexs that start from a counter other than 0. """ - dataset = BasicDataset(dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") + dataset = BasicDataset( + dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) index = input_data[0] + "-" + str(input_data[1]) @@ -1849,7 +2542,9 @@ def test_basicdataset_clean_index_normal(): Test that index cleaning works when no numeric counter is on the index this should give back the core and 0 as the tag. """ - dataset = BasicDataset(dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") + dataset = BasicDataset( + dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) index = "CCCC" core, counter = dataset._clean_index(index=index) assert core == index @@ -1861,14 +2556,18 @@ def test_basicdataset_filtering(): Test adding filtered molecules to the dataset. """ - dataset = BasicDataset(dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") + dataset = BasicDataset( + dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) molecules = duplicated_molecules(include_conformers=False, duplicates=1) # create a filtered result component_provenance = {"test_provenance": "version_1"} - dataset.filter_molecules(molecules=molecules, - component="TestFilter", - component_settings={}, - component_provenance=component_provenance) + dataset.filter_molecules( + molecules=molecules, + component="TestFilter", + component_settings={}, + component_provenance=component_provenance, + ) assert len(molecules) == dataset.n_filtered assert dataset.n_components == 1 @@ -1889,10 +2588,15 @@ def test_optimizationdataset_qc_spec(): Test generating the qc spec for optimization datasets. """ - dataset = OptimizationDataset(driver="energy", dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") + dataset = OptimizationDataset( + driver="energy", + dataset_name="Test dataset", + dataset_tagline="XXXXXXXX", + description="XXXXXXXX", + ) qc_spec = dataset.get_qc_spec("default", keyword_id="0") assert qc_spec.keywords == "0" - tags = ['program', "method", "basis", "driver"] + tags = ["program", "method", "basis", "driver"] for tag in tags: assert tag in qc_spec.dict() # make sure the driver was set back to gradient @@ -1904,30 +2608,41 @@ def test_torsiondrivedataset_torsion_indices(): Test adding molecules to a torsiondrive dataset with incorrect torsion indices. """ - dataset = TorsiondriveDataset(dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") + dataset = TorsiondriveDataset( + dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) molecules = duplicated_molecules(include_conformers=True, duplicates=1) for molecule in molecules: with pytest.raises(DihedralConnectionError): index = molecule.to_smiles() - dataset.add_molecule(index=index, molecule=molecule, dihedrals=[(0, 1, 1, 1)]) + dataset.add_molecule( + index=index, molecule=molecule, dihedrals=[(0, 1, 1, 1)] + ) -@pytest.mark.parametrize("dataset_type, program", [ - pytest.param(BasicDataset, "psi4", id="basic"), - pytest.param(OptimizationDataset, "geometric", id="optimization") -]) +@pytest.mark.parametrize( + "dataset_type, program", + [ + pytest.param(BasicDataset, "psi4", id="basic"), + pytest.param(OptimizationDataset, "geometric", id="optimization"), + ], +) def test_dataset_tasks(dataset_type, program): """ Make sure datasets can be converted to QCEngine single point and procedure calculations. """ - dataset = dataset_type(dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX") + dataset = dataset_type( + dataset_name="Test dataset", dataset_tagline="XXXXXXXX", description="XXXXXXXX" + ) # add another qc_spec molecules = duplicated_molecules(include_conformers=True, duplicates=1) # add them to the dataset for molecule in molecules: index = molecule.to_smiles() - dihedrals = [get_dihedral(molecule), ] + dihedrals = [ + get_dihedral(molecule), + ] dataset.add_molecule(index=index, molecule=molecule, dihedrals=dihedrals) tasks = dataset.to_tasks() diff --git a/openff/qcsubmit/tests/test_factories.py b/openff/qcsubmit/tests/test_factories.py index 17130f59..7c402cab 100644 --- a/openff/qcsubmit/tests/test_factories.py +++ b/openff/qcsubmit/tests/test_factories.py @@ -18,11 +18,14 @@ from openff.qcsubmit.utils import get_data, get_torsion -@pytest.mark.parametrize("factory_type", [ - pytest.param(BasicDatasetFactory, id="BasicDatasetFactory"), - pytest.param(OptimizationDatasetFactory, id="OptimizationDatasetFactory"), - pytest.param(TorsiondriveDatasetFactory, id="TorsiondriveDatasetFactory") -]) +@pytest.mark.parametrize( + "factory_type", + [ + pytest.param(BasicDatasetFactory, id="BasicDatasetFactory"), + pytest.param(OptimizationDatasetFactory, id="OptimizationDatasetFactory"), + pytest.param(TorsiondriveDatasetFactory, id="TorsiondriveDatasetFactory"), + ], +) def test_adding_workflow_components(factory_type): """ Test building workflows from a verity of workflow components. @@ -60,11 +63,14 @@ def test_adding_workflow_components(factory_type): assert len(factory.workflow) == 1 -@pytest.mark.parametrize("factory_type", [ - pytest.param(BasicDatasetFactory, id="BasicDatasetFactory"), - pytest.param(OptimizationDatasetFactory, id="OptimizationDatasetFactory"), - pytest.param(TorsiondriveDatasetFactory, id="TorsiondriveDatasetFactory") -]) +@pytest.mark.parametrize( + "factory_type", + [ + pytest.param(BasicDatasetFactory, id="BasicDatasetFactory"), + pytest.param(OptimizationDatasetFactory, id="OptimizationDatasetFactory"), + pytest.param(TorsiondriveDatasetFactory, id="TorsiondriveDatasetFactory"), + ], +) def test_adding_multiple_workflow_components(factory_type): """ Test adding a list of workflow components. @@ -85,11 +91,14 @@ def test_adding_multiple_workflow_components(factory_type): assert component in factory.workflow -@pytest.mark.parametrize("factory_type", [ - pytest.param(BasicDatasetFactory, id="BasicDatasetFactory"), - pytest.param(OptimizationDatasetFactory, id="OptimizationDatasetFactory"), - pytest.param(TorsiondriveDatasetFactory, id="TorsiondriveDatasetFactory") -]) +@pytest.mark.parametrize( + "factory_type", + [ + pytest.param(BasicDatasetFactory, id="BasicDatasetFactory"), + pytest.param(OptimizationDatasetFactory, id="OptimizationDatasetFactory"), + pytest.param(TorsiondriveDatasetFactory, id="TorsiondriveDatasetFactory"), + ], +) def test_remove_workflow_component(factory_type): """ Test removing a workflow component through the API. @@ -112,11 +121,14 @@ def test_remove_workflow_component(factory_type): assert factory.workflow == [] -@pytest.mark.parametrize("factory_type", [ - pytest.param(BasicDatasetFactory, id="BasicDatasetFactory"), - pytest.param(OptimizationDatasetFactory, id="OptimizationDatasetFactory"), - pytest.param(TorsiondriveDatasetFactory, id="TorsiondriveDatasetFactory") -]) +@pytest.mark.parametrize( + "factory_type", + [ + pytest.param(BasicDatasetFactory, id="BasicDatasetFactory"), + pytest.param(OptimizationDatasetFactory, id="OptimizationDatasetFactory"), + pytest.param(TorsiondriveDatasetFactory, id="TorsiondriveDatasetFactory"), + ], +) def test_get_wrokflow_component(factory_type): """ Test retrieving a workflow component. @@ -136,11 +148,14 @@ def test_get_wrokflow_component(factory_type): assert factory.get_workflow_components(component.type) == [component] -@pytest.mark.parametrize("factory_type", [ - pytest.param(BasicDatasetFactory, id="BasicDatasetFactory"), - pytest.param(OptimizationDatasetFactory, id="OptimizationDatasetFactory"), - pytest.param(TorsiondriveDatasetFactory, id="TorsiondriveDatasetFactory") -]) +@pytest.mark.parametrize( + "factory_type", + [ + pytest.param(BasicDatasetFactory, id="BasicDatasetFactory"), + pytest.param(OptimizationDatasetFactory, id="OptimizationDatasetFactory"), + pytest.param(TorsiondriveDatasetFactory, id="TorsiondriveDatasetFactory"), + ], +) def test_clear_workflow(factory_type): """ Test clearing out the workflow. @@ -167,7 +182,9 @@ def test_clear_workflow(factory_type): assert factory.workflow == [] -@pytest.mark.parametrize("file_type", [pytest.param("json", id="json"), pytest.param("yaml", id="yaml")]) +@pytest.mark.parametrize( + "file_type", [pytest.param("json", id="json"), pytest.param("yaml", id="yaml")] +) def test_factory_round_trip(file_type, tmpdir): """ Test round tripping a factory to file with a workflow. @@ -186,12 +203,17 @@ def test_factory_round_trip(file_type, tmpdir): assert factory2.workflow == factory.workflow -@pytest.mark.parametrize("file_type", [pytest.param("json", id="json"), pytest.param("yaml", id="yaml")]) -@pytest.mark.parametrize("factory_type", [ - pytest.param(BasicDatasetFactory, id="BasicDatasetFactory"), - pytest.param(OptimizationDatasetFactory, id="OptimizationDatasetFactory"), - pytest.param(TorsiondriveDatasetFactory, id="TorsiondriveDatasetFactory") -]) +@pytest.mark.parametrize( + "file_type", [pytest.param("json", id="json"), pytest.param("yaml", id="yaml")] +) +@pytest.mark.parametrize( + "factory_type", + [ + pytest.param(BasicDatasetFactory, id="BasicDatasetFactory"), + pytest.param(OptimizationDatasetFactory, id="OptimizationDatasetFactory"), + pytest.param(TorsiondriveDatasetFactory, id="TorsiondriveDatasetFactory"), + ], +) def test_exporting_settings_no_workflow(file_type, factory_type): """ Test exporting the settings to different file types. @@ -214,19 +236,23 @@ def test_exporting_settings_no_workflow(file_type, factory_type): assert str(value) in data -@pytest.mark.parametrize("file_type", [pytest.param("json", id="json"), pytest.param("yaml", id="yaml")]) -@pytest.mark.parametrize("factory_type", [ - pytest.param(BasicDatasetFactory, id="BasicDatasetFactory"), - pytest.param(OptimizationDatasetFactory, id="OptimizationDatasetFactory"), - pytest.param(TorsiondriveDatasetFactory, id="TorsiondriveDatasetFactory") -]) +@pytest.mark.parametrize( + "file_type", [pytest.param("json", id="json"), pytest.param("yaml", id="yaml")] +) +@pytest.mark.parametrize( + "factory_type", + [ + pytest.param(BasicDatasetFactory, id="BasicDatasetFactory"), + pytest.param(OptimizationDatasetFactory, id="OptimizationDatasetFactory"), + pytest.param(TorsiondriveDatasetFactory, id="TorsiondriveDatasetFactory"), + ], +) def test_exporting_settings_workflow(file_type, factory_type): """ Test exporting the settings and a workflow to the different file types. """ with temp_directory(): - factory = factory_type() changed_attrs = {"priority": "super_high", "compute_tag": "test tag"} for attr, value in changed_attrs.items(): @@ -246,12 +272,17 @@ def test_exporting_settings_workflow(file_type, factory_type): assert str(conformer_gen.max_conformers) in data -@pytest.mark.parametrize("file_type", [pytest.param("json", id="json"), pytest.param("yaml", id="yaml")]) -@pytest.mark.parametrize("factory_type", [ - pytest.param(BasicDatasetFactory, id="BasicDatasetFactory"), - pytest.param(OptimizationDatasetFactory, id="OptimizationDatasetFactory"), - pytest.param(TorsiondriveDatasetFactory, id="TorsiondriveDatasetFactory") -]) +@pytest.mark.parametrize( + "file_type", [pytest.param("json", id="json"), pytest.param("yaml", id="yaml")] +) +@pytest.mark.parametrize( + "factory_type", + [ + pytest.param(BasicDatasetFactory, id="BasicDatasetFactory"), + pytest.param(OptimizationDatasetFactory, id="OptimizationDatasetFactory"), + pytest.param(TorsiondriveDatasetFactory, id="TorsiondriveDatasetFactory"), + ], +) def test_importing_settings_no_workflow(file_type, factory_type): """ Test importing the settings with no workflow components from the supported file types. @@ -270,12 +301,17 @@ def test_importing_settings_no_workflow(file_type, factory_type): assert getattr(factory, attr) == value -@pytest.mark.parametrize("file_type", [pytest.param("json", id="json"), pytest.param("yaml", id="yaml")]) -@pytest.mark.parametrize("factory_type", [ - pytest.param(BasicDatasetFactory, id="BasicDatasetFactory"), - pytest.param(OptimizationDatasetFactory, id="OptimizationDatasetFactory"), - pytest.param(TorsiondriveDatasetFactory, id="TorsiondriveDatasetFactory") -]) +@pytest.mark.parametrize( + "file_type", [pytest.param("json", id="json"), pytest.param("yaml", id="yaml")] +) +@pytest.mark.parametrize( + "factory_type", + [ + pytest.param(BasicDatasetFactory, id="BasicDatasetFactory"), + pytest.param(OptimizationDatasetFactory, id="OptimizationDatasetFactory"), + pytest.param(TorsiondriveDatasetFactory, id="TorsiondriveDatasetFactory"), + ], +) def test_importing_settings_workflow(file_type, factory_type): """ Test importing the settings and a workflow from the supported file types. @@ -299,12 +335,17 @@ def test_importing_settings_workflow(file_type, factory_type): assert isinstance(component, workflow_components.StandardConformerGenerator) is True -@pytest.mark.parametrize("file_type", [pytest.param("json", id="json"), pytest.param("yaml", id="yaml")]) -@pytest.mark.parametrize("factory_type", [ - pytest.param(BasicDatasetFactory, id="BasicDatasetFactory"), - pytest.param(OptimizationDatasetFactory, id="OptimizationDatasetFactory"), - pytest.param(TorsiondriveDatasetFactory, id="TorsiondriveDatasetFactory") -]) +@pytest.mark.parametrize( + "file_type", [pytest.param("json", id="json"), pytest.param("yaml", id="yaml")] +) +@pytest.mark.parametrize( + "factory_type", + [ + pytest.param(BasicDatasetFactory, id="BasicDatasetFactory"), + pytest.param(OptimizationDatasetFactory, id="OptimizationDatasetFactory"), + pytest.param(TorsiondriveDatasetFactory, id="TorsiondriveDatasetFactory"), + ], +) def test_import_workflow_only(file_type, factory_type): """ Test importing a workflow only from a workflow file. @@ -323,12 +364,17 @@ def test_import_workflow_only(file_type, factory_type): assert factory.workflow != factory2.workflow -@pytest.mark.parametrize("file_type", [pytest.param("json", id="json"), pytest.param("yaml", id="yaml")]) -@pytest.mark.parametrize("factory_type", [ - pytest.param(BasicDatasetFactory, id="BasicDatasetFactory"), - pytest.param(OptimizationDatasetFactory, id="OptimizationDatasetFactory"), - pytest.param(TorsiondriveDatasetFactory, id="TorsiondriveDatasetFactory") -]) +@pytest.mark.parametrize( + "file_type", [pytest.param("json", id="json"), pytest.param("yaml", id="yaml")] +) +@pytest.mark.parametrize( + "factory_type", + [ + pytest.param(BasicDatasetFactory, id="BasicDatasetFactory"), + pytest.param(OptimizationDatasetFactory, id="OptimizationDatasetFactory"), + pytest.param(TorsiondriveDatasetFactory, id="TorsiondriveDatasetFactory"), + ], +) def test_export_workflow_only(file_type, factory_type): """ Test exporting the workflow only from the factory. @@ -352,10 +398,13 @@ def test_export_workflow_only(file_type, factory_type): assert "tag" not in data -@pytest.mark.parametrize("factory_type", [ - pytest.param(BasicDatasetFactory, id="BasicDatasetFactory"), - pytest.param(OptimizationDatasetFactory, id="OptimizationDatasetFactory"), -]) +@pytest.mark.parametrize( + "factory_type", + [ + pytest.param(BasicDatasetFactory, id="BasicDatasetFactory"), + pytest.param(OptimizationDatasetFactory, id="OptimizationDatasetFactory"), + ], +) def test_basic_opt_factory_index(factory_type): """ Test the basic factories ability to make a molecule index this should be the canonical, isomeric smiles. @@ -420,7 +469,9 @@ def test_torsiondrive_linear_torsion(): """ factory = TorsiondriveDatasetFactory() - molecules = Molecule.from_file(get_data("linear_molecules.sdf"), "sdf", allow_undefined_stereo=True) + molecules = Molecule.from_file( + get_data("linear_molecules.sdf"), "sdf", allow_undefined_stereo=True + ) for molecule in molecules: assert bool(factory._detect_linear_torsions(molecule)) is True @@ -447,7 +498,9 @@ def test_torsiondrive_torsion_string(): dihedral.append(atom.molecule_atom_index) reference_torsions.append(tuple(dihedral)) - assert torsion in reference_torsions or tuple(reversed(torsion)) in reference_torsions + assert ( + torsion in reference_torsions or tuple(reversed(torsion)) in reference_torsions + ) def test_create_dataset_missing_input(): @@ -455,19 +508,27 @@ def test_create_dataset_missing_input(): Make sure an error is raised if the input can not be found. """ factory = BasicDatasetFactory() - with pytest.raises(FileNotFoundError, match="The input missing_file.smi could not be found."): + with pytest.raises( + FileNotFoundError, match="The input missing_file.smi could not be found." + ): _ = factory.create_dataset( dataset_name="test dataset", tagline="test dataset", description="test dataset", - molecules="missing_file.smi" + molecules="missing_file.smi", ) -@pytest.mark.parametrize("factory_dataset_type", [ - pytest.param((BasicDatasetFactory, BasicDataset), id="BasicDatasetFactory"), - pytest.param((OptimizationDatasetFactory, OptimizationDataset), id="OptimizationDatasetFactory"), -]) +@pytest.mark.parametrize( + "factory_dataset_type", + [ + pytest.param((BasicDatasetFactory, BasicDataset), id="BasicDatasetFactory"), + pytest.param( + (OptimizationDatasetFactory, OptimizationDataset), + id="OptimizationDatasetFactory", + ), + ], +) def test_create_dataset(factory_dataset_type): """ Test making a the correct corresponding dataset type from a given factory type. @@ -477,19 +538,26 @@ def test_create_dataset(factory_dataset_type): element_filter = workflow_components.ElementFilter() element_filter.allowed_elements = [1, 6, 8, 7] factory.add_workflow_components(element_filter) - conformer_generator = workflow_components.StandardConformerGenerator(max_conformers=1) + conformer_generator = workflow_components.StandardConformerGenerator( + max_conformers=1 + ) factory.add_workflow_components(conformer_generator) - mols = Molecule.from_file(get_data("tautomers_small.smi"), "smi", allow_undefined_stereo=True) + mols = Molecule.from_file( + get_data("tautomers_small.smi"), "smi", allow_undefined_stereo=True + ) # set some settings - changed_attrs = {"compute_tag": "test tag", - "dataset_tags": ["openff", "test"]} + changed_attrs = {"compute_tag": "test tag", "dataset_tags": ["openff", "test"]} for attr, value in changed_attrs.items(): setattr(factory, attr, value) - dataset = factory.create_dataset(dataset_name="test name", molecules=mols, description="Force field test", - tagline="A test dataset") + dataset = factory.create_dataset( + dataset_name="test name", + molecules=mols, + description="Force field test", + tagline="A test dataset", + ) # check the attributes were changed for attr, value in changed_attrs.items(): @@ -511,13 +579,24 @@ def test_create_torsiondrive_dataset(): """ factory = TorsiondriveDatasetFactory() scan_filter = workflow_components.ScanEnumerator() - scan_filter.add_torsion_scan(smarts="[*:1]~[*:2]-[#8:3]-[#1:4]", scan_rage=(-90, 90), scan_increment=10) + scan_filter.add_torsion_scan( + smarts="[*:1]~[*:2]-[#8:3]-[#1:4]", scan_rage=(-90, 90), scan_increment=10 + ) factory.add_workflow_components(scan_filter) - conformer_generator = workflow_components.StandardConformerGenerator(max_conformers=1) + conformer_generator = workflow_components.StandardConformerGenerator( + max_conformers=1 + ) factory.add_workflow_components(conformer_generator) - mols = Molecule.from_file(get_data("tautomers_small.smi"), "smi", allow_undefined_stereo=True) - dataset = factory.create_dataset(dataset_name="test name", molecules=mols, description="Force field test", - tagline="A test dataset", processors=1) + mols = Molecule.from_file( + get_data("tautomers_small.smi"), "smi", allow_undefined_stereo=True + ) + dataset = factory.create_dataset( + dataset_name="test name", + molecules=mols, + description="Force field test", + tagline="A test dataset", + processors=1, + ) assert dataset.n_molecules > 0 assert dataset.n_records > 0 @@ -531,6 +610,10 @@ def test_create_dataset_atom_map(): factory = OptimizationDatasetFactory() mol = Molecule.from_smiles("CCCC([O-])=O") - mol.properties['atom_map'] = {1: 1, 2: 2, 3: 3, 4: 4} - _ = factory.create_dataset(dataset_name="test name", molecules=mol, description="Force field test", - tagline="A test dataset") + mol.properties["atom_map"] = {1: 1, 2: 2, 3: 3, 4: 4} + _ = factory.create_dataset( + dataset_name="test name", + molecules=mol, + description="Force field test", + tagline="A test dataset", + ) diff --git a/openff/qcsubmit/tests/test_serializers.py b/openff/qcsubmit/tests/test_serializers.py index c3eca944..f87e4f5d 100644 --- a/openff/qcsubmit/tests/test_serializers.py +++ b/openff/qcsubmit/tests/test_serializers.py @@ -29,10 +29,13 @@ from openff.qcsubmit.utils import get_data -@pytest.mark.parametrize("serializer_type", [ - pytest.param(("JSON", JsonSerializer), id="Json"), - pytest.param(("YAML", YamlSerializer), id="Yaml") -]) +@pytest.mark.parametrize( + "serializer_type", + [ + pytest.param(("JSON", JsonSerializer), id="Json"), + pytest.param(("YAML", YamlSerializer), id="Yaml"), + ], +) def test_register_again(serializer_type): """ Test adding another serializer @@ -42,12 +45,15 @@ def test_register_again(serializer_type): register_serializer(*serializer_type) -@pytest.mark.parametrize("compressor_type", [ - pytest.param(("XZ", LZMACompressor), id="XZ"), - pytest.param(("BZ2", BZ2Compressor), id="BZ2"), - pytest.param(("GZ", GzipCompressor), id="GZ"), - pytest.param(("", NoneCompressor), id="Normal") -]) +@pytest.mark.parametrize( + "compressor_type", + [ + pytest.param(("XZ", LZMACompressor), id="XZ"), + pytest.param(("BZ2", BZ2Compressor), id="BZ2"), + pytest.param(("GZ", GzipCompressor), id="GZ"), + pytest.param(("", NoneCompressor), id="Normal"), + ], +) def test_register_compressor_again(compressor_type): """ Test adding a compressor again. @@ -56,10 +62,13 @@ def test_register_compressor_again(compressor_type): register_compressor(*compressor_type) -@pytest.mark.parametrize("deserializer_type", [ - pytest.param(("JSON", JsonDeSerializer), id="Json"), - pytest.param(("YAML", YamlDeSerializer), id="Yaml") -]) +@pytest.mark.parametrize( + "deserializer_type", + [ + pytest.param(("JSON", JsonDeSerializer), id="Json"), + pytest.param(("YAML", YamlDeSerializer), id="Yaml"), + ], +) def test_register_again_deserializer(deserializer_type): """ Test registering a deserializer again. @@ -126,10 +135,9 @@ def test_deserializer_error(): deserialize("missing_file.json") -@pytest.mark.parametrize("serializer", [ - pytest.param(".json", id="Json"), - pytest.param(".yaml", id="yaml") -]) +@pytest.mark.parametrize( + "serializer", [pytest.param(".json", id="Json"), pytest.param(".yaml", id="yaml")] +) def test_serializer_round_trips(serializer): """ Test serializing data to and from file with no compression. @@ -144,16 +152,18 @@ def test_serializer_round_trips(serializer): assert data == deserialized_data -@pytest.mark.parametrize("compression", [ - pytest.param("xz", id="lzma"), - pytest.param("gz", id="gzip"), - pytest.param("", id="No compression"), - pytest.param("bz2", id="bz2") -]) -@pytest.mark.parametrize("serialization", [ - pytest.param("json", id="Json"), - pytest.param("yaml", id="yaml") -]) +@pytest.mark.parametrize( + "compression", + [ + pytest.param("xz", id="lzma"), + pytest.param("gz", id="gzip"), + pytest.param("", id="No compression"), + pytest.param("bz2", id="bz2"), + ], +) +@pytest.mark.parametrize( + "serialization", [pytest.param("json", id="Json"), pytest.param("yaml", id="yaml")] +) def test_compression_serialization_round_trip_file_name(serialization, compression): """ Test all of the different serialization and compression combinations. @@ -161,7 +171,9 @@ def test_compression_serialization_round_trip_file_name(serialization, compressi """ # get data in a dict format data = deserialize(get_data("settings_with_workflow.json")) - file_name = "".join(["settings_with_workflow", ".", serialization, ".", compression]) + file_name = "".join( + ["settings_with_workflow", ".", serialization, ".", compression] + ) # now export the file and read back with temp_directory(): serialize(serializable=data, file_name=file_name, compression=None) @@ -169,16 +181,18 @@ def test_compression_serialization_round_trip_file_name(serialization, compressi assert data == deserialized_data -@pytest.mark.parametrize("compression", [ - pytest.param("xz", id="lzma"), - pytest.param("gz", id="gzip"), - pytest.param("", id="No compression"), - pytest.param("bz2", id="bz2") -]) -@pytest.mark.parametrize("serialization", [ - pytest.param("json", id="Json"), - pytest.param("yaml", id="yaml") -]) +@pytest.mark.parametrize( + "compression", + [ + pytest.param("xz", id="lzma"), + pytest.param("gz", id="gzip"), + pytest.param("", id="No compression"), + pytest.param("bz2", id="bz2"), + ], +) +@pytest.mark.parametrize( + "serialization", [pytest.param("json", id="Json"), pytest.param("yaml", id="yaml")] +) def test_compression_serialization_round_trip(serialization, compression): """ Test all of the different serialization and compression combinations. diff --git a/openff/qcsubmit/tests/test_submissions.py b/openff/qcsubmit/tests/test_submissions.py index 3eaf4241..6d998901 100644 --- a/openff/qcsubmit/tests/test_submissions.py +++ b/openff/qcsubmit/tests/test_submissions.py @@ -30,11 +30,30 @@ from openff.qcsubmit.utils import get_data -@pytest.mark.parametrize("specification", [ - pytest.param(({"method": "hf", "basis": "3-21g", "program": "psi4"}, "energy"), id="PSI4 hf 3-21g energy"), - pytest.param(({"method": "smirnoff99Frosst-1.1.0", "basis": "smirnoff", "program": "openmm"}, "energy"), id="SMIRNOFF smirnoff99Frosst-1.1.0 energy"), - pytest.param(({"method": "uff", "basis": None, "program": "rdkit"}, "gradient"), id="RDKit UFF gradient") -]) +@pytest.mark.parametrize( + "specification", + [ + pytest.param( + ({"method": "hf", "basis": "3-21g", "program": "psi4"}, "energy"), + id="PSI4 hf 3-21g energy", + ), + pytest.param( + ( + { + "method": "smirnoff99Frosst-1.1.0", + "basis": "smirnoff", + "program": "openmm", + }, + "energy", + ), + id="SMIRNOFF smirnoff99Frosst-1.1.0 energy", + ), + pytest.param( + ({"method": "uff", "basis": None, "program": "rdkit"}, "gradient"), + id="RDKit UFF gradient", + ), + ], +) def test_basic_submissions_single_spec(fractal_compute_server, specification): """Test submitting a basic dataset to a snowflake server.""" @@ -49,15 +68,19 @@ def test_basic_submissions_single_spec(fractal_compute_server, specification): molecules = Molecule.from_file(get_data("butane_conformers.pdb"), "pdb") factory = BasicDatasetFactory(driver=driver) - factory.add_qc_spec(**qc_spec, spec_name="default", - spec_description="testing the single points", - overwrite=True) - - dataset = factory.create_dataset(dataset_name=f"Test single points info {program}, {driver}", - molecules=molecules, - description="Test basics dataset", - tagline="Testing single point datasets", - ) + factory.add_qc_spec( + **qc_spec, + spec_name="default", + spec_description="testing the single points", + overwrite=True, + ) + + dataset = factory.create_dataset( + dataset_name=f"Test single points info {program}, {driver}", + molecules=molecules, + description="Test basics dataset", + tagline="Testing single point datasets", + ) # force a metadata validation error dataset.metadata.long_description = None @@ -100,7 +123,9 @@ def test_basic_submissions_single_spec(fractal_compute_server, specification): assert basis == spec.basis break else: - raise RuntimeError(f"The requested compute was not found in the history {ds.data.history}") + raise RuntimeError( + f"The requested compute was not found in the history {ds.data.history}" + ) for spec in dataset.qc_specifications.values(): query = ds.get_records( @@ -122,23 +147,34 @@ def test_basic_submissions_multiple_spec(fractal_compute_server): client = FractalClient(fractal_compute_server) - qc_specs = [{"method": "openff-1.0.0", "basis": "smirnoff", "program": "openmm", "spec_name": "openff"}, - {"method": "gaff-2.11", "basis": "antechamber", "program": "openmm", "spec_name": "gaff"}] + qc_specs = [ + { + "method": "openff-1.0.0", + "basis": "smirnoff", + "program": "openmm", + "spec_name": "openff", + }, + { + "method": "gaff-2.11", + "basis": "antechamber", + "program": "openmm", + "spec_name": "gaff", + }, + ] molecules = Molecule.from_file(get_data("butane_conformers.pdb"), "pdb") factory = BasicDatasetFactory(driver="energy") factory.clear_qcspecs() for spec in qc_specs: - factory.add_qc_spec(**spec, - spec_description="testing the single points" - ) + factory.add_qc_spec(**spec, spec_description="testing the single points") - dataset = factory.create_dataset(dataset_name="Test single points multiple specs", - molecules=molecules, - description="Test basics dataset", - tagline="Testing single point datasets", - ) + dataset = factory.create_dataset( + dataset_name="Test single points multiple specs", + molecules=molecules, + description="Test basics dataset", + tagline="Testing single point datasets", + ) # force a metadata validation error dataset.metadata.long_description = None @@ -207,17 +243,23 @@ def test_basic_submissions_single_pcm_spec(fractal_compute_server): molecules = Molecule.from_file(get_data("butane_conformers.pdb"), "pdb") factory = BasicDatasetFactory(driver="energy") - factory.add_qc_spec(method="hf", basis="sto-3g", program=program, spec_name="default", - spec_description="testing the single points with pcm", - implicit_solvent=PCMSettings(units="au", medium_Solvent="water"), - overwrite=True) + factory.add_qc_spec( + method="hf", + basis="sto-3g", + program=program, + spec_name="default", + spec_description="testing the single points with pcm", + implicit_solvent=PCMSettings(units="au", medium_Solvent="water"), + overwrite=True, + ) # only use one molecule due to the time it takes to run with pcm - dataset = factory.create_dataset(dataset_name="Test single points with pcm water", - molecules=molecules[0], - description="Test basics dataset with pcm water", - tagline="Testing single point datasets with pcm water", - ) + dataset = factory.create_dataset( + dataset_name="Test single points with pcm water", + molecules=molecules[0], + description="Test basics dataset with pcm water", + tagline="Testing single point datasets with pcm water", + ) # force a metadata validation error dataset.metadata.long_description = None @@ -260,7 +302,9 @@ def test_basic_submissions_single_pcm_spec(fractal_compute_server): assert basis == spec.basis break else: - raise RuntimeError(f"The requested compute was not found in the history {ds.data.history}") + raise RuntimeError( + f"The requested compute was not found in the history {ds.data.history}" + ) for spec in dataset.qc_specifications.values(): query = ds.get_records( @@ -289,13 +333,21 @@ def test_adding_specifications(fractal_compute_server): mol = Molecule.from_smiles("CO") # make a dataset factory = OptimizationDatasetFactory() - opt_dataset = factory.create_dataset(dataset_name="Specification error check", molecules=mol, - description="test adding new compute specs to datasets", - tagline="test adding new compute specs") + opt_dataset = factory.create_dataset( + dataset_name="Specification error check", + molecules=mol, + description="test adding new compute specs to datasets", + tagline="test adding new compute specs", + ) opt_dataset.clear_qcspecs() # add a new mm spec - opt_dataset.add_qc_spec(method="openff-1.0.0", basis="smirnoff", program="openmm", - spec_description="default openff spec", spec_name="openff-1.0.0") + opt_dataset.add_qc_spec( + method="openff-1.0.0", + basis="smirnoff", + program="openmm", + spec_description="default openff spec", + spec_name="openff-1.0.0", + ) # submit the optimizations and let the compute run opt_dataset.submit(client=client) @@ -306,42 +358,82 @@ def test_adding_specifications(fractal_compute_server): ds = client.get_collection(opt_dataset.type, opt_dataset.dataset_name) # now try and add the specification again this should return True - assert opt_dataset._add_dataset_specification(spec=opt_dataset.qc_specifications["openff-1.0.0"], - procedure_spec=opt_dataset.optimization_procedure.get_optimzation_spec(), - dataset=ds) is True + assert ( + opt_dataset._add_dataset_specification( + spec=opt_dataset.qc_specifications["openff-1.0.0"], + procedure_spec=opt_dataset.optimization_procedure.get_optimzation_spec(), + dataset=ds, + ) + is True + ) # now change part of the spec but keep the name the same opt_dataset.clear_qcspecs() - opt_dataset.add_qc_spec(method="openff-1.2.1", basis="smirnoff", spec_name="openff-1.0.0", program="openmm", - spec_description="openff-1.2.1 with wrong name.") + opt_dataset.add_qc_spec( + method="openff-1.2.1", + basis="smirnoff", + spec_name="openff-1.0.0", + program="openmm", + spec_description="openff-1.2.1 with wrong name.", + ) # now try and add this specification with the same name but different settings with pytest.raises(QCSpecificationError): - opt_dataset._add_dataset_specification(spec=opt_dataset.qc_specifications["openff-1.0.0"], - procedure_spec=opt_dataset.optimization_procedure.get_optimzation_spec(), - dataset=ds) + opt_dataset._add_dataset_specification( + spec=opt_dataset.qc_specifications["openff-1.0.0"], + procedure_spec=opt_dataset.optimization_procedure.get_optimzation_spec(), + dataset=ds, + ) # now add a new specification but no compute and make sure it is overwritten opt_dataset.clear_qcspecs() - opt_dataset.add_qc_spec(method="ani1x", basis=None, program="torchani", spec_name="ani", spec_description="a ani spec") - assert opt_dataset._add_dataset_specification(spec=opt_dataset.qc_specifications["ani"], - procedure_spec=opt_dataset.optimization_procedure.get_optimzation_spec(), - dataset=ds) is True + opt_dataset.add_qc_spec( + method="ani1x", + basis=None, + program="torchani", + spec_name="ani", + spec_description="a ani spec", + ) + assert ( + opt_dataset._add_dataset_specification( + spec=opt_dataset.qc_specifications["ani"], + procedure_spec=opt_dataset.optimization_procedure.get_optimzation_spec(), + dataset=ds, + ) + is True + ) # now change the spec slightly and add again opt_dataset.clear_qcspecs() - opt_dataset.add_qc_spec(method="ani1ccx", basis=None, program="torchani", spec_name="ani", - spec_description="a ani spec") - assert opt_dataset._add_dataset_specification(spec=opt_dataset.qc_specifications["ani"], - procedure_spec=opt_dataset.optimization_procedure.get_optimzation_spec(), - dataset=ds) is True - - -@pytest.mark.parametrize("dataset_data", [ - pytest.param((BasicDatasetFactory, BasicDataset), id="Dataset"), - pytest.param((OptimizationDatasetFactory, OptimizationDataset), id="OptimizationDataset"), - pytest.param((TorsiondriveDatasetFactory, TorsiondriveDataset), id="TorsiondriveDataset") -]) + opt_dataset.add_qc_spec( + method="ani1ccx", + basis=None, + program="torchani", + spec_name="ani", + spec_description="a ani spec", + ) + assert ( + opt_dataset._add_dataset_specification( + spec=opt_dataset.qc_specifications["ani"], + procedure_spec=opt_dataset.optimization_procedure.get_optimzation_spec(), + dataset=ds, + ) + is True + ) + + +@pytest.mark.parametrize( + "dataset_data", + [ + pytest.param((BasicDatasetFactory, BasicDataset), id="Dataset"), + pytest.param( + (OptimizationDatasetFactory, OptimizationDataset), id="OptimizationDataset" + ), + pytest.param( + (TorsiondriveDatasetFactory, TorsiondriveDataset), id="TorsiondriveDataset" + ), + ], +) def test_adding_compute(fractal_compute_server, dataset_data): """ Test adding new compute to each of the dataset types using none psi4 programs. @@ -352,15 +444,19 @@ def test_adding_compute(fractal_compute_server, dataset_data): # make and clear out the qc specs factory = factory_type() factory.clear_qcspecs() - factory.add_qc_spec(method="openff-1.0.0", - basis="smirnoff", - program="openmm", - spec_name="default", - spec_description="default spec for openff") - dataset = factory.create_dataset(dataset_name=f"Test adding compute to {factory_type}", - molecules=mol, - description=f"Testing adding compute to a {dataset_type} dataset", - tagline="tests for adding compute.") + factory.add_qc_spec( + method="openff-1.0.0", + basis="smirnoff", + program="openmm", + spec_name="default", + spec_description="default spec for openff", + ) + dataset = factory.create_dataset( + dataset_name=f"Test adding compute to {factory_type}", + molecules=mol, + description=f"Testing adding compute to a {dataset_type} dataset", + tagline="tests for adding compute.", + ) # now submit again dataset.submit(client=client) @@ -370,14 +466,21 @@ def test_adding_compute(fractal_compute_server, dataset_data): # now lets make a dataset with new compute and submit it # transfer the metadata to compare the elements - compute_dataset = dataset_type(dataset_name=dataset.dataset_name, metadata=dataset.metadata, dataset_tagline=dataset.dataset_tagline, description=dataset.description) + compute_dataset = dataset_type( + dataset_name=dataset.dataset_name, + metadata=dataset.metadata, + dataset_tagline=dataset.dataset_tagline, + description=dataset.description, + ) compute_dataset.clear_qcspecs() # now add the new compute spec - compute_dataset.add_qc_spec(method="uff", - basis=None, - program="rdkit", - spec_name="rdkit", - spec_description="rdkit basic spec") + compute_dataset.add_qc_spec( + method="uff", + basis=None, + program="rdkit", + spec_name="rdkit", + spec_description="rdkit basic spec", + ) # make sure the dataset has no molecules and submit it assert compute_dataset.dataset == {} @@ -463,18 +566,21 @@ def test_basic_submissions_wavefunction(fractal_compute_server): factory = BasicDatasetFactory(driver="energy") factory.clear_qcspecs() - factory.add_qc_spec(method="hf", - basis="sto-6g", - program="psi4", - spec_name="default", - spec_description="wavefunction spec", - store_wavefunction="orbitals_and_eigenvalues") - - dataset = factory.create_dataset(dataset_name="Test single points with wavefunction", - molecules=molecules, - description="Test basics dataset", - tagline="Testing single point datasets with wavefunction", - ) + factory.add_qc_spec( + method="hf", + basis="sto-6g", + program="psi4", + spec_name="default", + spec_description="wavefunction spec", + store_wavefunction="orbitals_and_eigenvalues", + ) + + dataset = factory.create_dataset( + dataset_name="Test single points with wavefunction", + molecules=molecules, + description="Test basics dataset", + tagline="Testing single point datasets with wavefunction", + ) # submit the dataset # now submit again @@ -531,13 +637,28 @@ def test_optimization_submissions_with_constraints(fractal_compute_server): """ client = FractalClient(fractal_compute_server) ethane = Molecule.from_file(get_data("ethane.sdf"), "sdf") - dataset = OptimizationDataset(dataset_name="Test optimizations with constraint", description="Test optimization dataset with constraints", dataset_tagline="Testing optimization datasets") + dataset = OptimizationDataset( + dataset_name="Test optimizations with constraint", + description="Test optimization dataset with constraints", + dataset_tagline="Testing optimization datasets", + ) # add just mm spec - dataset.add_qc_spec(method="openff-1.0.0", basis="smirnoff", program="openmm", spec_name="default", spec_description="mm default spec", overwrite=True) + dataset.add_qc_spec( + method="openff-1.0.0", + basis="smirnoff", + program="openmm", + spec_name="default", + spec_description="mm default spec", + overwrite=True, + ) # build some constraints constraints = Constraints() - constraints.add_set_constraint(constraint_type="dihedral", indices=[2, 0, 1, 5], value=60, bonded=True) - constraints.add_freeze_constraint(constraint_type="distance", indices=[0, 1], bonded=True) + constraints.add_set_constraint( + constraint_type="dihedral", indices=[2, 0, 1, 5], value=60, bonded=True + ) + constraints.add_freeze_constraint( + constraint_type="distance", indices=[0, 1], bonded=True + ) # add the molecule index = ethane.to_smiles() dataset.add_molecule(index=index, molecule=ethane, constraints=constraints) @@ -558,14 +679,35 @@ def test_optimization_submissions_with_constraints(fractal_compute_server): # now make sure the constraints worked final_molecule = record.get_final_molecule() assert pytest.approx(final_molecule.measure((2, 0, 1, 5)), abs=1e-2) == 60 - assert record.get_initial_molecule().measure((0, 1)) == pytest.approx(final_molecule.measure((0, 1))) - - -@pytest.mark.parametrize("specification", [ - pytest.param(({"method": "hf", "basis": "3-21g", "program": "psi4"}, "gradient"), id="PSI4 hf 3-21g gradient"), - pytest.param(({"method": "openff_unconstrained-1.0.0", "basis": "smirnoff", "program": "openmm"}, "gradient"), id="SMIRNOFF openff_unconstrained-1.0.0 gradient"), - pytest.param(({"method": "uff", "basis": None, "program": "rdkit"}, "gradient"), id="RDKit UFF gradient") -]) + assert record.get_initial_molecule().measure((0, 1)) == pytest.approx( + final_molecule.measure((0, 1)) + ) + + +@pytest.mark.parametrize( + "specification", + [ + pytest.param( + ({"method": "hf", "basis": "3-21g", "program": "psi4"}, "gradient"), + id="PSI4 hf 3-21g gradient", + ), + pytest.param( + ( + { + "method": "openff_unconstrained-1.0.0", + "basis": "smirnoff", + "program": "openmm", + }, + "gradient", + ), + id="SMIRNOFF openff_unconstrained-1.0.0 gradient", + ), + pytest.param( + ({"method": "uff", "basis": None, "program": "rdkit"}, "gradient"), + id="RDKit UFF gradient", + ), + ], +) def test_optimization_submissions(fractal_compute_server, specification): """Test submitting an Optimization dataset to a snowflake server.""" @@ -579,13 +721,16 @@ def test_optimization_submissions(fractal_compute_server, specification): molecules = Molecule.from_file(get_data("butane_conformers.pdb"), "pdb") factory = OptimizationDatasetFactory(driver=driver) - factory.add_qc_spec(**qc_spec, spec_name="default", spec_description="test", overwrite=True) + factory.add_qc_spec( + **qc_spec, spec_name="default", spec_description="test", overwrite=True + ) - dataset = factory.create_dataset(dataset_name=f"Test optimizations info {program}, {driver}", - molecules=molecules[:2], - description="Test optimization dataset", - tagline="Testing optimization datasets", - ) + dataset = factory.create_dataset( + dataset_name=f"Test optimizations info {program}, {driver}", + molecules=molecules[:2], + description="Test optimization dataset", + tagline="Testing optimization datasets", + ) # force a metadata validation error dataset.metadata.long_description = None @@ -655,15 +800,22 @@ def test_optimization_submissions_with_pcm(fractal_compute_server): molecules = Molecule.from_smiles("C") factory = OptimizationDatasetFactory(driver="gradient") - factory.add_qc_spec(method="hf", basis="sto-3g", program=program, spec_name="default", spec_description="test", - implicit_solvent=PCMSettings(units="au", medium_Solvent="water"), - overwrite=True) - - dataset = factory.create_dataset(dataset_name="Test optimizations info with pcm water", - molecules=molecules, - description="Test optimization dataset", - tagline="Testing optimization datasets", - ) + factory.add_qc_spec( + method="hf", + basis="sto-3g", + program=program, + spec_name="default", + spec_description="test", + implicit_solvent=PCMSettings(units="au", medium_Solvent="water"), + overwrite=True, + ) + + dataset = factory.create_dataset( + dataset_name="Test optimizations info with pcm water", + molecules=molecules, + description="Test optimization dataset", + tagline="Testing optimization datasets", + ) # force a metadata validation error dataset.metadata.long_description = None @@ -732,16 +884,24 @@ def test_torsiondrive_scan_keywords(fractal_compute_server): scan_enum.add_torsion_scan(smarts="[*:1]~[#6:2]-[#8:3]~[*:4]") factory.add_workflow_components(scan_enum) factory.clear_qcspecs() - factory.add_qc_spec(method="openff_unconstrained-1.1.0", basis="smirnoff", program="openmm", spec_description="scan range test", spec_name="openff-1.1.0") - dataset = factory.create_dataset(dataset_name="Torsiondrive scan keywords", molecules=molecules, - description="Testing scan keywords which overwrite the global settings", - tagline="Testing scan keywords which overwrite the global settings") + factory.add_qc_spec( + method="openff_unconstrained-1.1.0", + basis="smirnoff", + program="openmm", + spec_description="scan range test", + spec_name="openff-1.1.0", + ) + dataset = factory.create_dataset( + dataset_name="Torsiondrive scan keywords", + molecules=molecules, + description="Testing scan keywords which overwrite the global settings", + tagline="Testing scan keywords which overwrite the global settings", + ) # now set the keywords keys = list(dataset.dataset.keys()) entry = dataset.dataset[keys[0]] - entry.keywords = {"grid_spacing": [5], - "dihedral_ranges": [(-10, 10)]} + entry.keywords = {"grid_spacing": [5], "dihedral_ranges": [(-10, 10)]} # now submit dataset.submit(client=client) @@ -765,15 +925,35 @@ def test_torsiondrive_constraints(fractal_compute_server): client = FractalClient(fractal_compute_server) molecule = Molecule.from_file(get_data("TRP.mol2")) - dataset = TorsiondriveDataset(dataset_name="Torsiondrive constraints", dataset_tagline="Testing torsiondrive constraints", description="Testing torsiondrive constraints.") + dataset = TorsiondriveDataset( + dataset_name="Torsiondrive constraints", + dataset_tagline="Testing torsiondrive constraints", + description="Testing torsiondrive constraints.", + ) dataset.clear_qcspecs() - dataset.add_qc_spec(method="uff", basis=None, program="rdkit", spec_name="uff", spec_description="tdrive constraints") + dataset.add_qc_spec( + method="uff", + basis=None, + program="rdkit", + spec_name="uff", + spec_description="tdrive constraints", + ) # use a restricted range to keep the scan fast - dataset.add_molecule(index="1", molecule=molecule, attributes=MoleculeAttributes.from_openff_molecule(molecule=molecule), dihedrals=[(4, 6, 8, 28)], keywords={"dihedral_ranges": [(-165, -145)]}) + dataset.add_molecule( + index="1", + molecule=molecule, + attributes=MoleculeAttributes.from_openff_molecule(molecule=molecule), + dihedrals=[(4, 6, 8, 28)], + keywords={"dihedral_ranges": [(-165, -145)]}, + ) entry = dataset.dataset["1"] # add the constraints - entry.add_constraint(constraint="freeze", constraint_type="dihedral", indices=[6, 8, 10, 13]) - entry.add_constraint(constraint="freeze", constraint_type="dihedral", indices=[8, 10, 13, 14]) + entry.add_constraint( + constraint="freeze", constraint_type="dihedral", indices=[6, 8, 10, 13] + ) + entry.add_constraint( + constraint="freeze", constraint_type="dihedral", indices=[8, 10, 13, 14] + ) dataset.submit(client=client, processes=1) fractal_compute_server.await_services(max_iter=50) @@ -782,7 +962,7 @@ def test_torsiondrive_constraints(fractal_compute_server): ds = client.get_collection("TorsionDriveDataset", dataset.dataset_name) record = ds.get_record(ds.df.index[0], "uff") - opt = client.query_procedures(id=record.optimization_history['[-150]'])[0] + opt = client.query_procedures(id=record.optimization_history["[-150]"])[0] constraints = opt.keywords["constraints"] # make sure both the freeze and set constraints are passed on assert "set" in constraints @@ -791,13 +971,31 @@ def test_torsiondrive_constraints(fractal_compute_server): assert len(constraints["freeze"]) == 2 assert constraints["freeze"][0]["indices"] == [6, 8, 10, 13] # make sure the dihedral has not changed - assert pytest.approx(opt.get_initial_molecule().measure((6, 8, 10, 13))) == opt.get_final_molecule().measure((6, 8, 10, 13)) - - -@pytest.mark.parametrize("specification", [ - pytest.param(({"method": "openff_unconstrained-1.1.0", "basis": "smirnoff", "program": "openmm"}, "gradient"), id="SMIRNOFF openff_unconstrained-1.0.0 gradient"), - pytest.param(({"method": "mmff94", "basis": None, "program": "rdkit"}, "gradient"), id="RDKit mmff94 gradient") -]) + assert pytest.approx( + opt.get_initial_molecule().measure((6, 8, 10, 13)) + ) == opt.get_final_molecule().measure((6, 8, 10, 13)) + + +@pytest.mark.parametrize( + "specification", + [ + pytest.param( + ( + { + "method": "openff_unconstrained-1.1.0", + "basis": "smirnoff", + "program": "openmm", + }, + "gradient", + ), + id="SMIRNOFF openff_unconstrained-1.0.0 gradient", + ), + pytest.param( + ({"method": "mmff94", "basis": None, "program": "rdkit"}, "gradient"), + id="RDKit mmff94 gradient", + ), + ], +) def test_torsiondrive_submissions(fractal_compute_server, specification): """ Test submitting a torsiondrive dataset and computing it. @@ -813,13 +1011,16 @@ def test_torsiondrive_submissions(fractal_compute_server, specification): molecules = Molecule.from_smiles("CO") factory = TorsiondriveDatasetFactory(driver=driver) - factory.add_qc_spec(**qc_spec, spec_name="default", spec_description="test", overwrite=True) + factory.add_qc_spec( + **qc_spec, spec_name="default", spec_description="test", overwrite=True + ) - dataset = factory.create_dataset(dataset_name=f"Test torsiondrives info {program}, {driver}", - molecules=molecules, - description="Test torsiondrive dataset", - tagline="Testing torsiondrive datasets", - ) + dataset = factory.create_dataset( + dataset_name=f"Test torsiondrives info {program}, {driver}", + molecules=molecules, + description="Test torsiondrive dataset", + tagline="Testing torsiondrive datasets", + ) # force a metadata validation error dataset.metadata.long_description = None @@ -872,11 +1073,18 @@ def test_torsiondrive_submissions(fractal_compute_server, specification): assert len(record.final_energy_dict) == 24 -@pytest.mark.parametrize("factory_type", [ - pytest.param(BasicDatasetFactory, id="BasicDataset ignore_errors"), - pytest.param(OptimizationDatasetFactory, id="OptimizationDataset ignore_errors"), - pytest.param(TorsiondriveDatasetFactory, id="TorsiondriveDataset ignore_errors"), -]) +@pytest.mark.parametrize( + "factory_type", + [ + pytest.param(BasicDatasetFactory, id="BasicDataset ignore_errors"), + pytest.param( + OptimizationDatasetFactory, id="OptimizationDataset ignore_errors" + ), + pytest.param( + TorsiondriveDatasetFactory, id="TorsiondriveDataset ignore_errors" + ), + ], +) def test_ignore_errors_all_datasets(fractal_compute_server, factory_type, capsys): """ For each dataset make sure that when the basis is not fully covered the dataset raises warning errors, and verbose information @@ -890,12 +1098,19 @@ def test_ignore_errors_all_datasets(fractal_compute_server, factory_type, capsys factory.add_workflow_components(scan_enum) factory.clear_qcspecs() # add only mm specs - factory.add_qc_spec(method="openff-1.0.0", basis="smirnoff", program="openmm", spec_name="parsley", spec_description="standard parsley spec") - dataset = factory.create_dataset(dataset_name=f"Test ignore_error for {factory.type}", - molecules=molecule, - description="Test ignore errors dataset", - tagline="Testing ignore errors datasets", - ) + factory.add_qc_spec( + method="openff-1.0.0", + basis="smirnoff", + program="openmm", + spec_name="parsley", + spec_description="standard parsley spec", + ) + dataset = factory.create_dataset( + dataset_name=f"Test ignore_error for {factory.type}", + molecules=molecule, + description="Test ignore errors dataset", + tagline="Testing ignore errors datasets", + ) # make sure the dataset raises an error here with pytest.raises(MissingBasisCoverageError): @@ -906,13 +1121,18 @@ def test_ignore_errors_all_datasets(fractal_compute_server, factory_type, capsys dataset.submit(client=client, ignore_errors=True, verbose=True) info = capsys.readouterr() - assert info.out == f"Number of new entries: {dataset.n_records}/{dataset.n_records}\n" + assert ( + info.out == f"Number of new entries: {dataset.n_records}/{dataset.n_records}\n" + ) -@pytest.mark.parametrize("factory_type", [ - pytest.param(BasicDatasetFactory, id="Basicdataset"), - pytest.param(OptimizationDatasetFactory, id="Optimizationdataset") -]) +@pytest.mark.parametrize( + "factory_type", + [ + pytest.param(BasicDatasetFactory, id="Basicdataset"), + pytest.param(OptimizationDatasetFactory, id="Optimizationdataset"), + ], +) def test_index_not_changed(fractal_compute_server, factory_type): """ Make sure that when we submit molecules from a dataset/optimizationdataset with one input conformer that the index is not changed. @@ -921,17 +1141,23 @@ def test_index_not_changed(fractal_compute_server, factory_type): factory.clear_qcspecs() client = FractalClient(fractal_compute_server) # add only mm specs - factory.add_qc_spec(method="openff-1.0.0", basis="smirnoff", program="openmm", spec_name="parsley", - spec_description="standard parsley spec") + factory.add_qc_spec( + method="openff-1.0.0", + basis="smirnoff", + program="openmm", + spec_name="parsley", + spec_description="standard parsley spec", + ) molecule = Molecule.from_smiles("C") # make sure we only have one conformer molecule.generate_conformers(n_conformers=1) - dataset = factory.create_dataset(dataset_name=f"Test index change for {factory.type}", - molecules=molecule, - description="Test index change dataset", - tagline="Testing index changes datasets", - ) + dataset = factory.create_dataset( + dataset_name=f"Test index change for {factory.type}", + molecules=molecule, + description="Test index change dataset", + tagline="Testing index changes datasets", + ) # now change the index name to something unique entry = dataset.dataset.pop(list(dataset.dataset.keys())[0]) @@ -944,16 +1170,21 @@ def test_index_not_changed(fractal_compute_server, factory_type): ds = client.get_collection(dataset.type, dataset.dataset_name) if dataset.type == "DataSet": - query = ds.get_records(method="openff-1.0.0", basis="smirnoff", program="openmm") + query = ds.get_records( + method="openff-1.0.0", basis="smirnoff", program="openmm" + ) assert "my_unique_index" in query.index else: assert "my_unique_index" in ds.df.index -@pytest.mark.parametrize("factory_type", [ - pytest.param(OptimizationDatasetFactory, id="OptimizationDataset index clash"), - pytest.param(TorsiondriveDatasetFactory, id="TorsiondriveDataset index clash"), -]) +@pytest.mark.parametrize( + "factory_type", + [ + pytest.param(OptimizationDatasetFactory, id="OptimizationDataset index clash"), + pytest.param(TorsiondriveDatasetFactory, id="TorsiondriveDataset index clash"), + ], +) def test_adding_dataset_entry_fail(fractal_compute_server, factory_type, capsys): """ Make sure that the new entries is not incremented if we can not add a molecule to the server due to a name clash. @@ -968,30 +1199,52 @@ def test_adding_dataset_entry_fail(fractal_compute_server, factory_type, capsys) factory.add_workflow_components(scan_enum) factory.clear_qcspecs() # add only mm specs - factory.add_qc_spec(method="openff-1.0.0", basis="smirnoff", program="openmm", spec_name="parsley", spec_description="standard parsley spec") - dataset = factory.create_dataset(dataset_name=f"Test index clash for {factory.type}", - molecules=molecule, - description="Test ignore errors dataset", - tagline="Testing ignore errors datasets", - ) + factory.add_qc_spec( + method="openff-1.0.0", + basis="smirnoff", + program="openmm", + spec_name="parsley", + spec_description="standard parsley spec", + ) + dataset = factory.create_dataset( + dataset_name=f"Test index clash for {factory.type}", + molecules=molecule, + description="Test ignore errors dataset", + tagline="Testing ignore errors datasets", + ) # make sure all expected index get submitted dataset.submit(client=client, verbose=True) info = capsys.readouterr() - assert info.out == f"Number of new entries: {dataset.n_records}/{dataset.n_records}\n" + assert ( + info.out == f"Number of new entries: {dataset.n_records}/{dataset.n_records}\n" + ) # now add a new spec and try and submit again dataset.clear_qcspecs() - dataset.add_qc_spec(method="mmff94", basis=None, program="rdkit", spec_name="mff94", spec_description="mff94 force field in rdkit") + dataset.add_qc_spec( + method="mmff94", + basis=None, + program="rdkit", + spec_name="mff94", + spec_description="mff94 force field in rdkit", + ) dataset.submit(client=client, verbose=True) info = capsys.readouterr() assert info.out == f"Number of new entries: 0/{dataset.n_records}\n" -@pytest.mark.parametrize("factory_type", [ - pytest.param(OptimizationDatasetFactory, id="OptimizationDataset expand compute"), - pytest.param(TorsiondriveDatasetFactory, id="TorsiondriveDataset expand compute"), -]) +@pytest.mark.parametrize( + "factory_type", + [ + pytest.param( + OptimizationDatasetFactory, id="OptimizationDataset expand compute" + ), + pytest.param( + TorsiondriveDatasetFactory, id="TorsiondriveDataset expand compute" + ), + ], +) def test_expanding_compute(fractal_compute_server, factory_type): """ Make sure that if we expand the compute of a dataset tasks are generated. @@ -1005,13 +1258,19 @@ def test_expanding_compute(fractal_compute_server, factory_type): factory.add_workflow_components(scan_enum) factory.clear_qcspecs() # add only mm specs - factory.add_qc_spec(method="openff-1.0.0", basis="smirnoff", program="openmm", spec_name="default", - spec_description="standard parsley spec") - dataset = factory.create_dataset(dataset_name=f"Test compute expand {factory.type}", - molecules=molecule, - description="Test compute expansion", - tagline="Testing compute expansion", - ) + factory.add_qc_spec( + method="openff-1.0.0", + basis="smirnoff", + program="openmm", + spec_name="default", + spec_description="standard parsley spec", + ) + dataset = factory.create_dataset( + dataset_name=f"Test compute expand {factory.type}", + molecules=molecule, + description="Test compute expansion", + tagline="Testing compute expansion", + ) # make sure all expected index get submitted dataset.submit(client=client) @@ -1022,13 +1281,19 @@ def test_expanding_compute(fractal_compute_server, factory_type): # now make another dataset to expand the compute factory.clear_qcspecs() # add only mm specs - factory.add_qc_spec(method="openff-1.2.0", basis="smirnoff", program="openmm", spec_name="parsley2", - spec_description="standard parsley spec") - dataset = factory.create_dataset(dataset_name=f"Test compute expand {factory.type}", - molecules=[], - description="Test compute expansion", - tagline="Testing compute expansion", - ) + factory.add_qc_spec( + method="openff-1.2.0", + basis="smirnoff", + program="openmm", + spec_name="parsley2", + spec_description="standard parsley spec", + ) + dataset = factory.create_dataset( + dataset_name=f"Test compute expand {factory.type}", + molecules=[], + description="Test compute expansion", + tagline="Testing compute expansion", + ) # now submit again dataset.submit(client=client) diff --git a/openff/qcsubmit/tests/test_workflow_components.py b/openff/qcsubmit/tests/test_workflow_components.py index 7f08c142..026106c2 100644 --- a/openff/qcsubmit/tests/test_workflow_components.py +++ b/openff/qcsubmit/tests/test_workflow_components.py @@ -40,15 +40,20 @@ def get_container(molecules: [Molecule]) -> ComponentResult: """ return ComponentResult( - component_name="intial", component_description={"description": "initial filter"}, molecules=molecules, - component_provenance={"test": "test component"}) + component_name="intial", + component_description={"description": "initial filter"}, + molecules=molecules, + component_provenance={"test": "test component"}, + ) def get_stereoisomers(): """ Get a set of molecules that all have some undefined stereochemistry. """ - mols = Molecule.from_file(get_data("stereoisomers.smi"), allow_undefined_stereo=True) + mols = Molecule.from_file( + get_data("stereoisomers.smi"), allow_undefined_stereo=True + ) return mols @@ -58,7 +63,9 @@ def get_tautomers(): Get a set of molecules that all have tautomers """ - mols = Molecule.from_file(get_data("tautomers_small.smi"), allow_undefined_stereo=True) + mols = Molecule.from_file( + get_data("tautomers_small.smi"), allow_undefined_stereo=True + ) return mols @@ -80,7 +87,9 @@ def test_register_component_replace(): gen = workflow_components.StandardConformerGenerator with pytest.raises(ComponentRegisterError): - register_component(component=workflow_components.StandardConformerGenerator, replace=False) + register_component( + component=workflow_components.StandardConformerGenerator, replace=False + ) # now register using replace with a new default register_component(component=gen, replace=True) @@ -97,9 +106,12 @@ def test_register_component_error(): register_component(component=charge_filter) -@pytest.mark.parametrize("component", [ - pytest.param(workflow_components.SmartsFilter, id="Class instance"), -]) +@pytest.mark.parametrize( + "component", + [ + pytest.param(workflow_components.SmartsFilter, id="Class instance"), + ], +) def test_deregister_component(component): """ Make sure we can deregister components via name or class. @@ -148,7 +160,6 @@ def test_custom_component(): test = CustomWorkflowComponent() class TestComponent(CustomWorkflowComponent): - component_name: Literal["TestComponent"] = "TestComponent" @classmethod @@ -163,7 +174,9 @@ def fail_reason(cls) -> str: def properties(cls) -> ComponentProperties: return ComponentProperties(process_parallel=True, produces_duplicates=True) - def _apply(self, molecules: List[Molecule], toolkit_registry) -> ComponentResult: + def _apply( + self, molecules: List[Molecule], toolkit_registry + ) -> ComponentResult: pass def provenance(self, toolkit_registry) -> Dict: @@ -178,7 +191,11 @@ def is_available(cls) -> bool: assert test.description() == "Test component" assert test.fail_reason() == "Test fail" assert {"test": "version1"} == test.provenance(GLOBAL_TOOLKIT_REGISTRY) - assert TestComponent.info() == {"name": "TestComponent", "description": "Test component", "fail_reason": "Test fail"} + assert TestComponent.info() == { + "name": "TestComponent", + "description": "Test component", + "fail_reason": "Test fail", + } def test_toolkit_mixin(): @@ -204,7 +221,9 @@ def fail_reason(cls) -> str: def properties(cls) -> ComponentProperties: return ComponentProperties(process_parallel=True, produces_duplicates=True) - def _apply(self, molecules: List[Molecule], toolkit_registry) -> ComponentResult: + def _apply( + self, molecules: List[Molecule], toolkit_registry + ) -> ComponentResult: pass test = TestClass() @@ -261,10 +280,17 @@ def test_weight_filter_validator(): assert "openff-toolkit" in weight.provenance(GLOBAL_TOOLKIT_REGISTRY) -@pytest.mark.parametrize("toolkits", [ - pytest.param(ToolkitRegistry(toolkit_precedence=[RDKitToolkitWrapper()]), id="rdkit"), - pytest.param(ToolkitRegistry(toolkit_precedence=[OpenEyeToolkitWrapper()]), id="openeye") -]) +@pytest.mark.parametrize( + "toolkits", + [ + pytest.param( + ToolkitRegistry(toolkit_precedence=[RDKitToolkitWrapper()]), id="rdkit" + ), + pytest.param( + ToolkitRegistry(toolkit_precedence=[OpenEyeToolkitWrapper()]), id="openeye" + ), + ], +) def test_weight_filter_apply(toolkits): """ Make sure the weight filter returns molecules within the limits. As the backend function choice is handled by qcsubmit @@ -289,21 +315,48 @@ def test_weight_filter_apply_no_toolkit(): with pytest.raises(ModuleNotFoundError): weight = workflow_components.MolecularWeightFilter() molecules = get_tautomers() - _ = weight.apply(molecules=molecules, toolkit_registry=ToolkitRegistry(), processors=1) + _ = weight.apply( + molecules=molecules, toolkit_registry=ToolkitRegistry(), processors=1 + ) @pytest.mark.parametrize( "data", [ - pytest.param((workflow_components.ElementFilter, "allowed_elements", [1, 10, 100]), id="ElementFilter"), - pytest.param((workflow_components.MolecularWeightFilter, "minimum_weight", 1), id="WeightFilter"), - pytest.param((workflow_components.StandardConformerGenerator, "max_conformers", 1), id="StandardConformers"), - pytest.param((workflow_components.EnumerateTautomers, "max_tautomers", 2), id="EnumerateTautomers"), - pytest.param((workflow_components.EnumerateStereoisomers, "undefined_only", True), id="EnumerateStereoisomers"), - pytest.param((workflow_components.RotorFilter, "maximum_rotors", 3), id="RotorFilter"), - pytest.param((workflow_components.WBOFragmenter, "threshold", 0.5), id="WBOFragmenter"), - pytest.param((workflow_components.EnumerateProtomers, "max_states", 5), id="EnumerateProtomers"), - pytest.param((workflow_components.RMSDCutoffConformerFilter, "cutoff", 1.2), id="RMSDCutoffConformerFilter") + pytest.param( + (workflow_components.ElementFilter, "allowed_elements", [1, 10, 100]), + id="ElementFilter", + ), + pytest.param( + (workflow_components.MolecularWeightFilter, "minimum_weight", 1), + id="WeightFilter", + ), + pytest.param( + (workflow_components.StandardConformerGenerator, "max_conformers", 1), + id="StandardConformers", + ), + pytest.param( + (workflow_components.EnumerateTautomers, "max_tautomers", 2), + id="EnumerateTautomers", + ), + pytest.param( + (workflow_components.EnumerateStereoisomers, "undefined_only", True), + id="EnumerateStereoisomers", + ), + pytest.param( + (workflow_components.RotorFilter, "maximum_rotors", 3), id="RotorFilter" + ), + pytest.param( + (workflow_components.WBOFragmenter, "threshold", 0.5), id="WBOFragmenter" + ), + pytest.param( + (workflow_components.EnumerateProtomers, "max_states", 5), + id="EnumerateProtomers", + ), + pytest.param( + (workflow_components.RMSDCutoffConformerFilter, "cutoff", 1.2), + id="RMSDCutoffConformerFilter", + ), ], ) def test_to_from_object(data): @@ -334,9 +387,19 @@ def test_rmsd_filter(): rmsd_filter = workflow_components.RMSDCutoffConformerFilter(cutoff=1) mol = Molecule.from_smiles("CCCC") # make a lot of conformers for the molecule - mol.generate_conformers(n_conformers=1000, rms_cutoff=0.05 * unit.angstrom, toolkit_registry=RDKitToolkitWrapper()) + mol.generate_conformers( + n_conformers=1000, + rms_cutoff=0.05 * unit.angstrom, + toolkit_registry=RDKitToolkitWrapper(), + ) ref_mol = copy.deepcopy(mol) - result = rmsd_filter.apply([mol, ], processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY) + result = rmsd_filter.apply( + [ + mol, + ], + processors=1, + toolkit_registry=GLOBAL_TOOLKIT_REGISTRY, + ) # now make sure the number of conformers is different assert result.molecules[0].n_conformers != ref_mol.n_conformers @@ -347,7 +410,13 @@ def test_rmsd_filter_no_conformers(): """ rmsd_filter = workflow_components.RMSDCutoffConformerFilter(cutoff=1) mol = Molecule.from_smiles("CCCC") - result = rmsd_filter.apply([mol, ], processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY) + result = rmsd_filter.apply( + [ + mol, + ], + processors=1, + toolkit_registry=GLOBAL_TOOLKIT_REGISTRY, + ) # now make sure the number of conformers is different assert result.n_molecules == 0 assert result.n_filtered == 1 @@ -366,7 +435,11 @@ def test_conformer_apply(): # remove duplicates from the set molecule_container = get_container(mols) - result = conf_gen.apply(molecule_container.molecules, processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY) + result = conf_gen.apply( + molecule_container.molecules, + processors=1, + toolkit_registry=GLOBAL_TOOLKIT_REGISTRY, + ) assert result.component_name == conf_gen.type assert result.component_description == conf_gen.dict() @@ -388,7 +461,9 @@ def test_elementfilter_apply(): mols = get_tautomers() - result = elem_filter.apply(mols, processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY) + result = elem_filter.apply( + mols, processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY + ) assert result.component_name == elem_filter.type assert result.component_description == elem_filter.dict() @@ -427,13 +502,20 @@ def test_enumerating_stereoisomers_apply(): mols = get_stereoisomers() - result = enumerate_stereo.apply(mols, processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY) + result = enumerate_stereo.apply( + mols, processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY + ) for mol in mols: assert mol in result.molecules # make sure no molecules have undefined stereo for molecule in result.molecules: - assert Molecule.from_smiles(molecule.to_smiles(), toolkit_registry=RDKitToolkitWrapper()) == molecule + assert ( + Molecule.from_smiles( + molecule.to_smiles(), toolkit_registry=RDKitToolkitWrapper() + ) + == molecule + ) assert molecule.n_conformers >= 1 @@ -442,19 +524,33 @@ def test_enumerating_stereoisomers_poor_input(): Test stereoisomer enumeration with an impossible stereochemistry. """ - molecule = Molecule.from_smiles("C=CCn1c([C@@H]2C[C@@H]3CC[C@@H]2O3)nnc1N1CCN(c2ccccc2)CC1") + molecule = Molecule.from_smiles( + "C=CCn1c([C@@H]2C[C@@H]3CC[C@@H]2O3)nnc1N1CCN(c2ccccc2)CC1" + ) enumerate_stereo = workflow_components.EnumerateStereoisomers() # the molecule should fail conformer generation enumerate_stereo.undefined_only = True enumerate_stereo.rationalise = True - result = enumerate_stereo.apply(molecules=[molecule, ], processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY) + result = enumerate_stereo.apply( + molecules=[ + molecule, + ], + processors=1, + toolkit_registry=GLOBAL_TOOLKIT_REGISTRY, + ) assert result.n_molecules == 0 assert result.n_filtered == 1 # now turn of rationalise enumerate_stereo.rationalise = False - result = enumerate_stereo.apply([molecule, ], processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY) + result = enumerate_stereo.apply( + [ + molecule, + ], + processors=1, + toolkit_registry=GLOBAL_TOOLKIT_REGISTRY, + ) assert molecule in result.molecules assert result.n_molecules == 1 @@ -463,15 +559,30 @@ def test_enumerating_stereoisomers_poor_input(): enumerate_stereo.undefined_only = False # make sure the input is missing and new isomers are found - result = enumerate_stereo.apply([molecule, ], processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY) + result = enumerate_stereo.apply( + [ + molecule, + ], + processors=1, + toolkit_registry=GLOBAL_TOOLKIT_REGISTRY, + ) assert molecule not in result.molecules assert molecule in result.filtered -@pytest.mark.parametrize("data", [ - pytest.param(("[H]C(=C([H])Cl)Cl", True), id="Molecule with missing stereo"), - pytest.param(("[H]c1c(c(c(c(c1N([C@@]([H])(O[H])SC([H])([H])[C@]([H])(C(=O)N([H])C([H])([H])C(=O)O[H])N([H])C(=O)C([H])([H])C([H])([H])[C@@]([H])(C(=O)O[H])N([H])[H])O[H])[H])[H])I)[H]", False), id="Molecule with good stereo") -]) +@pytest.mark.parametrize( + "data", + [ + pytest.param(("[H]C(=C([H])Cl)Cl", True), id="Molecule with missing stereo"), + pytest.param( + ( + "[H]c1c(c(c(c(c1N([C@@]([H])(O[H])SC([H])([H])[C@]([H])(C(=O)N([H])C([H])([H])C(=O)O[H])N([H])C(=O)C([H])([H])C([H])([H])[C@@]([H])(C(=O)O[H])N([H])[H])O[H])[H])[H])I)[H]", + False, + ), + id="Molecule with good stereo", + ), + ], +) def test_check_missing_stereo(data): """ Make sure that molecules with missing stereo chem are correctly identified. @@ -490,7 +601,9 @@ def test_enumerating_tautomers_apply(): mols = get_tautomers() - result = enumerate_tauts.apply(mols, processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY) + result = enumerate_tauts.apply( + mols, processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY + ) # check the input molecule is present for mol in mols: @@ -517,8 +630,14 @@ def test_enumerating_protomers_apply(): enumerate_protomers = workflow_components.EnumerateProtomers(max_states=2) assert enumerate_protomers.is_available() - mol = Molecule.from_smiles('Oc2ccc(c1ccncc1)cc2') - result = enumerate_protomers.apply([mol, ], processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY) + mol = Molecule.from_smiles("Oc2ccc(c1ccncc1)cc2") + result = enumerate_protomers.apply( + [ + mol, + ], + processors=1, + toolkit_registry=GLOBAL_TOOLKIT_REGISTRY, + ) assert mol in result.molecules # this means that the parent molecule was included @@ -536,7 +655,11 @@ def test_coverage_filter_remove(): # we have to remove duplicated records # remove duplicates from the set molecule_container = get_container(mols) - result = coverage_filter.apply(molecule_container.molecules, processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY) + result = coverage_filter.apply( + molecule_container.molecules, + processors=1, + toolkit_registry=GLOBAL_TOOLKIT_REGISTRY, + ) forcefield = ForceField("openff_unconstrained-1.0.0.offxml") # now see if any molecules do not have b83 @@ -560,7 +683,11 @@ def test_coverage_filter_allowed(): # we have to remove duplicated records # remove duplicates from the set molecule_container = get_container(mols) - result = coverage_filter.apply(molecule_container.molecules, processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY) + result = coverage_filter.apply( + molecule_container.molecules, + processors=1, + toolkit_registry=GLOBAL_TOOLKIT_REGISTRY, + ) forcefield = ForceField("openff_unconstrained-1.0.0.offxml") # now see if any molecules do not have b83 @@ -591,7 +718,11 @@ def test_coverage_allowed_no_match(): # we have to remove duplicated records # remove duplicates from the set molecule_container = get_container(mols) - result = coverage_filter.apply(molecule_container.molecules, processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY) + result = coverage_filter.apply( + molecule_container.molecules, + processors=1, + toolkit_registry=GLOBAL_TOOLKIT_REGISTRY, + ) # make sure all molecules have been removed as none have the made up id assert result.n_molecules == 0 @@ -614,12 +745,24 @@ def test_wbo_fragmentation_apply(): assert fragmenter.is_available() # check that a molecule with no rotatable bonds fails if we dont want the parent back benzene = Molecule.from_file(get_data("benzene.sdf"), "sdf") - result = fragmenter.apply([benzene, ], processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY) + result = fragmenter.apply( + [ + benzene, + ], + processors=1, + toolkit_registry=GLOBAL_TOOLKIT_REGISTRY, + ) assert result.n_molecules == 0 # now try a molecule which should give fragments diphenhydramine = Molecule.from_smiles("O(CCN(C)C)C(c1ccccc1)c2ccccc2") - result = fragmenter.apply([diphenhydramine, ], processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY) + result = fragmenter.apply( + [ + diphenhydramine, + ], + processors=1, + toolkit_registry=GLOBAL_TOOLKIT_REGISTRY, + ) assert result.n_molecules == 4 for molecule in result.molecules: assert "dihedrals" in molecule.properties @@ -632,7 +775,13 @@ def test_pfizer_fragmentation_apply(): assert fragmenter.is_available() # now try a molecule which should give fragments diphenhydramine = Molecule.from_smiles("O(CCN(C)C)C(c1ccccc1)c2ccccc2") - result = fragmenter.apply([diphenhydramine, ], processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY) + result = fragmenter.apply( + [ + diphenhydramine, + ], + processors=1, + toolkit_registry=GLOBAL_TOOLKIT_REGISTRY, + ) assert result.n_molecules == 4 for molecule in result.molecules: assert "dihedrals" in molecule.properties @@ -650,7 +799,11 @@ def test_rotor_filter_maximum(): # we have to remove duplicated records molecule_container = get_container(mols) - result = rotor_filter.apply(molecule_container.molecules, processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY) + result = rotor_filter.apply( + molecule_container.molecules, + processors=1, + toolkit_registry=GLOBAL_TOOLKIT_REGISTRY, + ) for molecule in result.molecules: assert len(molecule.find_rotatable_bonds()) <= rotor_filter.maximum_rotors @@ -666,7 +819,9 @@ def test_rotor_filter_minimum(): mols = get_tautomers() mol_container = get_container(mols) - result = rotor_filter.apply(mol_container.molecules, processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY) + result = rotor_filter.apply( + mol_container.molecules, processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY + ) for molecule in result.molecules: assert len(molecule.find_rotatable_bonds()) >= rotor_filter.minimum_rotors for molecule in result.filtered: @@ -683,10 +838,16 @@ def test_rotor_filter_validation(): rotor_filter.minimum_rotors = 4 mols = get_tautomers() mol_container = get_container(mols) - rotor_filter.apply(mol_container.molecules, processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY) + rotor_filter.apply( + mol_container.molecules, processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY + ) rotor_filter.minimum_rotors = 5 with pytest.raises(ValueError): - rotor_filter.apply(mol_container.molecules, processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY) + rotor_filter.apply( + mol_container.molecules, + processors=1, + toolkit_registry=GLOBAL_TOOLKIT_REGISTRY, + ) def test_rotor_filter_fail(): @@ -700,7 +861,11 @@ def test_rotor_filter_fail(): mols = get_tautomers() molecule_container = get_container(mols) - result = rotor_filter.apply(molecule_container.molecules, processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY) + result = rotor_filter.apply( + molecule_container.molecules, + processors=1, + toolkit_registry=GLOBAL_TOOLKIT_REGISTRY, + ) for molecule in result.molecules: assert len(molecule.find_rotatable_bonds()) <= rotor_filter.maximum_rotors for molecule in result.filtered: @@ -715,7 +880,9 @@ def test_smarts_filter_validator(): from openff.toolkit.typing.chemistry import SMIRKSParsingError with pytest.raises(ValidationError): - workflow_components.SmartsFilter(allowed_substructures=["[C:1]"], filtered_substructures=["[N:1]"]) + workflow_components.SmartsFilter( + allowed_substructures=["[C:1]"], filtered_substructures=["[N:1]"] + ) with pytest.raises(SMIRKSParsingError): workflow_components.SmartsFilter(allowed_substructures=[1, 2, 3, 4]) @@ -726,14 +893,18 @@ def test_smarts_filter_validator(): with pytest.raises(SMIRKSParsingError): # make sure each item is checked - workflow_components.SmartsFilter(allowed_substructures=["[C:1]-[C:2]", "ksbfsb"]) + workflow_components.SmartsFilter( + allowed_substructures=["[C:1]-[C:2]", "ksbfsb"] + ) with pytest.raises(SMIRKSParsingError): # good smarts with no tagged atoms. workflow_components.SmartsFilter(allowed_substructures=["[C]=[C]"]) # a good search string - smart_filter = workflow_components.SmartsFilter(allowed_substructures=["[C:1]=[C:2]"]) + smart_filter = workflow_components.SmartsFilter( + allowed_substructures=["[C:1]=[C:2]"] + ) assert len(smart_filter.allowed_substructures) == 1 @@ -743,12 +914,16 @@ def test_smarts_filter_allowed(): """ # this should only allow C, H, N, O containing molecules through similar to the element filter - filter = workflow_components.SmartsFilter(allowed_substructures=["[C:1]", "[c:1]", "[H:1]", "[O:1]", "[N:1]"]) + filter = workflow_components.SmartsFilter( + allowed_substructures=["[C:1]", "[c:1]", "[H:1]", "[O:1]", "[N:1]"] + ) allowed_elements = [1, 6, 7, 8] molecules = get_tautomers() - result = filter.apply(molecules, processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY) + result = filter.apply( + molecules, processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY + ) assert result.component_name == filter.type assert result.component_description == filter.dict() @@ -772,7 +947,9 @@ def test_smarts_filter_allowed_no_match(): molecules = get_tautomers() - result = filter.apply(molecules, processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY) + result = filter.apply( + molecules, processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY + ) assert result.n_molecules == 0 @@ -781,12 +958,25 @@ def test_smarts_filter_remove(): Make sure we can correctly remove any molecules which have unwanted substructures. """ - filter = workflow_components.SmartsFilter(filtered_substructures=["[Cl:1]", "[F:1]", "[P:1]", "[Br:1]", "[S:1]", "[I:1]", "[B:1]", "[#7:1]"]) + filter = workflow_components.SmartsFilter( + filtered_substructures=[ + "[Cl:1]", + "[F:1]", + "[P:1]", + "[Br:1]", + "[S:1]", + "[I:1]", + "[B:1]", + "[#7:1]", + ] + ) allowed_elements = [1, 6, 8] molecules = get_tautomers() - result = filter.apply(molecules, processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY) + result = filter.apply( + molecules, processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY + ) assert result.component_name == filter.type assert result.component_description == filter.dict() @@ -805,7 +995,10 @@ def test_scan_filter_mutually_exclusive(): Make sure an error is raised when both options are passed. """ with pytest.raises(ValidationError): - workflow_components.ScanFilter(scans_to_include=["[*:1]~[*:2]:[*:3]~[*:4]"], scans_to_exclude=["[*:1]~[*:2]-[CH3:3]-[H:4]"]) + workflow_components.ScanFilter( + scans_to_include=["[*:1]~[*:2]:[*:3]~[*:4]"], + scans_to_exclude=["[*:1]~[*:2]-[CH3:3]-[H:4]"], + ) def test_scan_filter_no_torsions(): @@ -815,9 +1008,13 @@ def test_scan_filter_no_torsions(): molecule = Molecule.from_smiles("CC") - filter = workflow_components.ScanFilter(scans_to_include=["[*:1]~[*:2]-[CH3:3]-[H:4]"]) + filter = workflow_components.ScanFilter( + scans_to_include=["[*:1]~[*:2]-[CH3:3]-[H:4]"] + ) - result = filter.apply(molecules=[molecule], processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY) + result = filter.apply( + molecules=[molecule], processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY + ) assert result.n_molecules == 0 @@ -834,9 +1031,13 @@ def test_scan_filter_include(): ethanol.properties["dihedrals"] = t_indexer # only keep the C-C scan - filter = workflow_components.ScanFilter(scans_to_include=["[#1:1]~[#6:2]-[#6:3]-[#1:4]"]) + filter = workflow_components.ScanFilter( + scans_to_include=["[#1:1]~[#6:2]-[#6:3]-[#1:4]"] + ) - result = filter.apply(molecules=[ethanol], processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY) + result = filter.apply( + molecules=[ethanol], processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY + ) assert result.n_molecules == 1 assert (0, 1) in ethanol.properties["dihedrals"].torsions @@ -855,9 +1056,13 @@ def test_scan_filter_exclude(): ethanol.properties["dihedrals"] = t_indexer # only keep the C-C scan - filter = workflow_components.ScanFilter(scans_to_exclude=["[#1:1]~[#6:2]-[#6:3]-[#1:4]"]) + filter = workflow_components.ScanFilter( + scans_to_exclude=["[#1:1]~[#6:2]-[#6:3]-[#1:4]"] + ) - result = filter.apply(molecules=[ethanol], processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY) + result = filter.apply( + molecules=[ethanol], processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY + ) assert result.n_molecules == 1 assert (1, 2) in ethanol.properties["dihedrals"].torsions @@ -870,9 +1075,13 @@ def test_scan_enumerator_no_scans(): mol = Molecule.from_smiles("CC") scan_tagger = workflow_components.ScanEnumerator() - scan_tagger.add_torsion_scan(smarts="[*:1]~[#8:1]-[#6:3]~[*:4]", scan_rage=(-40, 40), scan_increment=15) + scan_tagger.add_torsion_scan( + smarts="[*:1]~[#8:1]-[#6:3]~[*:4]", scan_rage=(-40, 40), scan_increment=15 + ) - result = scan_tagger.apply([mol], processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY) + result = scan_tagger.apply( + [mol], processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY + ) assert result.n_molecules == 0 assert result.n_filtered == 1 @@ -885,9 +1094,13 @@ def test_scan_enumerator_1d(): mol = Molecule.from_smiles("CCC") scan_tagger = workflow_components.ScanEnumerator() - scan_tagger.add_torsion_scan(smarts="[*:1]~[#6:2]-[#6:3]~[*:4]", scan_rage=(-60, 60), scan_increment=20) + scan_tagger.add_torsion_scan( + smarts="[*:1]~[#6:2]-[#6:3]~[*:4]", scan_rage=(-60, 60), scan_increment=20 + ) - result = scan_tagger.apply([mol], processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY) + result = scan_tagger.apply( + [mol], processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY + ) assert result.n_molecules == 1 indexer = mol.properties["dihedrals"] @@ -904,7 +1117,9 @@ def test_scan_enumerator_unique(): scan_tagger = workflow_components.ScanEnumerator() scan_tagger.add_torsion_scan(smarts="[*:1]~[#6:2]-[#6:3]~[*:4]") - result = scan_tagger.apply(molecules=[mol], processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY) + result = scan_tagger.apply( + molecules=[mol], processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY + ) assert result.n_molecules == 1 indexer = mol.properties["dihedrals"] @@ -919,12 +1134,17 @@ def test_scan_enumerator_2d(): mol = Molecule.from_smiles("COc1ccc(cc1)N") scan_tagger = workflow_components.ScanEnumerator() - scan_tagger.add_double_torsion(smarts1="[*:1]-[#7X3+0:2]-[#6:3]@[#6,#7:4]", smarts2="[#7X3+0:1](-[*:3])(-[*:4])-[#6:2]@[#6,#7]", - scan_range1=(-165, 180), - scan_range2=(-60, 60), - scan_increments=[15, 4]) - - result = scan_tagger.apply([mol], processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY) + scan_tagger.add_double_torsion( + smarts1="[*:1]-[#7X3+0:2]-[#6:3]@[#6,#7:4]", + smarts2="[#7X3+0:1](-[*:3])(-[*:4])-[#6:2]@[#6,#7]", + scan_range1=(-165, 180), + scan_range2=(-60, 60), + scan_increments=[15, 4], + ) + + result = scan_tagger.apply( + [mol], processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY + ) assert result.n_molecules == 1 indexer = mol.properties["dihedrals"] assert indexer.n_double_torsions == 1 @@ -941,9 +1161,16 @@ def test_improper_enumerator(): scan_tagger = workflow_components.ScanEnumerator() # even though there is more than one improper make sure we only get one scan back - scan_tagger.add_improper_torsion(smarts="[#6:1](-[#1:2])(:[#6:3]):[#6:4]", central_smarts="[#6:1]", scan_range=(-40, 40), scan_increment=4) + scan_tagger.add_improper_torsion( + smarts="[#6:1](-[#1:2])(:[#6:3]):[#6:4]", + central_smarts="[#6:1]", + scan_range=(-40, 40), + scan_increment=4, + ) - result = scan_tagger.apply([mol], processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY) + result = scan_tagger.apply( + [mol], processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY + ) assert result.n_molecules == 1 indexer = mol.properties["dihedrals"] @@ -957,7 +1184,9 @@ def test_formal_charge_filter_exclusive(): """ with pytest.raises(ValidationError): - workflow_components.ChargeFilter(charges_to_include=[0, 1], charges_to_exclude=[-1]) + workflow_components.ChargeFilter( + charges_to_include=[0, 1], charges_to_exclude=[-1] + ) def test_formal_charge_filter(): @@ -969,11 +1198,15 @@ def test_formal_charge_filter(): # filter out the molecule charge_filter = workflow_components.ChargeFilter(charges_to_exclude=[-1, 0]) - result = charge_filter.apply([molecule], processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY) + result = charge_filter.apply( + [molecule], processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY + ) assert result.n_molecules == 0 assert result.n_filtered == 1 # now allow it through charge_filter = workflow_components.ChargeFilter(charges_to_include=[-1]) - result = charge_filter.apply([molecule], processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY) + result = charge_filter.apply( + [molecule], processors=1, toolkit_registry=GLOBAL_TOOLKIT_REGISTRY + ) assert result.n_molecules == 1 diff --git a/openff/qcsubmit/tests/utils/test_visualize.py b/openff/qcsubmit/tests/utils/test_visualize.py index 4da83b9a..7c377ffb 100644 --- a/openff/qcsubmit/tests/utils/test_visualize.py +++ b/openff/qcsubmit/tests/utils/test_visualize.py @@ -14,7 +14,6 @@ "create_function", [molecules_to_pdf, _create_openeye_pdf, _create_rdkit_pdf] ) def test_create_pdf_function(tmpdir, create_function): - molecules = [ Molecule.from_smiles("C"), Molecule.from_smiles("CC"), @@ -31,7 +30,6 @@ def test_create_pdf_function(tmpdir, create_function): def test_molecules_to_pdf_bad_toolkit(): - with pytest.raises(ValueError, match="is not supported, chose"): # noinspection PyTypeChecker molecules_to_pdf([], "", toolkit="fake-toolkit")