Skip to content

Commit

Permalink
TD v2 models ready
Browse files Browse the repository at this point in the history
  • Loading branch information
loriab committed Dec 12, 2024
1 parent f623e94 commit 318816e
Show file tree
Hide file tree
Showing 8 changed files with 273 additions and 98 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/CI.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
if: matrix.python-version == '3.8' || matrix.python-version == '3.10'
run: pip install '.[test]'
- name: Run tests
run: pytest -rws -v --cov=qcelemental --color=yes --cov-report=xml -k "not TD and not Torsion" #-k "not pubchem_multiout_g"
run: pytest -rws -v --cov=qcelemental --color=yes --cov-report=xml #-k "not pubchem_multiout_g"
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
- name: QCSchema Examples Deploy
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/Lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
python-version: "3.8"
- name: Install black
run: pip install "black>=22.1.0,<23.0a0"
- name: Print code formatting with black
- name: Print code formatting with black (hints here if next step errors)
run: black --diff .
- name: Check code formatting with black
run: black --check .
Expand Down
8 changes: 5 additions & 3 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,12 @@ Enhancements
++++++++++++
- (:pr:`363`)
- (:pr:`363`)
- (:pr:`363`)
- (:pr:`363`) allow nwchemdriver w/o driver=energy. provenance now nwchemdriver not nwchemrelax
- (:pr:`363`) ``v2.TorsionDriveResult`` no longer inherits from Input and now has indep id and extras and new native_files.
- (:pr:`363`) ``v2.TorsionDriveInput.initial_molecule`` now ``initial_molecules`` as it's a list of >=1 molecules. keep change?
- (:pr:`363`) ``v2. TorsionDriveSpecification`` is a new model. instead of ``v2.TorsionDriveInput`` having a ``input_specification`` and an ``optimization_spec`` fields, it has a ``specification`` field that is a ``TorsionDriveSpecification`` which in turn hold opt info and in turn gradient/atomic info.
- (:pr:`363`) ``v2.TDKeywords`` got a ``schema_name`` field.
- (:pr:`363`) ``native_files`` field added to ``v2.OptimizationResult`` and ``v2.TorsionDriveResult`` gained a ``native_files`` field, though not protocols for user control.
- (:pr:`363`) ``v2.AtomicResult.convert_v()`` learned external_protocols option to inject that field if known from OptIn
- (:pr:`363`) Optking now fills in ``v2.OptimizationResult.stdout``. Through v2, once can alter gradient protocols in an optimization.
- (:pr:`363`) OptimizationSpecification learned a ``convert_v`` function to interconvert.
- (:pr:`363`) all the v2 models of ptcl/kw/spec/in/prop/res type have ``schema_name``. ``qcschema_input`` and ``qcschema_output`` now are ``qcschema_atomic_input`` and ``qcschema_atomic_output``
- (:pr:`363`) whereas ``v1.AtomicInput`` and ``v1.QCInputSpecification`` shared the same schema_name, ``v2.AtomicInput`` and ``v2.AtomicSpecification`` do not. This is a step towards more explicit schema names.
Expand Down
85 changes: 74 additions & 11 deletions qcelemental/models/v1/procedures.py
Original file line number Diff line number Diff line change
Expand Up @@ -404,10 +404,35 @@ def convert_v(
dself = self.dict()
# dself = self.model_dump(exclude_unset=True, exclude_none=True)
if target_version == 2:
dself["input_specification"].pop("schema_version", None)
dself["optimization_spec"].pop("schema_version", None)
gradspec = self.input_specification.convert_v(target_version).model_dump()
gradspec["program"] = dself["optimization_spec"]["keywords"].pop("program", "")
dself.pop("input_specification")

optspec = {}
optspec["program"] = dself["optimization_spec"].pop("procedure")
optspec["protocols"] = dself["optimization_spec"].pop("protocols")
optspec["keywords"] = dself["optimization_spec"].pop("keywords")
optspec["specification"] = gradspec
dself["optimization_spec"].pop("schema_name")
dself["optimization_spec"].pop("schema_version")
assert not dself["optimization_spec"], dself["optimization_spec"]
dself.pop("optimization_spec")

tdspec = {}
tdspec["program"] = "torsiondrive"
tdspec["extras"] = dself.pop("extras")
tdspec["keywords"] = dself.pop("keywords")
tdspec["specification"] = optspec

dtop = {}
dtop["provenance"] = dself.pop("provenance")
dtop["initial_molecules"] = dself.pop("initial_molecule")
dtop["specification"] = tdspec
dself.pop("schema_name")
dself.pop("schema_version")
assert not dself, dself

self_vN = qcel.models.v2.TorsionDriveInput(**dself)
self_vN = qcel.models.v2.TorsionDriveInput(**dtop)
else:
assert False, target_version

Expand Down Expand Up @@ -451,31 +476,69 @@ def _version_stamp(cls, v):
return 1

def convert_v(
self, target_version: int, /
self, target_version: int, /, *, external_input_data: "TorsionDriveInput" = None
) -> Union["qcelemental.models.v1.TorsionDriveResult", "qcelemental.models.v2.TorsionDriveResult"]:
"""Convert to instance of particular QCSchema version."""
import qcelemental as qcel

if check_convertible_version(target_version, error="TorsionDriveResult") == "self":
return self

opthist_class = next(iter(self.optimization_history.values()))[0].__class__
dself = self.dict()
if target_version == 2:
opthist_class = next(iter(self.optimization_history.values()))[0].__class__
dtop = {}

# remove harmless empty error field that v2 won't accept. if populated, pydantic will catch it.
if not dself.get("error", True):
dself.pop("error")

dself["input_specification"].pop("schema_version", None)
dself["optimization_spec"].pop("schema_version", None)
dself["optimization_history"] = {
v1_input_data = {
k: dself.pop(k)
for k in list(dself.keys())
if k in ["initial_molecule", "keywords", "optimization_spec", "input_specification"] # protocols
}
# any input provenance has been overwritten
# sep any merged extras known to belong to input
v1_input_data["extras"] = {k: dself["extras"].pop(k) for k in list(dself["extras"].keys()) if k in []}
v2_input_data = qcel.models.v1.TorsionDriveInput(**v1_input_data).convert_v(target_version)

# if dself["id"]:
# input_data["id"] = dself["id"] # in/out should likely match

if external_input_data:
# Note: overwriting with external, not updating. reconsider?
if isinstance(external_input_data, dict):
if isinstance(external_input_data["specification"], dict):
in_extras = external_input_data["specification"].get("extras", {})
else:
in_extras = external_input_data["specification"].extras
else:
in_extras = external_input_data.specification.extras
dtop["extras"] = {k: v for k, v in dself["extras"].items() if (k, v) not in in_extras.items()}
dtop["input_data"] = external_input_data
else:
dtop["input_data"] = v2_input_data
dtop["extras"] = dself.pop("extras")

dtop["provenance"] = dself.pop("provenance")
dtop["stdout"] = dself.pop("stdout")
dtop["stderr"] = dself.pop("stderr")
dtop["success"] = dself.pop("success")
dtop["final_energies"] = dself.pop("final_energies")
dtop["final_molecules"] = dself.pop("final_molecules")
dtop["optimization_history"] = {
k: [opthist_class(**res).convert_v(target_version) for res in lst]
for k, lst in dself["optimization_history"].items()
}
# if dself["optimization_spec"].pop("extras", None):
# pass
dself.pop("optimization_history")
dself.pop("schema_name")
dself.pop("schema_version")
if "error" in dself:
dtop["error"] = dself.pop("error") # guaranteed to be fatal
assert not dself, dself

self_vN = qcel.models.v2.TorsionDriveResult(**dself)
self_vN = qcel.models.v2.TorsionDriveResult(**dtop)
else:
assert False, target_version

Expand Down
2 changes: 1 addition & 1 deletion qcelemental/models/v2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
OptimizationProtocols,
OptimizationResult,
OptimizationSpecification,
QCInputSpecification,
TDKeywords,
TorsionDriveInput,
TorsionDriveResult,
TorsionDriveSpecification,
)
from .results import (
AtomicInput,
Expand Down
2 changes: 0 additions & 2 deletions qcelemental/models/v2/common_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,4 @@ def check_convertible_version(ver: int, error: str):
raise ValueError(f"QCSchema {error} version={version} does not exist for conversion.")


qcschema_torsion_drive_input_default = "qcschema_torsion_drive_input"
qcschema_torsion_drive_output_default = "qcschema_torsion_drive_output"
qcschema_molecule_default = "qcschema_molecule"
Loading

0 comments on commit 318816e

Please sign in to comment.