From 59903246cda2339a5ed546cfbfd609f4038277a3 Mon Sep 17 00:00:00 2001 From: Benjamin Pritchard Date: Fri, 19 Apr 2024 15:42:12 -0400 Subject: [PATCH 1/2] Move initial qcengine runner to test utils --- qcmanybody/qcengine_helper.py | 62 ----------------------------------- qcmanybody/tests/utils.py | 50 ++++++++++++++++++++++++++-- 2 files changed, 47 insertions(+), 65 deletions(-) delete mode 100644 qcmanybody/qcengine_helper.py diff --git a/qcmanybody/qcengine_helper.py b/qcmanybody/qcengine_helper.py deleted file mode 100644 index 718b4a6..0000000 --- a/qcmanybody/qcengine_helper.py +++ /dev/null @@ -1,62 +0,0 @@ -from typing import Mapping, Literal, Union, Iterable, Any - -import qcengine as qcng -from qcelemental.models import AtomicInput - -from qcmanybody.manybody import Molecule, ManyBodyCalculator -from qcmanybody.models import BsseEnum -from qcmanybody.utils import delabeler - - -def run_qcengine_base( - molecule: Molecule, - levels: Mapping[Union[int, Literal["supersystem"]], str], - specifications: Mapping[str, Mapping[str, Any]], - bsse_type: Iterable[BsseEnum], - return_total_data: bool, -): - - mc = ManyBodyCalculator(molecule, bsse_type, levels, return_total_data) - - component_results = {} - - computation_count = {} - for chem, label, imol in mc.iterate_molecules(): - print(label) - inp = AtomicInput(molecule=imol, **specifications[chem]["specification"]) - - _, real, bas = delabeler(label) - computation_count.setdefault(len(real), 0) - computation_count[len(real)] += 1 - - result = qcng.compute(inp, specifications[chem]["program"]) - - if not result.success: - print(result.error.error_message) - raise RuntimeError("Calculation did not succeed! Error:\n" + result.error.error_message) - - # pull out stuff - props = {"energy", "gradient", "hessian"} - - component_results[label] = {} - - for p in props: - if hasattr(result.properties, f"return_{p}"): - v = getattr(result.properties, f"return_{p}") - # print(f" {label} {p}: {v}") - if v is not None: - component_results[label][p] = v - - return mc, component_results - - -def run_qcengine( - molecule: Molecule, - levels: Mapping[Union[int, Literal["supersystem"]], str], - specifications: Mapping[str, Mapping[str, Any]], - bsse_type: Iterable[BsseEnum], - return_total_data: bool, -): - - mc, component_results = run_qcengine_base(molecule, levels, specifications, bsse_type, return_total_data) - return mc.analyze(component_results) diff --git a/qcmanybody/tests/utils.py b/qcmanybody/tests/utils.py index 8c7c494..fba203d 100644 --- a/qcmanybody/tests/utils.py +++ b/qcmanybody/tests/utils.py @@ -1,10 +1,14 @@ import json import math import os +from typing import Mapping, Union, Literal, Any, Iterable import numpy +import qcengine as qcng import zstandard +from qcelemental.models import Molecule, AtomicInput +from qcmanybody import ManyBodyCalculator, delabeler from qcmanybody.models import BsseEnum _my_dir = os.path.dirname(os.path.realpath(__file__)) @@ -62,9 +66,7 @@ def load_component_data(file_base): def generate_component_data(mol, levels, specifications, bsse_type, return_total_data, out_filename): - from qcmanybody.qcengine_helper import run_qcengine_base - - mc, component_results = run_qcengine_base(mol, levels, specifications, bsse_type, return_total_data) + mc, component_results = run_qcengine(mol, levels, specifications, bsse_type, return_total_data) component_results = jsonify(component_results) filepath = os.path.join(_my_dir, "component_data", out_filename + ".json.zst") @@ -154,3 +156,45 @@ def compare_results(qcmb_results, ref_results, levels): res[f"{bstr}-CORRECTED {l}-BODY CONTRIBUTION TO ENERGY"], ref_results[f"{bstr}-CORRECTED {l}-BODY CONTRIBUTION TO ENERGY"], ) + + +def run_qcengine( + molecule: Molecule, + levels: Mapping[Union[int, Literal["supersystem"]], str], + specifications: Mapping[str, Mapping[str, Any]], + bsse_type: Iterable[BsseEnum], + return_total_data: bool, +): + + mc = ManyBodyCalculator(molecule, bsse_type, levels, return_total_data) + + component_results = {} + + computation_count = {} + for chem, label, imol in mc.iterate_molecules(): + print(label) + inp = AtomicInput(molecule=imol, **specifications[chem]["specification"]) + + _, real, bas = delabeler(label) + computation_count.setdefault(len(real), 0) + computation_count[len(real)] += 1 + + result = qcng.compute(inp, specifications[chem]["program"]) + + if not result.success: + print(result.error.error_message) + raise RuntimeError("Calculation did not succeed! Error:\n" + result.error.error_message) + + # pull out stuff + props = {"energy", "gradient", "hessian"} + + component_results[label] = {} + + for p in props: + if hasattr(result.properties, f"return_{p}"): + v = getattr(result.properties, f"return_{p}") + # print(f" {label} {p}: {v}") + if v is not None: + component_results[label][p] = v + + return mc, component_results From c34f9376db1a60a6373e3334cc36f45120da0e72 Mon Sep 17 00:00:00 2001 From: Benjamin Pritchard Date: Sat, 20 Apr 2024 10:08:38 -0400 Subject: [PATCH 2/2] Some small cleanup (typing mostly) --- qcmanybody/builder.py | 11 +++++++---- qcmanybody/manybody.py | 2 +- qcmanybody/tests/utils.py | 7 ++++--- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/qcmanybody/builder.py b/qcmanybody/builder.py index dd6576c..42e3441 100644 --- a/qcmanybody/builder.py +++ b/qcmanybody/builder.py @@ -1,7 +1,7 @@ from __future__ import annotations import itertools -from typing import Iterable, Union, Literal, Optional, Dict, Set +from typing import Iterable, Union, Literal, Optional, Dict, Set, List from qcmanybody.models import BsseEnum, FragBasIndex @@ -29,7 +29,10 @@ def build_nbody_compute_list( Whether the total data (True; energy/gradient/Hessian) of the molecular system has been requested, as opposed to interaction data (False). supersystem_ie_only - ???? + Target the supersystem total/interaction energy (IE) data over the many-body expansion (MBE) " + analysis, thereby omitting intermediate-body calculations. + supersystem_max_nbody + Maximum n-body to use for a supersystem calculation. Must be specified if "supersystem" is in `nbodies` Returns ------- @@ -62,8 +65,8 @@ def build_nbody_compute_list( raise ValueError("supersystem_max_nbody must be provided if 'supersystem' contains nbodies") include_supersystem = True - nbodies = list(nbodies) - nbodies.remove("supersystem") + + nbodies: List[int] = [x for x in nbodies if x != "supersystem"] # What levels do we need? fragment_range = range(1, nfragments + 1) diff --git a/qcmanybody/manybody.py b/qcmanybody/manybody.py index b22298c..71004e0 100644 --- a/qcmanybody/manybody.py +++ b/qcmanybody/manybody.py @@ -452,7 +452,7 @@ def analyze( # All properties that were passed to us # * seed with "energy" so free/no-op jobs can process - available_properties = set(["energy"]) + available_properties: Set[str] = {"energy"} for property_data in component_results.values(): available_properties.update(property_data.keys()) diff --git a/qcmanybody/tests/utils.py b/qcmanybody/tests/utils.py index fba203d..655affe 100644 --- a/qcmanybody/tests/utils.py +++ b/qcmanybody/tests/utils.py @@ -65,8 +65,8 @@ def load_component_data(file_base): return unjsonify(json.load(f)) -def generate_component_data(mol, levels, specifications, bsse_type, return_total_data, out_filename): - mc, component_results = run_qcengine(mol, levels, specifications, bsse_type, return_total_data) +def generate_component_data(mol, levels, specifications, bsse_type, return_total_data, out_filename, supsersytem_ie_only=False): + mc, component_results = run_qcengine(mol, levels, specifications, bsse_type, return_total_data, supsersytem_ie_only) component_results = jsonify(component_results) filepath = os.path.join(_my_dir, "component_data", out_filename + ".json.zst") @@ -164,9 +164,10 @@ def run_qcengine( specifications: Mapping[str, Mapping[str, Any]], bsse_type: Iterable[BsseEnum], return_total_data: bool, + supersystem_ie_only: bool ): - mc = ManyBodyCalculator(molecule, bsse_type, levels, return_total_data) + mc = ManyBodyCalculator(molecule, bsse_type, levels, return_total_data, supersystem_ie_only) component_results = {}