Skip to content

Commit

Permalink
mbresprop as primary, not qcvars (#32)
Browse files Browse the repository at this point in the history
* mbresprop as primary, not qcvars

* changelog

* align run_qcengine args

* no double success
  • Loading branch information
loriab authored Jun 3, 2024
1 parent 01f9313 commit ddefb75
Show file tree
Hide file tree
Showing 10 changed files with 195 additions and 154 deletions.
12 changes: 12 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,21 @@
from `manybody.py` to `core.py` but it was already a top-level import. @loriab
* [\#30](https://github.com/MolSSI/QCManyBody/pull/30) Intf -- low-level "core" interface now requires named arguments
beyond the first recognizable ones (mol, bsse_type, levels). @loriab
* [\#32](https://github.com/MolSSI/QCManyBody/pull/32) Intf -- "high-level" interface now no longer stores QCVariables
(or any other results dicts) in extras @loriab
* [\#32](https://github.com/MolSSI/QCManyBody/pull/32) Utils -- `qcmanybody.utils.collect_vars` now returns with keys
from ManyBodyResultProperties rather than QCVariables. @loriab
* [\#32](https://github.com/MolSSI/QCManyBody/pull/32) Utils -- arguments rearranged in
`qcmanybody.tests.utils.run_qcengine` (use serial backend for core interface) to align with `ManyBodyCore` init
arguments. @loriab

#### New Features

* [\#32](https://github.com/MolSSI/QCManyBody/pull/32) QCSchema -- a new function
`ManyBodyResultProperties.to_qcvariables()` returns a translation map to QCVariables keys. @loriab
* [\#32](https://github.com/MolSSI/QCManyBody/pull/32) QCSchema -- a new function
`qcmanybody.utils.translate_qcvariables(map)` switches between QCVariable and QCSchema keys. @loriab

#### Enhancements

* [\#28](https://github.com/MolSSI/QCManyBody/pull/28) Intf -- high-level interface is now importable from the top level
Expand Down
1 change: 1 addition & 0 deletions qcmanybody/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
from .utils import delabeler, labeler, resize_gradient, resize_hessian

__version__ = version("qcmanybody")
del version
68 changes: 4 additions & 64 deletions qcmanybody/computer.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
import qcportal


__all__ = ["ManyBodyComputer"]


class BaseComputerQCNG(ProtoModel):
"""Base class for "computers" that plan, run, and process QC tasks."""

Expand Down Expand Up @@ -505,12 +508,6 @@ def get_results(
component_properties = external_results.pop("component_properties")
stdout = external_results.pop("stdout")

# load QCVariables
qcvars = {
"NUCLEAR REPULSION ENERGY": self.molecule.nuclear_repulsion_energy(),
"NBODY NUMBER": nbody_number,
}

properties = {
"calcinfo_nmc": len(self.nbodies_per_mc_level),
"calcinfo_nfr": self.nfragments, # or len(self.molecule.fragments)
Expand All @@ -520,27 +517,12 @@ def get_results(
"return_energy": ret_energy,
}

for k, val in external_results.items():
if k == "results":
k = "nbody"
qcvars[k] = val

qcvars["CURRENT ENERGY"] = ret_energy
if self.driver == "gradient":
qcvars["CURRENT GRADIENT"] = ret_ptype
properties["return_gradient"] = ret_ptype
elif self.driver == "hessian":
qcvars["CURRENT GRADIENT"] = ret_gradient
qcvars["CURRENT HESSIAN"] = ret_ptype
properties["return_gradient"] = ret_gradient
properties["return_hessian"] = ret_ptype

# build_out(qcvars)
atprop = build_manybodyproperties(qcvars["nbody"])
# print("ATPROP")
# v2: pp.pprint(atprop.model_dump())
# pp.pprint(atprop.dict())

# output_data = {
# "schema_version": 1,
# "molecule": gamessmol, # overwrites with outfile Cartesians in case fix_*=F
Expand All @@ -559,30 +541,20 @@ def get_results(
# print("QCVARS PRESCREEN")
# pp.pprint(qcvars)

for qcv, val in qcvars.items():
if not isinstance(val, dict):
qcvars[qcv] = val

# v2: component_results = self.model_dump()['task_list'] # TODO when/where include the indiv outputs
# ?component_results = self.dict()['task_list'] # TODO when/where include the indiv outputs
# for k, val in component_results.items():
# val['molecule'] = val['molecule'].to_schema(dtype=2)

# print("QCVARS")
# pp.pprint(qcvars)

nbody_model = ManyBodyResult(
**{
"input_data": self.input_data,
#'molecule': self.molecule,
# v2: 'properties': {**atprop.model_dump(), **properties},
"properties": {**atprop.dict(), **properties},
"properties": {**external_results["results"], **properties},
"component_properties": component_properties,
"component_results": component_results,
"provenance": provenance_stamp(__name__),
"extras": {
"qcvars": qcvars,
},
"return_result": ret_ptype,
"stdout": stdout,
"success": True,
Expand All @@ -592,35 +564,3 @@ def get_results(
# logger.debug('\nNBODY QCSchema:\n' + pp.pformat(nbody_model.model_dump()))

return nbody_model


qcvars_to_manybodyproperties = {}
# v2: for skprop in ManyBodyResultProperties.model_fields.keys():
for skprop in ManyBodyResultProperties.__fields__.keys():
qcvar = skprop.replace("_body", "-body").replace("_corr", "-corr").replace("_", " ").upper()
qcvars_to_manybodyproperties[qcvar] = skprop
qcvars_to_manybodyproperties["CURRENT ENERGY"] = "return_energy"
qcvars_to_manybodyproperties["CURRENT GRADIENT"] = "return_gradient"
qcvars_to_manybodyproperties["CURRENT HESSIAN"] = "return_hessian"


def build_manybodyproperties(qcvars: Mapping) -> ManyBodyResultProperties:
"""For results extracted from QC output in QCDB terminology, translate to QCSchema terminology.
Parameters
----------
qcvars : PreservingDict
Dictionary of calculation information in QCDB QCVariable terminology.
Returns
-------
atprop : ManyBodyResultProperties
Object of calculation information in QCSchema ManyBodyResultProperties terminology.
"""
atprop = {}
for pv, dpv in qcvars.items():
if pv in qcvars_to_manybodyproperties:
atprop[qcvars_to_manybodyproperties[pv]] = dpv

return ManyBodyResultProperties(**atprop)
4 changes: 2 additions & 2 deletions qcmanybody/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -565,8 +565,8 @@ def analyze(
for bt in self.bsse_type:
nbody_dict.update(
collect_vars(
bt.upper(),
property_label.upper(),
bt,
property_label,
all_results[f"{property_label}_body_dict"][bt],
self.max_nbody,
is_embedded,
Expand Down
33 changes: 32 additions & 1 deletion qcmanybody/models/manybody_output_pydv1.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,37 @@ class Config(ProtoModel.Config):
)


def _qcvars_translator(cls, reverse: bool = False) -> Dict[str, str]:
"""Form translation map between many-body results QCSchema and Psi4/QCDB terminologies.
Parameters
----------
reverse
Keys are QCVariable names (`reverse=True`) rather than QCSchema names (default; `reverse=False`).
Returns
-------
dict
Map from ManyBodyResultProperties field names to QCVariable names, or reverse.
"""
qcvars_to_mbprop = {}
# v2: for skprop in ManyBodyResultProperties.model_fields.keys():
for skprop in cls.__fields__.keys():
qcvar = skprop.replace("_body", "-body").replace("_corr", "-corr").replace("_", " ").upper()
qcvars_to_mbprop[qcvar] = skprop
for ret in ["energy", "gradient", "hessian"]:
qcvars_to_mbprop[f"CURRENT {ret.upper()}"] = f"return_{ret}"

if reverse:
return qcvars_to_mbprop
else:
return {v: k for k, v in qcvars_to_mbprop.items()}


ManyBodyResultProperties.to_qcvariables = classmethod(_qcvars_translator)


# ==== Results ================================================================


Expand Down Expand Up @@ -353,7 +384,7 @@ class ManyBodyResult(SuccessfulResultBase):
description="The primary logging output of the program, whether natively standard output or a file. Presence vs. absence (or null-ness?) configurable by protocol.",
)
stderr: Optional[str] = Field(None, description="The standard error of the program execution.")
success: Literal[True] = Field(True, description="Always `True` for a successful result")
# v2: success: Literal[True] = Field(True, description="Always `True` for a successful result")

@validator("component_results")
def _component_results(cls, value, values):
Expand Down
54 changes: 31 additions & 23 deletions qcmanybody/tests/test_mbe_he4_multilevel.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,14 @@
# v2: from qcelemental.models.procedures_manybody import AtomicSpecification, ManyBodyKeywords, ManyBodyInput
from qcelemental.testing import compare_recursive, compare_values

from qcmanybody.computer import ManyBodyComputer, qcvars_to_manybodyproperties
from qcmanybody.models import AtomicSpecification, ManyBodyInput, ManyBodyKeywords
from qcmanybody.computer import ManyBodyComputer
from qcmanybody.models import AtomicSpecification, ManyBodyInput, ManyBodyKeywords, ManyBodyResultProperties
from qcmanybody.utils import translate_qcvariables

from .addons import using, uusing
from .test_mbe_he4_singlelevel import sumdict as sumdict_single


def skprop(qcvar):
# qcng: return qcng.procedures.manybody.qcvars_to_manybodyproperties[qcvar]
return qcvars_to_manybodyproperties[qcvar]


@pytest.fixture(scope="function")
def mbe_data_multilevel_631g():
# note that spherical/cartesian irrelevant for He & 6-31G, and fc/ae irrelevant for He
Expand Down Expand Up @@ -731,6 +727,12 @@ def test_nbody_he4_multi(levels, mbe_keywords, anskey, bodykeys, outstrs, calcin
print(f"MMMMMMM {request.node.name}")
pprint.pprint(ret.dict(), width=200)

# don't want QCVariables stashed in extras, but prepare the qcvars translation, and check it
assert ret.extras == {}, f"[w] extras wrongly present: {ret.extras.keys()}"
qcvars = translate_qcvariables(ret.properties.dict())

skprop = ManyBodyResultProperties.to_qcvariables(reverse=True)

refs = he4_refs_conv_multilevel_631g[pattern]
ans = refs[anskey]
ref_nmbe = calcinfo_nmbe[pattern]
Expand All @@ -739,29 +741,29 @@ def test_nbody_he4_multi(levels, mbe_keywords, anskey, bodykeys, outstrs, calcin
atol = 2.5e-8

for qcv, ref in refs.items():
skp = skprop(qcv)
skp = skprop[qcv]
if qcv in ref_bodykeys:
assert compare_values(ref, ret.extras["qcvars"]["nbody"][qcv], atol=atol, label=f"[a] qcvars {qcv}")
assert compare_values(ref, qcvars[qcv], atol=atol, label=f"[a] qcvars {qcv}")
assert compare_values(ref, getattr(ret.properties, skp), atol=atol, label=f"[b] skprop {skp}")
else:
assert qcv not in ret.extras["qcvars"]["nbody"], f"[z] {qcv=} wrongly present"
assert qcv not in qcvars, f"[z] {qcv=} wrongly present"
assert getattr(ret.properties, skp) is None

for qcv in sumdict["4b_all"]:
skp = skprop(qcv)
skp = skprop[qcv]
if qcv in ref_sumdict:
ref = refs[ref_sumdict[qcv]]
assert compare_values(ref, ret.extras["qcvars"]["nbody"][qcv], atol=atol, label=f"[c] qcvars {qcv}")
assert compare_values(ref, qcvars[qcv], atol=atol, label=f"[c] qcvars {qcv}")
assert compare_values(ref, getattr(ret.properties, skp), atol=atol, label=f"[d] skprop {skp}")
else:
assert qcv not in ret.extras["qcvars"]["nbody"], f"[y] {qcv=} wrongly present"
assert qcv not in qcvars, f"[y] {qcv=} wrongly present"
assert getattr(ret.properties, skp) is None

for qcv, ref in {
"CURRENT ENERGY": ans,
}.items():
skp = skprop(qcv)
assert compare_values(ref, ret.extras["qcvars"][qcv], atol=atol, label=f"[e] qcvars {qcv}")
skp = skprop[qcv]
assert compare_values(ref, qcvars[qcv], atol=atol, label=f"[e] qcvars {qcv}")
assert compare_values(ref, getattr(ret.properties, skp), atol=atol, label=f"[f] skprop {skp}")
assert compare_values(ans, ret.return_result, atol=atol, label=f"[g] ret")

Expand Down Expand Up @@ -825,6 +827,12 @@ def test_nbody_he4_supersys(levels, mbe_keywords, anskey, bodykeys, outstrs, cal
print(f"MMMMMMM {request.node.name}")
pprint.pprint(ret.dict(), width=200)

# don't want QCVariables stashed in extras, but prepare the qcvars translation, and check it
assert ret.extras == {}, f"[w] extras wrongly present: {ret.extras.keys()}"
qcvars = translate_qcvariables(ret.properties.dict())

skprop = ManyBodyResultProperties.to_qcvariables(reverse=True)

refs = he4_refs_conv_multilevel_631g[pattern]
ans = refs[anskey]
ref_nmbe = calcinfo_nmbe[pattern]
Expand All @@ -833,29 +841,29 @@ def test_nbody_he4_supersys(levels, mbe_keywords, anskey, bodykeys, outstrs, cal
atol = 2.5e-8

for qcv, ref in refs.items():
skp = skprop(qcv)
skp = skprop[qcv]
if qcv in ref_bodykeys:
assert compare_values(ref, ret.extras["qcvars"]["nbody"][qcv], atol=atol, label=f"[a] qcvars {qcv}")
assert compare_values(ref, qcvars[qcv], atol=atol, label=f"[a] qcvars {qcv}")
assert compare_values(ref, getattr(ret.properties, skp), atol=atol, label=f"[b] skprop {skp}")
else:
assert qcv not in ret.extras["qcvars"]["nbody"], f"[z] {qcv=} wrongly present"
assert qcv not in qcvars, f"[z] {qcv=} wrongly present"
assert getattr(ret.properties, skp) is None

for qcv in sumdict["4b_all"]:
skp = skprop(qcv)
skp = skprop[qcv]
if qcv in ref_sumdict:
ref = refs[ref_sumdict[qcv]]
assert compare_values(ref, ret.extras["qcvars"]["nbody"][qcv], atol=atol, label=f"[c] qcvars {qcv}")
assert compare_values(ref, qcvars[qcv], atol=atol, label=f"[c] qcvars {qcv}")
assert compare_values(ref, getattr(ret.properties, skp), atol=atol, label=f"[d] skprop {skp}")
else:
assert qcv not in ret.extras["qcvars"]["nbody"], f"[y] {qcv=} wrongly present"
assert qcv not in qcvars, f"[y] {qcv=} wrongly present"
assert getattr(ret.properties, skp) is None

for qcv, ref in {
"CURRENT ENERGY": ans,
}.items():
skp = skprop(qcv)
assert compare_values(ref, ret.extras["qcvars"][qcv], atol=atol, label=f"[e] qcvars {qcv}")
skp = skprop[qcv]
assert compare_values(ref, qcvars[qcv], atol=atol, label=f"[e] qcvars {qcv}")
assert compare_values(ref, getattr(ret.properties, skp), atol=atol, label=f"[f] skprop {skp}")
assert compare_values(ans, ret.return_result, atol=atol, label=f"[g] ret")

Expand Down
Loading

0 comments on commit ddefb75

Please sign in to comment.