Skip to content

Commit

Permalink
Tests: Standard suite of tests and moves around to distinguish betwee…
Browse files Browse the repository at this point in the history
…n standard set and program specific
  • Loading branch information
dgasmith committed Feb 28, 2019
1 parent 16f6f91 commit 29e6973
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 61 deletions.
86 changes: 63 additions & 23 deletions qcengine/programs/tests/test_dftd3.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,37 @@

import copy

import numpy as np
import pytest
import qcelemental as qcel
from qcelemental.testing import compare, compare_recursive, compare_values, tnm

import qcengine as qcng
from qcengine.programs import dftd3
from qcengine.testing import is_psi4_new_enough, using_dftd3, using_dftd3_321, using_psi4, using_qcdb


@using_dftd3
@pytest.mark.parametrize("method", [
"b3lyp-d3",
"b3lyp-d3m",
"b3lyp-d3bj",
"b3lyp-d3mbj",
])
def test_dftd3_task(method):
json_data = {"molecule": qcng.get_molecule("eneyne"), "driver": "energy", "model": {"method": method}}

ret = qcng.compute(json_data, "dftd3", raise_error=True, return_dict=True)

assert ret["driver"] == "energy"
assert "provenance" in ret
assert "normal termination of dftd3" in ret["stdout"]

for key in ["cpu", "hostname", "username", "wall_time"]:
assert key in ret["provenance"]

assert ret["success"] is True


## Resources

ref = {}
Expand Down Expand Up @@ -436,7 +459,8 @@ def eneyne_ne_qcschemamols():
ne = qcel.molparse.to_schema(qcel.molparse.from_string(sne)['qm'], dtype=1)

mAgB = qcel.molparse.from_string(seneyne)['qm']
mAgB['real'] = [(iat < mAgB['fragment_separators'][0]) for iat in range(len(mAgB['elem']))] # works b/c chgmult doesn't need refiguring
mAgB['real'] = [(iat < mAgB['fragment_separators'][0])
for iat in range(len(mAgB['elem']))] # works b/c chgmult doesn't need refiguring
mAgB = qcel.molparse.to_schema(mAgB, dtype=1)

gAmB = qcel.molparse.from_string(seneyne)['qm']
Expand Down Expand Up @@ -531,8 +555,7 @@ def test_dftd3__from_arrays(inp, expected):
res = dftd3.from_arrays(**inp[0])
assert compare_recursive(expected, res, atol=1.e-4)
assert compare(inp[1], _compute_key(res), 'key')
res = dftd3.from_arrays(
name_hint=res['fctldash'], level_hint=res['dashlevel'], param_tweaks=res['dashparams'])
res = dftd3.from_arrays(name_hint=res['fctldash'], level_hint=res['dashlevel'], param_tweaks=res['dashparams'])
assert compare_recursive(expected, res, tnm() + ' idempotent', atol=1.e-4)


Expand Down Expand Up @@ -583,9 +606,11 @@ def test_3():

@using_dftd3
@pytest.mark.parametrize(
"subjects", [
"subjects",
[
pytest.param(eneyne_ne_psi4mols, marks=using_psi4),
pytest.param(eneyne_ne_qcdbmols, marks=using_psi4), # needs qcdb.Molecule, presently more common in psi4 than in qcdb
pytest.param(eneyne_ne_qcdbmols,
marks=using_psi4), # needs qcdb.Molecule, presently more common in psi4 than in qcdb
],
ids=['qmol', 'pmol'])
@pytest.mark.parametrize(
Expand Down Expand Up @@ -638,9 +663,11 @@ def test_qcdb__energy_d3():

@using_dftd3
@pytest.mark.parametrize(
"subjects", [
"subjects",
[
pytest.param(eneyne_ne_psi4mols, marks=using_psi4),
pytest.param(eneyne_ne_qcdbmols, marks=using_psi4), # needs qcdb.Molecule, presently more common in psi4 than in qcdb
pytest.param(eneyne_ne_qcdbmols,
marks=using_psi4), # needs qcdb.Molecule, presently more common in psi4 than in qcdb
pytest.param(eneyne_ne_qcschemamols),
],
ids=['qmol', 'pmol', 'qcmol'])
Expand All @@ -658,11 +685,15 @@ def test_dftd3__run_dftd3__2body(inp, subjects, request):
gexpected = gref[inp['parent']][inp['lbl']][inp['subject']].ravel()

if 'qcmol' in request.node.name:
subject.update({'model': {'method': inp['name']},
'driver': 'gradient',
'keywords': {},
'schema_name': 'qcschema_input',
'schema_version': 1})
subject.update({
'model': {
'method': inp['name']
},
'driver': 'gradient',
'keywords': {},
'schema_name': 'qcschema_input',
'schema_version': 1
})
jrec = dftd3.run_json(subject)
else:
jrec = dftd3.run_dftd3(inp['name'], subject, options={}, ptype='gradient')
Expand All @@ -677,14 +708,17 @@ def test_dftd3__run_dftd3__2body(inp, subjects, request):
assert compare_values(gexpected, jrec['extras']['qcvars']['CURRENT GRADIENT'], atol=1.e-7)
assert compare_values(gexpected, jrec['extras']['qcvars']['DISPERSION CORRECTION GRADIENT'], atol=1.e-7)
assert compare_values(gexpected, jrec['extras']['qcvars']['2-BODY DISPERSION CORRECTION GRADIENT'], atol=1.e-7)
assert compare_values(gexpected, jrec['extras']['qcvars'][inp['lbl'] + ' DISPERSION CORRECTION GRADIENT'], atol=1.e-7)
assert compare_values(
gexpected, jrec['extras']['qcvars'][inp['lbl'] + ' DISPERSION CORRECTION GRADIENT'], atol=1.e-7)


@using_dftd3_321
@pytest.mark.parametrize(
"subjects", [
"subjects",
[
pytest.param(eneyne_ne_psi4mols, marks=using_psi4),
pytest.param(eneyne_ne_qcdbmols, marks=using_psi4), # needs qcdb.Molecule, presently more common in psi4 than in qcdb
pytest.param(eneyne_ne_qcdbmols,
marks=using_psi4), # needs qcdb.Molecule, presently more common in psi4 than in qcdb
pytest.param(eneyne_ne_qcschemamols),
],
ids=['qmol', 'pmol', 'qcmol'])
Expand All @@ -702,11 +736,15 @@ def test_dftd3__run_dftd3__3body(inp, subjects, request):
gexpected = gref[inp['parent']][inp['lbl']][inp['subject']].ravel()

if 'qcmol' in request.node.name:
subject.update({'model': {'method': inp['name']},
'driver': 'gradient',
'keywords': {},
'schema_name': 'qcschema_input',
'schema_version': 1})
subject.update({
'model': {
'method': inp['name']
},
'driver': 'gradient',
'keywords': {},
'schema_name': 'qcschema_input',
'schema_version': 1
})
jrec = dftd3.run_json(subject)
else:
jrec = dftd3.run_dftd3(inp['name'], subject, options={}, ptype='gradient')
Expand All @@ -716,9 +754,11 @@ def test_dftd3__run_dftd3__3body(inp, subjects, request):
assert compare_values(expected, jrec['extras']['qcvars']['CURRENT ENERGY'], atol=1.e-7)
assert compare_values(expected, jrec['extras']['qcvars']['DISPERSION CORRECTION ENERGY'], atol=1.e-7)
assert compare_values(expected, jrec['extras']['qcvars']['3-BODY DISPERSION CORRECTION ENERGY'], atol=1.e-7)
assert compare_values(expected, jrec['extras']['qcvars']['AXILROD-TELLER-MUTO 3-BODY DISPERSION CORRECTION ENERGY'], atol=1.e-7)
assert compare_values(
expected, jrec['extras']['qcvars']['AXILROD-TELLER-MUTO 3-BODY DISPERSION CORRECTION ENERGY'], atol=1.e-7)

assert compare_values(gexpected, jrec['extras']['qcvars']['CURRENT GRADIENT'], atol=1.e-7)
assert compare_values(gexpected, jrec['extras']['qcvars']['DISPERSION CORRECTION GRADIENT'], atol=1.e-7)
assert compare_values(gexpected, jrec['extras']['qcvars']['3-BODY DISPERSION CORRECTION GRADIENT'], atol=1.e-7)
assert compare_values(gexpected, jrec['extras']['qcvars']['AXILROD-TELLER-MUTO 3-BODY DISPERSION CORRECTION GRADIENT'], atol=1.e-7)
assert compare_values(
gexpected, jrec['extras']['qcvars']['AXILROD-TELLER-MUTO 3-BODY DISPERSION CORRECTION GRADIENT'], atol=1.e-7)
Original file line number Diff line number Diff line change
Expand Up @@ -113,20 +113,3 @@ def test_torchani_task():
assert ret.driver == "gradient"


@testing.using_dftd3
def test_dftd3_task():
json_data = copy.deepcopy(_base_json)
json_data["molecule"] = qcng.get_molecule("eneyne")
json_data["driver"] = "energy"
json_data["model"] = {"method": "b3lyp-d3", "basis": ""}

ret = qcng.compute(json_data, "dftd3", raise_error=True, return_dict=True)

assert ret["driver"] == "energy"
assert "provenance" in ret
assert "normal termination of dftd3" in ret["stdout"]

for key in ["cpu", "hostname", "username", "wall_time"]:
assert key in ret["provenance"]

assert ret["success"] is True
44 changes: 23 additions & 21 deletions qcengine/testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,28 +66,30 @@ def is_dftd3_new_enough(version_feature_introduced):
return parse_version(candidate_version) >= parse_version(version_feature_introduced)


# Add flags
using_psi4 = pytest.mark.skipif(
is_psi4_new_enough("1.2") is False,
reason="Could not find psi4 or version too old. Please install the package to enable tests")

using_rdkit = pytest.mark.skipif(
_plugin_import("rdkit") is False, reason="Could not find rdkit. Please install the package to enable tests")

using_geometric = pytest.mark.skipif(
_plugin_import("geometric") is False,
reason="could not find geomeTRIC. Please install the package to enable tests")
# Figure out what is imported
_programs = {
"dftd3": which('dftd3', return_bool=True),
"geometric": _plugin_import("geometric"),
"psi4": is_psi4_new_enough("1.2"),
"rdkit": _plugin_import("rdkit"),
"qcdb": _plugin_import("qcdb"),
"torchani": _plugin_import("torchani"),
}

def has_program(name):
return _programs[name]

def _build_pytest_skip(program):
import_message = "Not detecting module {}. Install package if necessary to enable tests."
return pytest.mark.skipif(has_program(program) is False, reason=import_message.format(program))

using_torchani = pytest.mark.skipif(
_plugin_import("torchani") is False, reason="Could not find TorchAni. Please install the package to enable tests")

using_qcdb = pytest.mark.skipif(
_plugin_import("qcdb") is False,
reason='Not detecting common driver. Install package if necessary and add to envvar PYTHONPATH')

using_dftd3 = pytest.mark.skipif(
which('dftd3', return_bool=True) is False,
reason='Not detecting executable dftd3. Install package if necessary and add to envvar PATH')
# Add flags
using_dftd3 = _build_pytest_skip("dftd3")
using_geometric = _build_pytest_skip("geometric")
using_psi4 = _build_pytest_skip("psi4")
using_rdkit = _build_pytest_skip("rdkit")
using_qcdb = _build_pytest_skip("qcdb")
using_torchani = _build_pytest_skip("torchani")

using_dftd3_321 = pytest.mark.skipif(
is_dftd3_new_enough("3.2.1") is False,
Expand Down
58 changes: 58 additions & 0 deletions qcengine/tests/test_standard_suite.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"""
Tests the DQM compute dispatch module
"""

import copy

import pytest
from qcelemental.models import Molecule, ResultInput

import qcengine as qcng
from qcengine import testing

_canonical_methods = [
("dftd3", {"method": "b3lyp-d3"}),
("psi4", {"method": "hf", "basis": "6-31G"}),
("rdkit", {"method": "UFF"}),
("torchani", {"method": "ANI1"}),
]

@pytest.mark.parametrize("program, model", _canonical_methods)
def test_compute_energy(program, model):
if not testing.has_program(program):
pytest.skip("Program '{}' not found.".format(program))


inp = ResultInput(molecule=qcng.get_molecule("hydrogen"), driver="energy", model=model)
ret = qcng.compute(inp, program, raise_error=True)

assert ret.success is True
assert isinstance(ret.return_result, float)


@pytest.mark.parametrize("program, model", _canonical_methods)
def test_compute_gradient(program, model):
if not testing.has_program(program):
pytest.skip("Program '{}' not found.".format(program))

inp = ResultInput(molecule=qcng.get_molecule("hydrogen"), driver="gradient", model=model)
ret = qcng.compute(inp, program, raise_error=True)

assert ret.success is True
assert isinstance(ret.return_result, list)


@pytest.mark.parametrize("program, model", [
("dftd3", {"method": "bad"}),
("psi4", {"method": "bad"}),
("rdkit", {"method": "bad"}),
("torchani", {"method": "bad"}),
])
def test_compute_bad_models(program, model):
if not testing.has_program(program):
pytest.skip("Program '{}' not found.".format(program))

inp = ResultInput(molecule=qcng.get_molecule("hydrogen"), driver="energy", model=model)

with pytest.raises(ValueError) as exc:
ret = qcng.compute(inp, program, raise_error=True)

0 comments on commit 29e6973

Please sign in to comment.