Skip to content

Commit

Permalink
bsse_type=none (#31)
Browse files Browse the repository at this point in the history
* bsse_type=none

* fix up api docs
  • Loading branch information
loriab authored Jun 1, 2024
1 parent de617f3 commit fd5c4e9
Show file tree
Hide file tree
Showing 10 changed files with 97 additions and 40 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
- label: Py-max
python-version: "3.12"
runs-on: ubuntu-latest
pytest: "-k 'not (he4 and (3b or 4b) and not sio)'"
pytest: "-k '(not (he4 and (3b or 4b) and not sio)) or supersys'"

- label: Demo
python-version: "3.11"
Expand Down
13 changes: 12 additions & 1 deletion docs/api.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
::: qcmanybody.ManyBodyCore
options:
show_root_heading: true

::: qcmanybody.ManyBodyComputer
options:
show_root_heading: true
inherited_members: true
members: true
members:
- from_manybodyinput

$pydantic: qcmanybody.computer.ManyBodyComputer

::: qcmanybody.utils
options:
show_root_heading: true

::: qcmanybody.builder
options:
show_root_heading: true
1 change: 1 addition & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
partial schema dictionary format rather than requiring a constructed `qcelemental.Molecule` object. If the molecule
is a single large fragment, an error is thrown. @loriab
* [\#30](https://github.com/MolSSI/QCManyBody/pull/30) Docs -- add end-to-end demos in test_examples. @loriab
* [\#31](https://github.com/MolSSI/QCManyBody/pull/31) Schema -- add "none" as a bsse_type alias to "nocp". @loriab

#### Bug Fixes

Expand Down
6 changes: 3 additions & 3 deletions docs/core-interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ The core interface of QCManyBody is designed to allow for more flexibility in ho
The primary responsibilities of the core interface are:

1. Given a molecule and desired levels of MBE, return the fragments and levels that must be computed for each fragment
2. Given a dictionary of the results of those computations, analyze those results and calculate the desired manybody properties
2. Given a dictionary of the results of those computations, analyze those results and calculate the desired many-body properties

Note that the user is expected to run the calculations themselves, and the core interface does not provide any tools for
running the calculations.


## Using the core interface

The core interface is accessed through the [`ManyBodyCore`][qcmanybody.manybody.ManyBodyCore]
The core interface is accessed through the [`ManyBodyCore`][qcmanybody.core.ManyBodyCore]
class.

The first step is to create a molecule. This molecule is a
Expand Down Expand Up @@ -57,7 +57,7 @@ map these strings to some meaningful definition of a calculation.

For a complete discussion of the other options available in the `ManyBodyCore` object, see the
[keywords discussion](keywords.md)
the [`ManyBodyCore API documentation`][qcmanybody.manybody.ManyBodyCore].
the [`ManyBodyCore API documentation`][qcmanybody.core.ManyBodyCore].

The next step is to obtain the calculations to be run from the `ManyBodyCore` object.
This is done with a python generator function `iterate_molecules` that returns
Expand Down
4 changes: 2 additions & 2 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ pip install git+https://github.com/MolSSI/QCManyBody.git

The package has two main interfaces. The high-level interface allows for a comprehensive workflow, where the user
provides complete information about the calculation, including the full specification (method, basis set, etc.) of the
manybody calculation. This is designed to work with [QCEngine](https://github.com/MolSSI/QCEngine) or other packages
many-body calculation. This is designed to work with [QCEngine](https://github.com/MolSSI/QCEngine) or other packages
that implement the [QCSchema](https://github.com/MolSSI/QCSchema).

For more information, see [High-level interface](high-level-interface.md).

QCManyBody also has a core low-level interface that allows for more flexibility in how the calculations are run. This
QCManyBody also has a low-level _core_ interface that allows for more flexibility in how the calculations are run. This
interface generally takes a molecule and an arbitrary definition of quantum chemistry specifications, and expects
the user to run them themselves.

Expand Down
3 changes: 3 additions & 0 deletions qcmanybody/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
from qcmanybody.models import BsseEnum, FragBasIndex


__all__ = ["build_nbody_compute_list"]


def build_nbody_compute_list(
bsse_type: Iterable[BsseEnum],
nfragments: int,
Expand Down
20 changes: 15 additions & 5 deletions qcmanybody/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
logger = logging.getLogger(__name__)


__all__ = ["ManyBodyCalculator", "ManyBodyCore"]


class ManyBodyCore:
def __init__(
self,
Expand Down Expand Up @@ -154,6 +157,16 @@ def format_calc_plan(self, sset: str = "all") -> Tuple[str, Dict[str, Dict[int,
-------
info
A text summary with per- model chemistry and per- n-body-level job counts.
```
Model chemistry "c4-ccsd": 22
Number of 1-body computations: 16 (nocp: 0, cp: 0, vmfc_compute: 16)
Number of 2-body computations: 6 (nocp: 0, cp: 0, vmfc_compute: 6)
Model chemistry "c4-mp2": 28
Number of 1-body computations: 12 (nocp: 0, cp: 0, vmfc_compute: 12)
Number of 2-body computations: 12 (nocp: 0, cp: 0, vmfc_compute: 12)
Number of 3-body computations: 4 (nocp: 0, cp: 0, vmfc_compute: 4)
```
Dict[str, Dict[int, int]]
Data structure with outer key mc-label, inner key 1-indexed n-body, value job count.
"""
Expand Down Expand Up @@ -486,17 +499,14 @@ def analyze(
key can be generated with the ``qcmanybody.utils.labeler`` function.
The inner string key is any property; QCManyBody presently knows how
to process energy/gradient/Hessian.
```
{'["ccsd", [1], [1]]': {'energy': -2.87, 'gradient': array([[0., 0., 0.]])},
'["ccsd", [2], [2]]': {'energy': -2.87, 'gradient': array([[0., 0., 0.]])},
'["mp2", [1], [1]]': {'energy': -2.86, 'gradient': array([[0., 0., 0.]])},
'["mp2", [2], [2]]': {'energy': -2.86, 'gradient': array([[0., 0., 0.]])},
'["mp2", [1, 2], [1, 2]]': {'energy': -5.73, 'gradient': array([[ 0., 0., 0.0053], [ 0., 0., -0.0053]])},
}
Return
------
```
"""

# All properties that were passed to us
Expand Down
1 change: 1 addition & 0 deletions qcmanybody/models/manybody_input_pydv1.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ class BsseEnum(str, Enum):
vmfc = "vmfc" # Valiron-Mayer function counterpoise
ssfc = "cp"
mbe = "nocp"
none = "nocp"

def formal(self):
return {
Expand Down
2 changes: 1 addition & 1 deletion qcmanybody/tests/test_mbe_keywords.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ def test_mbe_level_fails(mbe_data, kws, errmsg):
pytest.param({"bsse_type": "nocp"}, [BsseEnum.nocp]),
pytest.param({"bsse_type": ["vmfc"]}, [BsseEnum.vmfc]),
pytest.param({"bsse_type": ["vmfc", "nocp"]}, [BsseEnum.vmfc, BsseEnum.nocp]),
pytest.param({"bsse_type": ["ssFC", "nocp"]}, [BsseEnum.cp, BsseEnum.nocp]),
pytest.param({"bsse_type": ["ssFC", "none"]}, [BsseEnum.cp, BsseEnum.nocp]),
pytest.param({"bsse_type": ["ssfc", "cp"]}, [BsseEnum.cp]),
pytest.param({"bsse_type": ["ssfc", "vmfc", "nocp", "cp"]}, [BsseEnum.cp, BsseEnum.vmfc, BsseEnum.nocp]),
pytest.param({"bsse_type": "mbE"}, [BsseEnum.nocp]),
Expand Down
85 changes: 58 additions & 27 deletions qcmanybody/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,18 @@
from qcmanybody.models import FragBasIndex


__all__ = [
# "collect_vars",
"delabeler",
"labeler",
# "print_nbody_energy",
"provenance_stamp",
"resize_gradient",
"resize_hessian",
# "sum_cluster_data",
]


def find_shape(x: Union[float, np.ndarray]) -> Tuple[int, ...]:
if isinstance(x, float):
return (1,)
Expand Down Expand Up @@ -54,13 +66,13 @@ def resize_gradient(
*,
reverse: bool = False,
) -> np.ndarray:
"""Pads or extracts a gradient array between subsystem and full supersystem sizes.
r"""Pads or extracts a gradient array between subsystem and full supersystem sizes.
Parameters
----------
grad
Gradient matrix of natural size for *bas*, (3 * <nat in bas>, 3).
If `reverse=True`, gradient matrix of supersystem size, (3 * <nat of all fragments>, 3).
Gradient matrix of natural size for *bas*, (3 * _<nat in bas\>_, 3).
If `reverse=True`, gradient matrix of supersystem size, (3 * _<nat of all fragments\>_, 3).
bas
1-indexed fragments active in *grad*.
If `reverse=True`, 1-indexed fragments to be extracted from *grad*.
Expand All @@ -71,12 +83,13 @@ def resize_gradient(
Dictionary containing slices that index the gradient matrix for each of the 1-indexed fragments.
For He--HOOH--Me cluster, `{1: slice(0, 1), 2: slice(1, 5), 3: slice(5, 10)}`.
reverse
If True, contract *grad* from supersystem size and shape that which is natural for *bas*.
If True, contract *grad* from supersystem size and shape to that which is natural for *bas*.
Returns
-------
Gradient array padded with zeros to supersystem size, (3 * <nat of supersystem>, 3).
If reverse=True, gradient array extracted to natural size, (3 * <nat in bas>, 3).
np.ndarray
Gradient array padded with zeros to supersystem size, (3 * _<nat of supersystem\>_, 3).
If reverse=True, gradient array extracted to natural size, (3 * _<nat in bas\>_, 3).
"""
if reverse:
Expand Down Expand Up @@ -105,13 +118,13 @@ def resize_hessian(
*,
reverse: bool = False,
) -> np.ndarray:
"""Pads or extracts a Hessian array between subsystem and full supersystem sizes.
r"""Pads or extracts a Hessian array between subsystem and full supersystem sizes.
Parameters
----------
grad
Hessian matrix of natural size for *bas*, (3 * <nat in bas>, 3 * <nat in bas>).
If `reverse=True`, Hessian matrix of supersystem size, (3 * <nat of all fragments>, 3 * <nat of all fragments>).
hess
Hessian matrix of natural size for *bas*, (3 * _<nat in bas\>_, 3 * _<nat in bas\>_).
If `reverse=True`, Hessian matrix of supersystem size, (3 * _<nat of all fragments\>_, 3 * _<nat of all fragments\>_).
bas
1-indexed fragments active in *hess*.
If `reverse=True`, 1-indexed fragments to be extracted from *hess*.
Expand All @@ -122,12 +135,13 @@ def resize_hessian(
Dictionary containing slices that index the gradient matrix for each of the 1-indexed fragments.
For He--HOOH--Me cluster, `{1: slice(0, 1), 2: slice(1, 5), 3: slice(5, 10)}`.
reverse
If True, contract *hess* from supersystem size and shape that which is natural for *bas*.
If True, contract *hess* from supersystem size and shape to that which is natural for *bas*.
Returns
-------
Hessian array padded with zeros to supersystem size, (3 * <nat of supersystem>, 3 * <nat of supersystem>).
If reverse=True, Hessian array extracted to natural size, (3 * <nat in bas>, 3 * <nat in bas>).
ndarray
Hessian array padded with zeros to supersystem size, (3 * _<nat of supersystem\>_, 3 * _<nat of supersystem\>_).
If reverse=True, Hessian array extracted to natural size, (3 * _<nat in bas\>_, 3 * _<nat in bas\>_).
"""
if reverse:
Expand Down Expand Up @@ -174,19 +188,20 @@ def sum_cluster_data(
A list of (frag, bas) tuples notating all the required computations for the desired sum.
mc_level_lbl
User label for what modelchem level results should be pulled out of *data*.
vmfc, optional
vmfc
Is this a vmfc calculation, by default False?
nb, optional
nb
1-indexed n-body level only used when `vmfc=True`, by default 0.
Returns
-------
Scalar or array containing summed energy, gradient, Hessian, or other result.
Usually (nocp or cp; `vmfc=False`), compute_list defines all fragments of a given number of
active fragments and active basis fragments, so the return is the 3b@3b sum, for example.
Other times (`vmfc=True`), compute list defines all fragments of a given number of active basis
fragments. Then alternating weighting is applied so if `nb=3`, the return is the quantity
(3b@3b sum - 2b@3b sum + 1b@3b sum), for example.
Union[float, np.ndarray]
Scalar or array containing summed energy, gradient, Hessian, or other result.
Usually (nocp or cp; `vmfc=False`), compute_list defines all fragments of a given number of
active fragments and active basis fragments, so the return is the 3b@3b sum, for example.
Other times (`vmfc=True`), compute list defines all fragments of a given number of active basis
fragments. Then alternating weighting is applied so if `nb=3`, the return is the quantity
(3b@3b sum - 2b@3b sum + 1b@3b sum), for example.
Raises
------
Expand Down Expand Up @@ -230,13 +245,13 @@ def labeler(mc_level_lbl: str, frag: Tuple[int, ...], bas: Tuple[int, ...]) -> s
Returns
-------
str
JSON string from inputs: `labeler("mp2",(1), (1, 2))` returns `'["mp2", 1, [1, 2]]'`.
JSON string from inputs: `labeler("mp2", (1), (1, 2))` returns `'["mp2", 1, [1, 2]]'`.
"""
return json.dumps([str(mc_level_lbl), frag, bas])


def delabeler(item: str) -> Tuple[str, Tuple[int, ...], Tuple[int, ...]]:
"""Transform labels like string "1_((2,), (1, 2))" into tuple (1, (2,), (1, 2))."""
"""Back-form from label into tuple: `delabeler('["mp2", 1, [1, 2]]')` returns `('mp2', 1, [1, 2])`."""

mc, frag, bas = json.loads(item)
return str(mc), frag, bas
Expand All @@ -260,7 +275,7 @@ def print_nbody_energy(
Specialization for table title.
nfragments
Number of lines in table is number of fragments.
embedding, optional
embedding
Whether charge embedding present suppress printing, usually False
supersystem_ie_only
Whether only 1-body and nfragments-body levels are available, usually False.
Expand All @@ -269,7 +284,18 @@ def print_nbody_energy(
Returns
-------
A text table Hartrees and kcal/mol
str
A text table in Hartrees and kcal/mol
```
==> N-Body: Counterpoise Corrected (CP) energies <=='
n-Body Total Energy Interaction Energy N-body Contribution to Interaction Energy'
[Eh] [Eh] [kcal/mol] [Eh] [kcal/mol]'
1 -386.455609352609 0.000000000000 0.000000000000 0.000000000000 0.000000000000'
2 -384.203153844163 2.252455508446 1413.437170812134 2.252455508446 1413.437170812134'
FULL/RTN 3 -384.128628718676 2.326980633933 1460.202393089624 0.074525125487 46.765222277490'
```
"""
info = f"""\n ==> N-Body: {header} energies <==\n\n"""
info += f""" {"n-Body":>12} Total Energy Interaction Energy N-body Contribution to Interaction Energy\n"""
Expand Down Expand Up @@ -342,14 +368,15 @@ def collect_vars(
Dictionary of minimal per-body info already specialized for property *prop* and treatment *bsse*. May contain either total data or interaction data, both cummulative not additive, from 1-body to max_nbody-body (see also *supersystem_ie_only*). Interaction data signaled by zero float or array for 1-body. May contain multiple model chemistry levels.
max_nbody
_description_
embedding, optional
embedding
Is charge embedding enabled, by default False?
supersystem_ie_only, optional
supersystem_ie_only
Is data available in *body_dict* only for 1-body (possibly zero) and nfr-body levels? By default False: data is available for consecutive levels, up to max_nbody-body.
has_supersystem
Whether contributions higher than max_nbody are a summary correction.
Returns
-------
dict
_description_. Empty return if *embedding* enabled.
"""
previous_e = body_dict[1]
Expand Down Expand Up @@ -406,6 +433,10 @@ def provenance_stamp(routine: str) -> Dict[str, str]:
with QCManyBody's credentials for creator and version. The
generating routine's name is passed in through `routine`.
```python
qcmb.utils.provenance_stamp(__name__)
#> {'creator': 'QCManyBody', 'version': '0.2.2', 'routine': '__main__'}
```
"""
import qcmanybody
return {"creator": "QCManyBody", "version": qcmanybody.__version__, "routine": routine}

0 comments on commit fd5c4e9

Please sign in to comment.