From 919bce220f903e01babe5e1127feccae2961cb1f Mon Sep 17 00:00:00 2001 From: "Lori A. Burns" Date: Tue, 29 Oct 2024 13:00:59 -0400 Subject: [PATCH 01/11] failedoperation.input_data as object when possible --- docs/source/changelog.rst | 3 + qcengine/compute.py | 2 + qcengine/tests/test_harness_canonical.py | 89 +++++++++++++++++++++--- qcengine/util.py | 6 +- 4 files changed, 91 insertions(+), 9 deletions(-) diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index da004075..eb9c251e 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -70,6 +70,9 @@ Misc. MUST (Unmerged) +++++++++++++++ +- When qcengine.compute() fails and forms a fop = FailedOperation (raise_error=T), with v2, `fop.input_data` will be an <>Input model (when possible; if the error was in forming the model, it'll still be a dict), not always a dict like v1. +- When .compute() fails and collects the input for processing, with v2 it now uses the <>Input model passed to the executor, not the model-or-dict passed into compute(). +- The net result of the two above is that whereas fop.input_data in v1 was reliably a dict and its contents would reflect whether a model or dict was passed to qcengine.compute(), now in v2, fop.input_data is a model whenever possible (to mirror <>Result.input_data) regardless of model or dict passed to qcengine.compute(); the only case where it's a dict is if the error was in forming the model. WIP (Unmerged) ++++++++++++++ diff --git a/qcengine/compute.py b/qcengine/compute.py index 9bc47ce9..36f6317c 100644 --- a/qcengine/compute.py +++ b/qcengine/compute.py @@ -119,6 +119,8 @@ def compute( else: metadata["retries"] += 1 except: + if return_version >= 2: + output_data = input_data raise return handle_output_metadata( diff --git a/qcengine/tests/test_harness_canonical.py b/qcengine/tests/test_harness_canonical.py index 5064f4d6..ad85cecb 100644 --- a/qcengine/tests/test_harness_canonical.py +++ b/qcengine/tests/test_harness_canonical.py @@ -2,14 +2,17 @@ Tests the DQM compute dispatch module """ import copy +import pprint +from typing import Union import msgpack import numpy as np import pytest +import qcelemental as qcel from qcelemental.tests.test_model_results import center_data import qcengine as qcng -from qcengine.testing import checkver_and_convert, has_program, schema_versions, using +from qcengine.testing import checkver_and_convert, has_program, schema_versions, schema_versions2, using qcsk_bs = {"name": "custom_basis", "center_data": center_data, "atom_map": ["bs_sto3g_h", "bs_sto3g_h"]} @@ -159,7 +162,10 @@ def test_compute_energy_qcsk_basis(program, model, keywords, schema_versions, re # ("xtb", {"method": "bad"}), ], ) -def test_compute_bad_models(program, model, schema_versions, request): +@pytest.mark.parametrize("raiserr", [False, True]) +@pytest.mark.parametrize("retdict", [False, True]) +@pytest.mark.parametrize("inpdict", [False, True]) +def test_compute_bad_models(program, model, schema_versions, request, raiserr, retdict, inpdict): models, retver, _ = schema_versions if not has_program(program): @@ -167,13 +173,80 @@ def test_compute_bad_models(program, model, schema_versions, request): amodel = copy.deepcopy(model) adriver = amodel.pop("driver", "energy") - inp = models.AtomicInput( - molecule=models.Molecule(**qcng.get_molecule("hydrogen", return_dict=True)), driver=adriver, model=amodel - ) - + inp = { + "molecule": models.Molecule(**qcng.get_molecule("hydrogen", return_dict=True)), + "model": amodel, + "driver": adriver, + } + if not inpdict: + inp = models.AtomicInput(**inp) inp = checkver_and_convert(inp, request.node.name, "pre") - with pytest.raises(qcng.exceptions.InputError) as exc: - ret = qcng.compute(inp, program, raise_error=True, return_version=retver) + + if raiserr: + with pytest.raises(qcng.exceptions.InputError) as exc: + qcng.compute(inp, program, raise_error=raiserr, return_dict=retdict, return_version=retver) + else: + ret = qcng.compute(inp, program, raise_error=raiserr, return_dict=retdict, return_version=retver) + ret = checkver_and_convert( + ret, request.node.name, "post", vercheck=False, cast_dict_as="FailedOperation" + ) # TODO release vercheck=F? + if retdict: + assert ret["success"] is False, "wrongly successful" + assert ( + ret["error"]["error_type"] == "input_error" + ), f"wrong type: {ret['error']['error_type']=} != 'input_error'" + assert ret["input_data"]["model"]["method"] == "bad", "input not copied over" + else: + assert ret.success is False, "wrongly successful" + assert isinstance(ret, Union[qcel.models.v1.FailedOperation, qcel.models.v2.FailedOperation]), "wrong class" + assert ret.error.error_type == "input_error", f"wrong type: {ret.error.error_type=} != 'input_error'" + if "v2" in request.node.name: + assert ret.input_data.model.method == "bad", "input not copied over" + else: + assert ret.input_data["model"]["method"] == "bad", "input not copied over" + + +@pytest.mark.parametrize( + "program, model", + [ + ("psi4", {"method": "hf", "driver": "eighth"}), + ], +) +@pytest.mark.parametrize("raiserr", [False, True]) +@pytest.mark.parametrize("retdict", [False, True]) +def test_compute_badder_models(program, model, schema_versions2, request, raiserr, retdict): + models, retver, _ = schema_versions2 + + if not has_program(program): + pytest.skip("Program '{}' not found.".format(program)) + + amodel = copy.deepcopy(model) + adriver = amodel.pop("driver", "energy") + inp = { + "molecule": models.Molecule(**qcng.get_molecule("hydrogen", return_dict=True)), + "model": amodel, + "driver": adriver, + } + # inp = checkver_and_convert(inp, request.node.name, "pre") + + if raiserr: + with pytest.raises(qcng.exceptions.InputError) as exc: + qcng.compute(inp, program, raise_error=raiserr, return_dict=retdict, return_version=retver) + else: + ret = qcng.compute(inp, program, raise_error=raiserr, return_dict=retdict, return_version=retver) + if retdict: + assert ret["success"] is False, "wrongly successful" + assert ( + ret["error"]["error_type"] == "input_error" + ), f"wrong type: {ret['error']['error_type']=} != 'input_error'" + assert ret["input_data"]["driver"] == "eighth", "input not copied over" + else: + assert ret.success is False, "wrongly successful" + assert isinstance(ret, Union[qcel.models.v1.FailedOperation, qcel.models.v2.FailedOperation]), "wrong class" + assert ret.error.error_type == "input_error", f"wrong type: {ret.error.error_type=} != 'input_error'" + # note that input_data *always* a dict in this test (even for v2) + # since the error is that the AtomicInput model can't be constructed + assert ret.input_data["driver"] == "eighth", "input not copied over" def test_psi4_restarts(monkeypatch, schema_versions, request): diff --git a/qcengine/util.py b/qcengine/util.py index 75ac94b2..6eab18d4 100644 --- a/qcengine/util.py +++ b/qcengine/util.py @@ -224,8 +224,12 @@ def handle_output_metadata( 1: qcelemental.models.v1.FailedOperation, 2: qcelemental.models.v2.FailedOperation, }[convert_version] + + # for input_data, use object (not dict) if possible for >=v2 ret = model( - success=output_fusion.pop("success", False), error=output_fusion.pop("error"), input_data=output_fusion + success=output_fusion.pop("success", False), + error=output_fusion.pop("error"), + input_data=output_data if (convert_version >= 2) else output_fusion, ) if convert_version > 0: From 0d7711014cb6c3cdfdd66bf9fc5d92e03c0b7a06 Mon Sep 17 00:00:00 2001 From: "Lori A. Burns" Date: Tue, 5 Nov 2024 13:12:36 -0500 Subject: [PATCH 02/11] fix more tests, incl. d3/d4 return FailedOp and RandomError treated same as others --- qcengine/compute.py | 2 ++ qcengine/programs/dftd_ng.py | 12 +++++++++++- qcengine/programs/psi4.py | 6 ++++-- qcengine/programs/tests/test_molpro.py | 4 +++- qcengine/programs/tests/test_qchem.py | 12 +++++++++--- qcengine/programs/tests/test_terachem.py | 4 +++- 6 files changed, 32 insertions(+), 8 deletions(-) diff --git a/qcengine/compute.py b/qcengine/compute.py index 36f6317c..18031627 100644 --- a/qcengine/compute.py +++ b/qcengine/compute.py @@ -113,6 +113,8 @@ def compute( output_data = executor.compute(input_data, config) break except RandomError as e: + if return_version >= 2: + output_data = input_data if x == config.retries: raise e diff --git a/qcengine/programs/dftd_ng.py b/qcengine/programs/dftd_ng.py index d1b39846..5c4314d4 100644 --- a/qcengine/programs/dftd_ng.py +++ b/qcengine/programs/dftd_ng.py @@ -10,7 +10,7 @@ from typing import Any, ClassVar, Dict import qcelemental -from qcelemental.models.v2 import AtomicInput, AtomicResult +from qcelemental.models.v2 import AtomicInput, AtomicResult, FailedOperation from qcelemental.util import parse_version, safe_version, which_import from ..config import TaskConfig @@ -109,6 +109,11 @@ def compute(self, input_model: AtomicInput, config: TaskConfig) -> AtomicResult: # Run the Harness output = run_qcschema(input_model) + + # d4 qcschema interface stores error in Result model + if not output.success: + return FailedOperation(input_data=input_data, error=output.error.model_dump()) + output = output.convert_v(2) if "info" in output.extras: @@ -278,6 +283,11 @@ def compute(self, input_model: AtomicInput, config: TaskConfig) -> AtomicResult: # Run the Harness output = run_qcschema(input_model) + + # d3 qcschema interface stores error in Result model + if not output.success: + return FailedOperation(input_data=input_data, error=output.error.model_dump()) + output = output.convert_v(2) if "info" in output.extras: diff --git a/qcengine/programs/psi4.py b/qcengine/programs/psi4.py index 10562b13..ed87f952 100644 --- a/qcengine/programs/psi4.py +++ b/qcengine/programs/psi4.py @@ -7,7 +7,8 @@ from pathlib import Path from typing import TYPE_CHECKING, Any, ClassVar, Dict -from qcelemental.models.v2 import AtomicResult, BasisSet +from qcelemental.models.v1 import AtomicResult as AtomicResult +from qcelemental.models.v2 import AtomicResult as BasisSet from qcelemental.util import deserialize, parse_version, safe_version, which, which_import from ..exceptions import InputError, RandomError, ResourceError, UnknownError @@ -306,4 +307,5 @@ def compute(self, input_model: "AtomicInput", config: "TaskConfig") -> "AtomicRe # Delete keys output_data.pop("return_output", None) - return AtomicResult(**output_data) + atres = AtomicResult(**output_data) + return atres.convert_v(2) diff --git a/qcengine/programs/tests/test_molpro.py b/qcengine/programs/tests/test_molpro.py index 3873ab5c..a3a3f4f8 100644 --- a/qcengine/programs/tests/test_molpro.py +++ b/qcengine/programs/tests/test_molpro.py @@ -15,7 +15,9 @@ def test_molpro_output_parser(test_case): data = molpro_info.get_test_data(test_case) inp = qcel.models.v1.AtomicInput.parse_raw(data["input.json"]) - output = qcng.get_program("molpro", check=False).parse_output(data, inp).dict() + output = qcng.get_program("molpro", check=False).parse_output(data, inp) + # only qcng.compute() handles schema versions. above returns v2, so need to convert + output = output.convert_v(1).dict() output.pop("provenance", None) output.pop("schema_version", None) diff --git a/qcengine/programs/tests/test_qchem.py b/qcengine/programs/tests/test_qchem.py index 397bf3e7..f6d71424 100644 --- a/qcengine/programs/tests/test_qchem.py +++ b/qcengine/programs/tests/test_qchem.py @@ -29,7 +29,9 @@ def test_qchem_output_parser(test_case): inp = qcel.models.v1.AtomicInput.parse_raw(data["input.json"]) outfiles = qcel.util.deserialize(data["outfiles.msgpack"], "msgpack-ext") - output = qcng.get_program("qchem", check=False).parse_output(outfiles, inp).dict() + output = qcng.get_program("qchem", check=False).parse_output(outfiles, inp) + # only qcng.compute() handles schema versions. above returns v2, so need to convert + output = output.convert_v(1).dict() output.pop("provenance", None) output_ref = qcel.models.v1.AtomicResult.parse_raw(data["output.json"]).dict() @@ -129,7 +131,9 @@ def test_qchem_logfile_parser(test_case): data = qchem_logonly_info.get_test_data(test_case) outfiles = {"dispatch.out": data["qchem.out"]} with pytest.warns(Warning): - output = qcng.get_program("qchem", check=False).parse_logfile(outfiles).dict() + output = qcng.get_program("qchem", check=False).parse_logfile(outfiles) + # only qcng.compute() handles schema versions. above returns v2, so need to convert + output = output.convert_v(1).dict() output["stdout"] = None output_ref = qcel.models.v1.AtomicResult.parse_raw(data["output.json"]).dict() @@ -152,7 +156,9 @@ def test_qchem_logfile_parser_qcscr(test_case): outfiles = qcel.util.deserialize(data["outfiles.msgpack"], "msgpack-ext") with pytest.warns(Warning): - output = qcng.get_program("qchem", check=False).parse_logfile(outfiles).dict() + output = qcng.get_program("qchem", check=False).parse_logfile(outfiles) + # only qcng.compute() handles schema versions. above returns v2, so need to convert + output = output.convert_v(1).dict() output["stdout"] = None output_ref = qcel.models.v1.AtomicResult.parse_raw(data["output.json"]).dict() diff --git a/qcengine/programs/tests/test_terachem.py b/qcengine/programs/tests/test_terachem.py index 8558b301..2c5c6856 100644 --- a/qcengine/programs/tests/test_terachem.py +++ b/qcengine/programs/tests/test_terachem.py @@ -15,7 +15,9 @@ def test_terachem_output_parser(test_case): data = terachem_info.get_test_data(test_case) inp = qcel.models.v1.AtomicInput.parse_raw(data["input.json"]) - output = qcng.get_program("terachem", check=False).parse_output(data, inp).dict() + output = qcng.get_program("terachem", check=False).parse_output(data, inp) + # only qcng.compute() handles schema versions. above returns v2, so need to convert + output = output.convert_v(1).dict() output_ref = qcel.models.v1.AtomicResult.parse_raw(data["output.json"]).dict() # Forgiving molecule since it is now sparse From 0b781c623b03c0a5613852d624dfc1bd56072e4d Mon Sep 17 00:00:00 2001 From: "Lori A. Burns" Date: Tue, 5 Nov 2024 14:09:37 -0500 Subject: [PATCH 03/11] fix the rest, incl. let dicts be cast to models based on schema_version field if present --- .github/workflows/CI.yml | 2 +- docs/source/changelog.rst | 1 + qcengine/cli.py | 10 +++++++- qcengine/procedures/berny.py | 6 +++-- qcengine/procedures/geometric.py | 4 ++- qcengine/procedures/model.py | 12 ++++++--- qcengine/procedures/optking.py | 4 ++- qcengine/programs/model.py | 26 +++++++++++++------- qcengine/programs/psi4.py | 2 +- qcengine/programs/tests/test_programs.py | 30 +++++++++++++++++++---- qcengine/testing.py | 3 +-- qcengine/tests/test_cli.py | 31 +++++++++++++----------- qcengine/tests/test_procedures.py | 4 ++- qcengine/util.py | 17 ++++++++----- 14 files changed, 105 insertions(+), 47 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 75664742..d6a8d249 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -158,7 +158,7 @@ jobs: #if: false run: | conda remove qcelemental --force - python -m pip install 'git+https://github.com/loriab/QCElemental.git@csse_pyd2_warnings' --no-deps + python -m pip install 'git+https://github.com/loriab/QCElemental.git@csse_layout_536a' --no-deps # note: conda remove --force, not mamba remove --force b/c https://github.com/mamba-org/mamba/issues/412 # alt. is micromamba but not yet ready for setup-miniconda https://github.com/conda-incubator/setup-miniconda/issues/75 diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index eb9c251e..262dc5c1 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -73,6 +73,7 @@ MUST (Unmerged) - When qcengine.compute() fails and forms a fop = FailedOperation (raise_error=T), with v2, `fop.input_data` will be an <>Input model (when possible; if the error was in forming the model, it'll still be a dict), not always a dict like v1. - When .compute() fails and collects the input for processing, with v2 it now uses the <>Input model passed to the executor, not the model-or-dict passed into compute(). - The net result of the two above is that whereas fop.input_data in v1 was reliably a dict and its contents would reflect whether a model or dict was passed to qcengine.compute(), now in v2, fop.input_data is a model whenever possible (to mirror <>Result.input_data) regardless of model or dict passed to qcengine.compute(); the only case where it's a dict is if the error was in forming the model. +- DFTD3 & DFTD4 (new intf) - intercept ``v1.AtomicResult`` with ``success=False`` and ``error`` fields set from QCSchema interfaces and return ``FailedOperation``s. Someday when upstream switches to v2, request packages return FaileOp directly and use ``input_error`` rather than ``input error``. WIP (Unmerged) ++++++++++++++ diff --git a/qcengine/cli.py b/qcengine/cli.py index 8daad915..eb4f4d3c 100644 --- a/qcengine/cli.py +++ b/qcengine/cli.py @@ -73,6 +73,9 @@ def parse_args(): "(ii) A file name, " "(iii) '-', indicating data will be read from STDIN.", ) + run.add_argument( + "--return-version", default=-1, type=int, help="Schema version to return, if not the input version" + ) run_procedure = subparsers.add_parser( "run-procedure", @@ -214,7 +217,12 @@ def main(args=None): if command == "info": info_cli(args) elif command == "run": - ret = compute(data_arg_helper(args["data"]), args["program"], task_config=task_config) + ret = compute( + data_arg_helper(args["data"]), + args["program"], + task_config=task_config, + return_version=args["return_version"], + ) print(ret.json()) elif command == "run-procedure": ret = compute_procedure(data_arg_helper(args["data"]), args["procedure"], task_config=task_config) diff --git a/qcengine/procedures/berny.py b/qcengine/procedures/berny.py index 465fba5c..2c29a2dd 100644 --- a/qcengine/procedures/berny.py +++ b/qcengine/procedures/berny.py @@ -5,7 +5,8 @@ from typing import Any, ClassVar, Dict, Union import numpy as np -from qcelemental.models.v2 import FailedOperation, OptimizationInput, OptimizationResult +from qcelemental.models.v1 import OptimizationResult +from qcelemental.models.v2 import FailedOperation, OptimizationInput from qcelemental.util import which_import import qcengine @@ -94,5 +95,6 @@ def compute( "stdout": log_stream.getvalue(), # collect logged messages } ) - return OptimizationResult(**output_data) + output_data = OptimizationResult(**output_data) + return output_data.convert_v(2) return FailedOperation(input_data=input_data, error=error) diff --git a/qcengine/procedures/geometric.py b/qcengine/procedures/geometric.py index e8f49bc9..e5f62d5a 100644 --- a/qcengine/procedures/geometric.py +++ b/qcengine/procedures/geometric.py @@ -1,6 +1,7 @@ from typing import Any, ClassVar, Dict, Union -from qcelemental.models.v2 import OptimizationInput, OptimizationResult +from qcelemental.models.v1 import OptimizationResult +from qcelemental.models.v2 import OptimizationInput from qcelemental.util import safe_version, which_import from .model import ProcedureHarness @@ -65,5 +66,6 @@ def compute(self, input_model: "OptimizationInput", config: "TaskConfig") -> "Op output_data["input_specification"]["extras"].pop("_qcengine_local_config", None) if output_data["success"]: output_data = OptimizationResult(**output_data) + output_data = output_data.convert_v(2) return output_data diff --git a/qcengine/procedures/model.py b/qcengine/procedures/model.py index 64f7714c..e4117b58 100644 --- a/qcengine/procedures/model.py +++ b/qcengine/procedures/model.py @@ -71,9 +71,15 @@ def _build_model( # remember these are user-provided dictionaries, so they'll have the mandatory fields, # like driver, not the helpful discriminator fields like schema_version. - # for now, the two dictionaries look the same, so cast to the one we want - # note that this prevents correctly identifying the user schema version when dict passed in, so either as_v1/None or as_v2 will fail - mdl = model_wrapper(data, v1_model) + schver = data.get("schema_version") + if schver == 1: + mdl = model_wrapper(data, v1_model) + elif schver == 2: + mdl = model_wrapper(data, v2_model) + else: + # for now, the two dictionaries look the same, so cast to the one we want + # note that this prevents correctly identifying the user schema version when dict passed in, so either as_v1/None or as_v2 will fail + mdl = model_wrapper(data, v1_model) input_schema_version = mdl.schema_version if return_input_schema_version: diff --git a/qcengine/procedures/optking.py b/qcengine/procedures/optking.py index a7bb81e7..9ea1ac0f 100644 --- a/qcengine/procedures/optking.py +++ b/qcengine/procedures/optking.py @@ -1,6 +1,7 @@ from typing import Any, ClassVar, Dict, Union -from qcelemental.models.v2 import OptimizationInput, OptimizationResult +from qcelemental.models.v1 import OptimizationResult +from qcelemental.models.v2 import OptimizationInput from qcelemental.util import safe_version, which_import from .model import ProcedureHarness @@ -55,5 +56,6 @@ def compute(self, input_model: "OptimizationInput", config: "TaskConfig") -> "Op output_data["input_specification"]["extras"].pop("_qcengine_local_config", None) if output_data["success"]: output_data = OptimizationResult(**output_data) + output_data = output_data.convert_v(2) return output_data diff --git a/qcengine/programs/model.py b/qcengine/programs/model.py index 63fb735b..73c2425e 100644 --- a/qcengine/programs/model.py +++ b/qcengine/programs/model.py @@ -76,19 +76,27 @@ def build_input_model( # Note: Someday when the multiple QCSchema versions QCEngine supports are all within the # Pydantic v2 API base class, this can use discriminated unions instead of logic. - if isinstance(data, qcelemental.models.v1.AtomicInput): - mdl = model_wrapper(data, qcelemental.models.v1.AtomicInput) - elif isinstance(data, qcelemental.models.v2.AtomicInput): - mdl = model_wrapper(data, qcelemental.models.v2.AtomicInput) + v1_model = getattr(qcelemental.models.v1, "AtomicInput") + v2_model = getattr(qcelemental.models.v2, "AtomicInput") + + if isinstance(data, v1_model): + mdl = model_wrapper(data, v1_model) + elif isinstance(data, v2_model): + mdl = model_wrapper(data, v2_model) elif isinstance(data, dict): # remember these are user-provided dictionaries, so they'll have the mandatory fields, # like driver, not the helpful discriminator fields like schema_version. - # for now, the two dictionaries look the same, so cast to the one we want - # note that this prevents correctly identifying the user schema version when dict passed in, so either as_v1/None or as_v2 will fail - mdl = model_wrapper( - data, qcelemental.models.v1.AtomicInput - ) # TODO v2 # TODO kill off excuse_as_v2, now fix 2->-1 in schema_versions + schver = data.get("schema_version") + if schver == 1: + mdl = model_wrapper(data, v1_model) + elif schver == 2: + mdl = model_wrapper(data, v2_model) + else: + # for now, the two dictionaries look the same, so cast to the one we want + # note that this prevents correctly identifying the user schema version when dict passed in, so either as_v1/None or as_v2 will fail + # TODO v2 # TODO kill off excuse_as_v2, now fix 2->-1 in schema_versions + mdl = model_wrapper(data, v1_model) input_schema_version = mdl.schema_version if return_input_schema_version: diff --git a/qcengine/programs/psi4.py b/qcengine/programs/psi4.py index ed87f952..a17a993a 100644 --- a/qcengine/programs/psi4.py +++ b/qcengine/programs/psi4.py @@ -8,7 +8,7 @@ from typing import TYPE_CHECKING, Any, ClassVar, Dict from qcelemental.models.v1 import AtomicResult as AtomicResult -from qcelemental.models.v2 import AtomicResult as BasisSet +from qcelemental.models.v2 import BasisSet from qcelemental.util import deserialize, parse_version, safe_version, which, which_import from ..exceptions import InputError, RandomError, ResourceError, UnknownError diff --git a/qcengine/programs/tests/test_programs.py b/qcengine/programs/tests/test_programs.py index ee533964..aedec5f7 100644 --- a/qcengine/programs/tests/test_programs.py +++ b/qcengine/programs/tests/test_programs.py @@ -255,7 +255,7 @@ def test_mopac_task(schema_versions, request): assert "== MOPAC DONE ==" in ret.stdout -def test_random_failure_no_retries(failure_engine, schema_versions, request): +def test_random_failure_no_retries_input(failure_engine, schema_versions, request): _, retver, _ = schema_versions failure_engine.iter_modes = ["input_error"] @@ -263,14 +263,28 @@ def test_random_failure_no_retries(failure_engine, schema_versions, request): ret = checkver_and_convert(ret, request.node.name, "post", vercheck=False) assert ret.error.error_type == "input_error" - assert "retries" not in ret.input_data["provenance"].keys() + provenance_keys = ( + ret.input_data.provenance.model_dump().keys() + if ("_v2" in request.node.name) + else ret.input_data["provenance"].keys() + ) + assert "retries" not in provenance_keys + + +def test_random_failure_no_retries_random(failure_engine, schema_versions, request): + _, retver, _ = schema_versions failure_engine.iter_modes = ["random_error"] ret = qcng.compute(failure_engine.get_job(), failure_engine.name, raise_error=False, return_version=retver) ret = checkver_and_convert(ret, request.node.name, "post", vercheck=False) assert ret.error.error_type == "random_error" - assert "retries" not in ret.input_data["provenance"].keys() + provenance_keys = ( + ret.input_data.provenance.model_dump().keys() + if ("_v2" in request.node.name) + else ret.input_data["provenance"].keys() + ) + assert "retries" not in provenance_keys def test_random_failure_with_retries(failure_engine, schema_versions, request): @@ -286,7 +300,10 @@ def test_random_failure_with_retries(failure_engine, schema_versions, request): ) ret = checkver_and_convert(ret, request.node.name, "post", vercheck=False) - assert ret.input_data["provenance"]["retries"] == 2 + retries = ( + ret.input_data.provenance.retries if ("_v2" in request.node.name) else ret.input_data["provenance"]["retries"] + ) + assert retries == 2 assert ret.error.error_type == "random_error" failure_engine.iter_modes = ["random_error", "input_error"] @@ -299,7 +316,10 @@ def test_random_failure_with_retries(failure_engine, schema_versions, request): ) ret = checkver_and_convert(ret, request.node.name, "post", vercheck=False) - assert ret.input_data["provenance"]["retries"] == 1 + retries = ( + ret.input_data.provenance.retries if ("_v2" in request.node.name) else ret.input_data["provenance"]["retries"] + ) + assert retries == 1 assert ret.error.error_type == "input_error" diff --git a/qcengine/testing.py b/qcengine/testing.py index 5df7f89d..5abc6ee6 100644 --- a/qcengine/testing.py +++ b/qcengine/testing.py @@ -116,8 +116,7 @@ def compute(self, input_data: "AtomicInput", config: "TaskConfig") -> "AtomicRes grad = [0, 0, -grad_value, 0, 0, grad_value] if mode == "pass": - # TODO return v2 schema_versions[2].AtomicResult( - return qcel.models.v1.AtomicResult( + return schema_versions[2].AtomicResult( **{ **input_data.dict(), **{ diff --git a/qcengine/tests/test_cli.py b/qcengine/tests/test_cli.py index 2161c9cc..d4562845 100644 --- a/qcengine/tests/test_cli.py +++ b/qcengine/tests/test_cli.py @@ -7,7 +7,7 @@ import qcelemental from qcengine import cli, get_molecule, util -from qcengine.testing import checkver_and_convert, schema_versions2, using +from qcengine.testing import checkver_and_convert, schema_versions, using def run_qcengine_cli(args: List[str], stdin: str = None) -> str: @@ -57,9 +57,9 @@ def test_info(): @using("psi4") -def test_run_psi4(tmp_path, schema_versions2, request): +def test_run_psi4(tmp_path, schema_versions, request): """Tests qcengine run with psi4 and JSON input""" - models, _, _ = schema_versions2 + models, retver, _ = schema_versions def check_result(stdout): output = json.loads(stdout) @@ -74,14 +74,14 @@ def check_result(stdout): ) inp = checkver_and_convert(inp, request.node.name, "pre") - args = ["run", "psi4", inp.model_dump_json()] + args = ["run", "psi4", f"--return-version={retver}", inp.model_dump_json()] check_result(run_qcengine_cli(args)) - args = ["run", "psi4", os.path.join(tmp_path, "input.json")] + args = ["run", "psi4", os.path.join(tmp_path, "input.json"), f"--return-version={retver}"] with util.disk_files({"input.json": inp.model_dump_json()}, {}, cwd=tmp_path): check_result(run_qcengine_cli(args)) - args = ["run", "psi4", "-"] + args = ["run", "psi4", f"--return-version={retver}", "-"] # model_dump_json() works on v1 or v2 (see above). below tests that json() still works on v1. if "as_v1" in request.node.name: check_result(run_qcengine_cli(args, stdin=inp.json())) @@ -91,9 +91,9 @@ def check_result(stdout): @using("geometric") @using("psi4") -def test_run_procedure(tmp_path, schema_versions2, request): +def test_run_procedure(tmp_path, schema_versions, request): """Tests qcengine run-procedure with geometric, psi4, and JSON input""" - models, _, _ = schema_versions2 + models, retver, _ = schema_versions def check_result(stdout): output = json.loads(stdout) @@ -109,16 +109,19 @@ def check_result(stdout): inp = models.OptimizationInput(**inp) inp = checkver_and_convert(inp, request.node.name, "pre") - args = ["run-procedure", "geometric", inp.model_dump_json()] + if "to_v" in request.node.name: + args = ["run", "geometric", inp.model_dump_json(), f"--return-version={retver}"] + else: + args = ["run-procedure", "geometric", inp.model_dump_json()] check_result(run_qcengine_cli(args)) - args = ["run-procedure", "geometric", os.path.join(tmp_path, "input.json")] + if "to_v" in request.node.name: + args = ["run", "geometric", f"--return-version={retver}", os.path.join(tmp_path, "input.json")] + else: + args = ["run-procedure", "geometric", os.path.join(tmp_path, "input.json")] with util.disk_files({"input.json": inp.model_dump_json()}, {}, cwd=tmp_path): check_result(run_qcengine_cli(args)) - args = ["run-procedure", "geometric", inp.model_dump_json()] - check_result(run_qcengine_cli(args, stdin=inp.model_dump_json())) - # try unified route - args = ["run", "geometric", inp.model_dump_json()] + args = ["run", "geometric", inp.model_dump_json(), f"--return-version={retver}"] check_result(run_qcengine_cli(args, stdin=inp.model_dump_json())) diff --git a/qcengine/tests/test_procedures.py b/qcengine/tests/test_procedures.py index f042ebfd..bb19d304 100644 --- a/qcengine/tests/test_procedures.py +++ b/qcengine/tests/test_procedures.py @@ -403,7 +403,8 @@ def test_torsiondrive_generic(schema_versions, request): ret = qcng.compute(input_data, "torsiondrive", raise_error=True, return_version=retver) ret = checkver_and_convert(ret, request.node.name, "post") - assert ret.error is None + if "_v2" not in request.node.name: + assert ret.error is None assert ret.success expected_grid_ids = {"180", "0"} @@ -422,6 +423,7 @@ def test_torsiondrive_generic(schema_versions, request): assert ret.provenance.creator.lower() == "torsiondrive" assert ret.optimization_history["180"][0].provenance.creator.lower() == "geometric" assert ret.optimization_history["180"][0].trajectory[0].provenance.creator.lower() == "rdkit" + assert ret.optimization_history["180"][0].trajectory[0].schema_version == 2 if ("_v2" in request.node.name) else 1 assert ret.stdout == "All optimizations converged at lowest energy. Job Finished!\n" diff --git a/qcengine/util.py b/qcengine/util.py index 6eab18d4..24dcd455 100644 --- a/qcengine/util.py +++ b/qcengine/util.py @@ -226,15 +226,20 @@ def handle_output_metadata( }[convert_version] # for input_data, use object (not dict) if possible for >=v2 - ret = model( - success=output_fusion.pop("success", False), - error=output_fusion.pop("error"), - input_data=output_data if (convert_version >= 2) else output_fusion, - ) + success_ret = output_fusion.pop("success", False) + error_ret = output_fusion.pop("error") + if convert_version >= 2: + if isinstance(output_data, (qcelemental.models.v1.FailedOperation, qcelemental.models.v2.FailedOperation)): + # when harnesses return FailedOp object rather than raising error + inp_ret = output_data.input_data + else: + inp_ret = output_data.__class__(**output_fusion) + else: + inp_ret = output_fusion + ret = model(success=success_ret, error=error_ret, input_data=inp_ret) if convert_version > 0: ret = ret.convert_v(convert_version) - returned_version = getattr(ret, "schema_version", "not a model") if return_dict: # Use Pydantic to serialize, then reconstruct as Python dict of Python Primals From 5330a6c930079dca49cd61fce9fcf5b6a176c517 Mon Sep 17 00:00:00 2001 From: "Lori A. Burns" Date: Tue, 5 Nov 2024 14:40:11 -0500 Subject: [PATCH 04/11] fix xtb --- docs/source/changelog.rst | 1 + qcengine/programs/xtb.py | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 262dc5c1..ee06a064 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -74,6 +74,7 @@ MUST (Unmerged) - When .compute() fails and collects the input for processing, with v2 it now uses the <>Input model passed to the executor, not the model-or-dict passed into compute(). - The net result of the two above is that whereas fop.input_data in v1 was reliably a dict and its contents would reflect whether a model or dict was passed to qcengine.compute(), now in v2, fop.input_data is a model whenever possible (to mirror <>Result.input_data) regardless of model or dict passed to qcengine.compute(); the only case where it's a dict is if the error was in forming the model. - DFTD3 & DFTD4 (new intf) - intercept ``v1.AtomicResult`` with ``success=False`` and ``error`` fields set from QCSchema interfaces and return ``FailedOperation``s. Someday when upstream switches to v2, request packages return FaileOp directly and use ``input_error`` rather than ``input error``. +- ``qcengine run`` learned new argument ``--return-version`` analogous to ``qcengine.compute(..., return_version=1|2)`` so CLI matches API capabilities. Note *not* ported to phasing-out ``qcengine run-procedure``. WIP (Unmerged) ++++++++++++++ diff --git a/qcengine/programs/xtb.py b/qcengine/programs/xtb.py index 1caf0e1f..c58ad97b 100644 --- a/qcengine/programs/xtb.py +++ b/qcengine/programs/xtb.py @@ -11,7 +11,7 @@ from typing import Any, ClassVar, Dict -from qcelemental.models.v2 import AtomicInput, AtomicResult +from qcelemental.models.v2 import AtomicInput, AtomicResult, FailedOperation from qcelemental.util import safe_version, which_import from ..config import TaskConfig @@ -68,6 +68,11 @@ def compute(self, input_data: AtomicInput, config: TaskConfig) -> AtomicResult: # Run the Harness input_data = input_data.convert_v(1) output = run_qcschema(input_data) + + # xtb qcschema interface stores error in Result model + if not output.success: + return FailedOperation(input_data=input_data, error=output.error.model_dump()) + output = output.convert_v(2) # Make sure all keys from the initial input spec are sent along From bc3c857cd768c8c1c0dd59ca80158ac85d8a171d Mon Sep 17 00:00:00 2001 From: "Lori A. Burns" Date: Wed, 6 Nov 2024 00:02:25 -0500 Subject: [PATCH 05/11] fixes for py38 and dftd3 --- qcengine/tests/test_harness_canonical.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qcengine/tests/test_harness_canonical.py b/qcengine/tests/test_harness_canonical.py index ad85cecb..c808ecff 100644 --- a/qcengine/tests/test_harness_canonical.py +++ b/qcengine/tests/test_harness_canonical.py @@ -195,15 +195,15 @@ def test_compute_bad_models(program, model, schema_versions, request, raiserr, r assert ( ret["error"]["error_type"] == "input_error" ), f"wrong type: {ret['error']['error_type']=} != 'input_error'" - assert ret["input_data"]["model"]["method"] == "bad", "input not copied over" + assert ret["input_data"]["model"]["method"] == model["method"], "input not copied over" else: assert ret.success is False, "wrongly successful" - assert isinstance(ret, Union[qcel.models.v1.FailedOperation, qcel.models.v2.FailedOperation]), "wrong class" + assert isinstance(ret, (qcel.models.v1.FailedOperation, qcel.models.v2.FailedOperation)), "wrong class" assert ret.error.error_type == "input_error", f"wrong type: {ret.error.error_type=} != 'input_error'" if "v2" in request.node.name: - assert ret.input_data.model.method == "bad", "input not copied over" + assert ret.input_data.model.method == model["method"], "input not copied over" else: - assert ret.input_data["model"]["method"] == "bad", "input not copied over" + assert ret.input_data["model"]["method"] == model["method"], "input not copied over" @pytest.mark.parametrize( From 828094c8e5a2ed3407663980ce276c18aeef0543 Mon Sep 17 00:00:00 2001 From: "Lori A. Burns" Date: Wed, 6 Nov 2024 00:39:15 -0500 Subject: [PATCH 06/11] fix for adcc --- qcengine/tests/test_harness_canonical.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/qcengine/tests/test_harness_canonical.py b/qcengine/tests/test_harness_canonical.py index c808ecff..0aebdb7a 100644 --- a/qcengine/tests/test_harness_canonical.py +++ b/qcengine/tests/test_harness_canonical.py @@ -3,7 +3,6 @@ """ import copy import pprint -from typing import Union import msgpack import numpy as np @@ -242,7 +241,7 @@ def test_compute_badder_models(program, model, schema_versions2, request, raiser assert ret["input_data"]["driver"] == "eighth", "input not copied over" else: assert ret.success is False, "wrongly successful" - assert isinstance(ret, Union[qcel.models.v1.FailedOperation, qcel.models.v2.FailedOperation]), "wrong class" + assert isinstance(ret, (qcel.models.v1.FailedOperation, qcel.models.v2.FailedOperation)), "wrong class" assert ret.error.error_type == "input_error", f"wrong type: {ret.error.error_type=} != 'input_error'" # note that input_data *always* a dict in this test (even for v2) # since the error is that the AtomicInput model can't be constructed From 04599aa0bca2ec54866f2721d47e2329b3ce9a1d Mon Sep 17 00:00:00 2001 From: "Lori A. Burns" Date: Wed, 6 Nov 2024 16:14:13 -0500 Subject: [PATCH 07/11] test dftd3 on windows --- devtools/conda-envs/opt-disp-cf.yaml | 2 +- qcengine/compute.py | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/devtools/conda-envs/opt-disp-cf.yaml b/devtools/conda-envs/opt-disp-cf.yaml index 33919af9..04c27a64 100644 --- a/devtools/conda-envs/opt-disp-cf.yaml +++ b/devtools/conda-envs/opt-disp-cf.yaml @@ -8,7 +8,7 @@ dependencies: - mopac # Mixed Tests - - dftd3-python + - dftd3-python =1.2.0 - dftd4-python - gcp-correction - geometric diff --git a/qcengine/compute.py b/qcengine/compute.py index 18031627..97e13fd7 100644 --- a/qcengine/compute.py +++ b/qcengine/compute.py @@ -70,6 +70,28 @@ def compute( AtomicResult, OptimizationResult, FailedOperation, etc., or Dict representation of any object type A QCSchema representation of the requested output, type depends on return_dict key. + .. _`table:compute_result`: + + +-----------+-------------+-------------+--------------------------------------------------+ + | good_calc | raise_error | return_dict | output | + +===========+=============+=============+==================================================+ + | T | F (def) | F (def) | ``AtomicResult`` object | + +-----------+-------------+-------------+--------------------------------------------------+ + | T | T | F (def) | ``AtomicResult`` object | + +-----------+-------------+-------------+--------------------------------------------------+ + | T | T | T | dict of ``AtomicResult`` | + +-----------+-------------+-------------+--------------------------------------------------+ + | T | F (def) | T | dict of ``AtomicResult`` | + +-----------+-------------+-------------+--------------------------------------------------+ + | F | F (def) | F (def) | ``FailedOperation`` object | + +-----------+-------------+-------------+--------------------------------------------------+ + | F | T | F (def) | raises ``InputError`` (type encoded in FailedOp) | + +-----------+-------------+-------------+--------------------------------------------------+ + | F | T | T | raises ``InputError`` (type encoded in FailedOp) | + +-----------+-------------+-------------+--------------------------------------------------+ + | F | F (def) | T | dict of ``FailedOperation`` | + +-----------+-------------+-------------+--------------------------------------------------+ + .. versionadded:: 0.50.0 input_data can newly be QCSchema v2 as well as longstanding v1. The compute_procedure is newly incorporated into compute. From 85af55b1bb5cc488244c0f86771d71b479d6023d Mon Sep 17 00:00:00 2001 From: "Lori A. Burns" Date: Wed, 6 Nov 2024 18:57:28 -0500 Subject: [PATCH 08/11] try windows and new d3 --- .github/workflows/CI.yml | 6 ++++++ devtools/conda-envs/disp-win.yaml | 26 ++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 devtools/conda-envs/disp-win.yaml diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index d6a8d249..d45dc911 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -102,6 +102,12 @@ jobs: runs-on: windows-latest pytest: "-k 'not (hes2 or qchem)'" + - conda-env: disp-win + python-version: 3.13 + label: disp-win + runs-on: windows-latest + pytest: "" + - conda-env: mace python-version: "3.10" label: MACE diff --git a/devtools/conda-envs/disp-win.yaml b/devtools/conda-envs/disp-win.yaml new file mode 100644 index 00000000..8bea39d1 --- /dev/null +++ b/devtools/conda-envs/disp-win.yaml @@ -0,0 +1,26 @@ +name: test +channels: + - conda-forge + - nodefaults +dependencies: + - dftd3-python =1.2.0 + - gcp-correction + - geometric + - optking + - pymdi + + # Core + - python + - pyyaml + - py-cpuinfo + - psutil + - qcelemental + - pydantic=2 + - pydantic-settings + - msgpack-python + + # Testing + - pytest + - pytest-cov + - codecov + From 19fa61345e031d7697281d1e45c07f562853dc28 Mon Sep 17 00:00:00 2001 From: "Lori A. Burns" Date: Wed, 6 Nov 2024 19:19:47 -0500 Subject: [PATCH 09/11] fix --- .github/workflows/CI.yml | 3 ++- devtools/conda-envs/opt-disp-cf.yaml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index d45dc911..ab198d1d 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -102,8 +102,9 @@ jobs: runs-on: windows-latest pytest: "-k 'not (hes2 or qchem)'" + # 3.13 - conda-env: disp-win - python-version: 3.13 + python-version: 3.12 label: disp-win runs-on: windows-latest pytest: "" diff --git a/devtools/conda-envs/opt-disp-cf.yaml b/devtools/conda-envs/opt-disp-cf.yaml index 04c27a64..33919af9 100644 --- a/devtools/conda-envs/opt-disp-cf.yaml +++ b/devtools/conda-envs/opt-disp-cf.yaml @@ -8,7 +8,7 @@ dependencies: - mopac # Mixed Tests - - dftd3-python =1.2.0 + - dftd3-python - dftd4-python - gcp-correction - geometric From 907aa0db62fadac790539803f6d14e3091d0423d Mon Sep 17 00:00:00 2001 From: "Lori A. Burns" Date: Wed, 6 Nov 2024 21:43:36 -0500 Subject: [PATCH 10/11] test path --- .github/workflows/CI.yml | 162 +++++++++--------- .../empirical_dispersion_resources.py | 8 + 2 files changed, 89 insertions(+), 81 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index ab198d1d..8a09c646 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -21,86 +21,86 @@ jobs: fail-fast: false matrix: cfg: - #- conda-env: psi - # python-version: 3.7 - # label: Psi4-1.5 + ##- conda-env: psi + ## python-version: 3.7 + ## label: Psi4-1.5 + ## runs-on: ubuntu-latest + ## pytest: "" + ## Note: removed Sep 2024 b/c too hard to reconcile w/pyd v2 + + #- conda-env: psi-nightly + # python-version: "3.10" + # label: Psi4-1.6 # runs-on: ubuntu-latest # pytest: "" - # Note: removed Sep 2024 b/c too hard to reconcile w/pyd v2 - - - conda-env: psi-nightly - python-version: "3.10" - label: Psi4-1.6 - runs-on: ubuntu-latest - pytest: "" - - - conda-env: psi-cf - python-version: "3.12" - label: Psi4-1.8 - runs-on: windows-latest - pytest: "-k 'not (hes2 or qchem)'" - - conda-env: torchani - python-version: 3.8 - label: ANI - runs-on: ubuntu-latest - pytest: "" + #- conda-env: psi-cf + # python-version: "3.12" + # label: Psi4-1.8 + # runs-on: windows-latest + # pytest: "-k 'not (hes2 or qchem)'" - - conda-env: openmm - python-version: 3.8 - label: OpenMM - runs-on: ubuntu-latest - pytest: "" + #- conda-env: torchani + # python-version: 3.8 + # label: ANI + # runs-on: ubuntu-latest + # pytest: "" - - conda-env: xtb - python-version: "3.10" - label: xTB - runs-on: ubuntu-latest - pytest: "" + #- conda-env: openmm + # python-version: 3.8 + # label: OpenMM + # runs-on: ubuntu-latest + # pytest: "" - #- conda-env: qcore - # python-version: 3.7 - # label: QCore + #- conda-env: xtb + # python-version: "3.10" + # label: xTB # runs-on: ubuntu-latest # pytest: "" - # Note: removed Sep 2024 b/c too hard to reconcile w/pyd v2. Manby approves. - - conda-env: nwchem - python-version: 3.8 - label: NWChem70 - runs-on: ubuntu-20.04 - pytest: "" - # formerly NWChem v6.6 with python-version: 3.6 & runs-on: ubuntu-16.04 but ubuntu env retired by GH Sep 2021 + ##- conda-env: qcore + ## python-version: 3.7 + ## label: QCore + ## runs-on: ubuntu-latest + ## pytest: "" + ## Note: removed Sep 2024 b/c too hard to reconcile w/pyd v2. Manby approves. - - conda-env: nwchem-cf - python-version: 3.12 - label: NWChem - runs-on: ubuntu-latest - pytest: "" + #- conda-env: nwchem + # python-version: 3.8 + # label: NWChem70 + # runs-on: ubuntu-20.04 + # pytest: "" + # # formerly NWChem v6.6 with python-version: 3.6 & runs-on: ubuntu-16.04 but ubuntu env retired by GH Sep 2021 - - conda-env: mrchem - python-version: 3.8 - label: MRChem - runs-on: ubuntu-latest - pytest: "" + #- conda-env: nwchem-cf + # python-version: 3.12 + # label: NWChem + # runs-on: ubuntu-latest + # pytest: "" - - conda-env: adcc - python-version: 3.8 - label: ADCC - runs-on: ubuntu-latest - pytest: "" + #- conda-env: mrchem + # python-version: 3.8 + # label: MRChem + # runs-on: ubuntu-latest + # pytest: "" - - conda-env: opt-disp - python-version: 3.8 - label: optimization-dispersion - runs-on: ubuntu-latest - pytest: "" + #- conda-env: adcc + # python-version: 3.8 + # label: ADCC + # runs-on: ubuntu-latest + # pytest: "" - - conda-env: opt-disp-cf - python-version: 3.11 - label: optimization-dispersion - runs-on: windows-latest - pytest: "-k 'not (hes2 or qchem)'" + #- conda-env: opt-disp + # python-version: 3.8 + # label: optimization-dispersion + # runs-on: ubuntu-latest + # pytest: "" + + #- conda-env: opt-disp-cf + # python-version: 3.11 + # label: optimization-dispersion + # runs-on: windows-latest + # pytest: "-k 'not (hes2 or qchem)'" # 3.13 - conda-env: disp-win @@ -109,24 +109,24 @@ jobs: runs-on: windows-latest pytest: "" - - conda-env: mace - python-version: "3.10" - label: MACE - runs-on: ubuntu-latest - pytest: "" - - - conda-env: aimnet2 - python-version: 3.11 - label: AIMNET2 - runs-on: ubuntu-latest - pytest: "" - - #- conda-env: nwchem + #- conda-env: mace # python-version: "3.10" - # label: TeraChem - # runs-on: ubuntu-20.04 + # label: MACE + # runs-on: ubuntu-latest # pytest: "" + #- conda-env: aimnet2 + # python-version: 3.11 + # label: AIMNET2 + # runs-on: ubuntu-latest + # pytest: "" + + ##- conda-env: nwchem + ## python-version: "3.10" + ## label: TeraChem + ## runs-on: ubuntu-20.04 + ## pytest: "" + name: "🐍 ${{ matrix.cfg.python-version }} • ${{ matrix.cfg.label }} • ${{ matrix.cfg.runs-on }}" runs-on: ${{ matrix.cfg.runs-on }} diff --git a/qcengine/programs/empirical_dispersion_resources.py b/qcengine/programs/empirical_dispersion_resources.py index 8f19185a..43b1a89e 100644 --- a/qcengine/programs/empirical_dispersion_resources.py +++ b/qcengine/programs/empirical_dispersion_resources.py @@ -1011,6 +1011,14 @@ def _get_d3_definitions(dashlevel: str) -> dict: the data returned by dftd3 into the right shape to make qcng happy. """ + from dftd3.parameters import get_data_file_name + + ans = get_data_file_name() + print(f"{ans=}") + import os + + print(f"{os.path.isfile(ans)=}") + # sys.exit(4) from dftd3.parameters import get_all_damping_params # The names here are the subset allowed by qcng with the names used in dftd3 From a7f030befe735f923b911b226dc9ddacb0326abc Mon Sep 17 00:00:00 2001 From: "Lori A. Burns" Date: Fri, 8 Nov 2024 03:25:48 -0500 Subject: [PATCH 11/11] regular testing lanes --- .github/workflows/CI.yml | 168 +++++++++--------- .../empirical_dispersion_resources.py | 8 - 2 files changed, 84 insertions(+), 92 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 8a09c646..2d0b75c5 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -21,112 +21,112 @@ jobs: fail-fast: false matrix: cfg: - ##- conda-env: psi - ## python-version: 3.7 - ## label: Psi4-1.5 - ## runs-on: ubuntu-latest - ## pytest: "" - ## Note: removed Sep 2024 b/c too hard to reconcile w/pyd v2 - - #- conda-env: psi-nightly - # python-version: "3.10" - # label: Psi4-1.6 + #- conda-env: psi + # python-version: 3.7 + # label: Psi4-1.5 # runs-on: ubuntu-latest # pytest: "" + # Note: removed Sep 2024 b/c too hard to reconcile w/pyd v2 - #- conda-env: psi-cf - # python-version: "3.12" - # label: Psi4-1.8 - # runs-on: windows-latest - # pytest: "-k 'not (hes2 or qchem)'" + - conda-env: psi-nightly + python-version: "3.10" + label: Psi4-1.6 + runs-on: ubuntu-latest + pytest: "" - #- conda-env: torchani - # python-version: 3.8 - # label: ANI - # runs-on: ubuntu-latest - # pytest: "" + - conda-env: psi-cf + python-version: "3.12" + label: Psi4-1.8 + runs-on: windows-latest + pytest: "-k 'not (hes2 or qchem)'" - #- conda-env: openmm - # python-version: 3.8 - # label: OpenMM - # runs-on: ubuntu-latest - # pytest: "" + - conda-env: torchani + python-version: 3.8 + label: ANI + runs-on: ubuntu-latest + pytest: "" - #- conda-env: xtb - # python-version: "3.10" - # label: xTB + - conda-env: openmm + python-version: 3.8 + label: OpenMM + runs-on: ubuntu-latest + pytest: "" + + - conda-env: xtb + python-version: "3.10" + label: xTB + runs-on: ubuntu-latest + pytest: "" + + #- conda-env: qcore + # python-version: 3.7 + # label: QCore # runs-on: ubuntu-latest # pytest: "" + # Note: removed Sep 2024 b/c too hard to reconcile w/pyd v2. Manby approves. - ##- conda-env: qcore - ## python-version: 3.7 - ## label: QCore - ## runs-on: ubuntu-latest - ## pytest: "" - ## Note: removed Sep 2024 b/c too hard to reconcile w/pyd v2. Manby approves. + - conda-env: nwchem + python-version: 3.8 + label: NWChem70 + runs-on: ubuntu-20.04 + pytest: "" + # formerly NWChem v6.6 with python-version: 3.6 & runs-on: ubuntu-16.04 but ubuntu env retired by GH Sep 2021 - #- conda-env: nwchem - # python-version: 3.8 - # label: NWChem70 - # runs-on: ubuntu-20.04 - # pytest: "" - # # formerly NWChem v6.6 with python-version: 3.6 & runs-on: ubuntu-16.04 but ubuntu env retired by GH Sep 2021 + - conda-env: nwchem-cf + python-version: 3.12 + label: NWChem + runs-on: ubuntu-latest + pytest: "" - #- conda-env: nwchem-cf - # python-version: 3.12 - # label: NWChem - # runs-on: ubuntu-latest - # pytest: "" + - conda-env: mrchem + python-version: 3.8 + label: MRChem + runs-on: ubuntu-latest + pytest: "" - #- conda-env: mrchem - # python-version: 3.8 - # label: MRChem - # runs-on: ubuntu-latest - # pytest: "" + - conda-env: adcc + python-version: 3.8 + label: ADCC + runs-on: ubuntu-latest + pytest: "" - #- conda-env: adcc - # python-version: 3.8 - # label: ADCC - # runs-on: ubuntu-latest - # pytest: "" + - conda-env: opt-disp + python-version: 3.8 + label: optimization-dispersion + runs-on: ubuntu-latest + pytest: "" - #- conda-env: opt-disp - # python-version: 3.8 - # label: optimization-dispersion - # runs-on: ubuntu-latest - # pytest: "" + - conda-env: opt-disp-cf + python-version: 3.11 + label: optimization-dispersion + runs-on: windows-latest + pytest: "-k 'not (hes2 or qchem)'" - #- conda-env: opt-disp-cf - # python-version: 3.11 - # label: optimization-dispersion + # 3.13 + #- conda-env: disp-win + # python-version: 3.12 + # label: disp-win # runs-on: windows-latest - # pytest: "-k 'not (hes2 or qchem)'" + # pytest: "" - # 3.13 - - conda-env: disp-win - python-version: 3.12 - label: disp-win - runs-on: windows-latest + - conda-env: mace + python-version: "3.10" + label: MACE + runs-on: ubuntu-latest pytest: "" - #- conda-env: mace - # python-version: "3.10" - # label: MACE - # runs-on: ubuntu-latest - # pytest: "" + - conda-env: aimnet2 + python-version: 3.11 + label: AIMNET2 + runs-on: ubuntu-latest + pytest: "" - #- conda-env: aimnet2 - # python-version: 3.11 - # label: AIMNET2 - # runs-on: ubuntu-latest + #- conda-env: nwchem + # python-version: "3.10" + # label: TeraChem + # runs-on: ubuntu-20.04 # pytest: "" - ##- conda-env: nwchem - ## python-version: "3.10" - ## label: TeraChem - ## runs-on: ubuntu-20.04 - ## pytest: "" - name: "🐍 ${{ matrix.cfg.python-version }} • ${{ matrix.cfg.label }} • ${{ matrix.cfg.runs-on }}" runs-on: ${{ matrix.cfg.runs-on }} diff --git a/qcengine/programs/empirical_dispersion_resources.py b/qcengine/programs/empirical_dispersion_resources.py index 43b1a89e..8f19185a 100644 --- a/qcengine/programs/empirical_dispersion_resources.py +++ b/qcengine/programs/empirical_dispersion_resources.py @@ -1011,14 +1011,6 @@ def _get_d3_definitions(dashlevel: str) -> dict: the data returned by dftd3 into the right shape to make qcng happy. """ - from dftd3.parameters import get_data_file_name - - ans = get_data_file_name() - print(f"{ans=}") - import os - - print(f"{os.path.isfile(ans)=}") - # sys.exit(4) from dftd3.parameters import get_all_damping_params # The names here are the subset allowed by qcng with the names used in dftd3