Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bsse_type=none #31

Merged
merged 2 commits into from
Jun 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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}
Loading