-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* separate schema files, move computer * rename * missed one
- Loading branch information
Showing
8 changed files
with
289 additions
and
553 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,12 @@ | ||
from .manybody_pydv1 import ( | ||
from .manybody_input_pydv1 import ( | ||
AtomicSpecification, | ||
BsseEnum, | ||
FragBasIndex, | ||
ManyBodyInput, | ||
ManyBodyKeywords, | ||
) | ||
from .manybody_output_pydv1 import ( | ||
ManyBodyResult, | ||
ManyBodyResultProperties, | ||
MAX_NBODY | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,238 @@ | ||
|
||
from __future__ import annotations | ||
|
||
from enum import Enum, IntEnum | ||
from typing import Any, Dict, List, Optional, Literal, Tuple, Union, TYPE_CHECKING | ||
|
||
# v2: from pydantic import create_model, Field, field_validator, FieldValidationInfo | ||
try: | ||
from pydantic.v1 import create_model, Field, validator | ||
except ImportError: | ||
from pydantic import create_model, Field, validator | ||
|
||
from qcelemental.models.types import Array | ||
#from .basemodels import ExtendedConfigDict, ProtoModel | ||
from qcelemental.models.common_models import Model | ||
from qcelemental.models.molecule import Molecule | ||
from qcelemental.models.results import AtomicResultProperties, AtomicResultProtocols | ||
from qcelemental.models import DriverEnum, ProtoModel, Provenance | ||
|
||
|
||
# ==== Misplaced & Next Models ================================================ | ||
|
||
class AtomicSpecification(ProtoModel): | ||
"""Specification for a single point QC calculation""" | ||
|
||
keywords: Dict[str, Any] = Field({}, description="The program specific keywords to be used.") | ||
program: str = Field(..., description="The program for which the Specification is intended.") | ||
|
||
schema_name: Literal["qcschema_atomicspecification"] = "qcschema_atomicspecification" | ||
driver: DriverEnum = Field(..., description=DriverEnum.__doc__) | ||
model: Model = Field(..., description=Model.__doc__) | ||
protocols: AtomicResultProtocols = Field( | ||
AtomicResultProtocols(), | ||
description=AtomicResultProtocols.__doc__, | ||
) | ||
|
||
|
||
class ResultBase(ProtoModel): | ||
"""Base class for all result classes""" | ||
|
||
#input_data: InputBase = Field(..., description=InputBase.__doc__) | ||
input_data: Any | ||
success: bool = Field( | ||
..., | ||
description="A boolean indicator that the operation succeeded or failed. Allows programmatic assessment of " | ||
"all results regardless of if they failed or succeeded by checking `result.success`.", | ||
) | ||
stdout: Optional[str] = Field( | ||
None, | ||
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.") | ||
|
||
|
||
class SuccessfulResultBase(ResultBase): | ||
"""Base object for any successful result""" | ||
|
||
success: Literal[True] = Field(True, description="Always `True` for a successful result") | ||
|
||
|
||
# ==== Protocols ============================================================== | ||
|
||
class ManyBodyProtocolEnum(str, Enum): | ||
""" | ||
Which atomic evaluations to keep in a many body evaluation. | ||
""" | ||
|
||
all = "all" | ||
all_real = "all_real" | ||
largest_body = "largest_body" | ||
none = "none" | ||
|
||
|
||
class ManyBodyProtocols(ProtoModel): | ||
""" | ||
Protocols regarding the manipulation of a ManyBody output data. | ||
""" | ||
|
||
atomics: ManyBodyProtocolEnum = Field( | ||
ManyBodyProtocolEnum.all, | ||
description=str(ManyBodyProtocolEnum.__doc__), | ||
) | ||
|
||
# v2: model_config = ExtendedConfigDict(force_skip_defaults=True) | ||
class Config: | ||
force_skip_defaults = True | ||
|
||
|
||
# ==== Inputs ================================================================= | ||
|
||
class BsseEnum(str, Enum): | ||
"""Available basis-set superposition error (BSSE) treatments.""" | ||
|
||
nocp = "nocp" # plain supramolecular interaction energy | ||
cp = "cp" # Boys-Bernardi counterpoise correction; site-site functional counterpoise (SSFC) | ||
vmfc = "vmfc" # Valiron-Mayer function counterpoise | ||
ssfc = "cp" | ||
|
||
def formal(self): | ||
return { | ||
"nocp": "Non-Counterpoise Corrected", | ||
"cp": "Counterpoise Corrected", | ||
"vmfc": "Valiron-Mayer Function Counterpoise", | ||
}[self] | ||
|
||
def abbr(self): | ||
return { | ||
"nocp": "NoCP", | ||
"cp": "CP", | ||
"vmfc": "VMFC", | ||
}[self] | ||
|
||
|
||
FragBasIndex = Tuple[Tuple[int], Tuple[int]] | ||
|
||
|
||
class ManyBodyKeywords(ProtoModel): | ||
"""The many-body-specific keywords for user control.""" | ||
|
||
bsse_type: List[BsseEnum] = Field( | ||
[BsseEnum.cp], | ||
# definitive description | ||
description="Requested BSSE treatments. First in list determines which interaction or total " | ||
"energy/gradient/Hessian returned.", | ||
) | ||
embedding_charges: Dict[int, List[float]] = Field( | ||
{}, | ||
description="Atom-centered point charges to be used on molecule fragments whose basis sets are not included in " | ||
"the computation. Keys: 1-based index of fragment. Values: list of atom charges for that fragment.", | ||
# TODO embedding charges should sum to fragment charge, right? enforce? | ||
# TODO embedding charges irrelevant to CP (basis sets always present)? | ||
json_schema_extra={ | ||
"shape": ["nfr", "<varies: nat in ifr>"], | ||
}, | ||
) | ||
return_total_data: Optional[bool] = Field( | ||
None, | ||
validate_default=True, | ||
# definitive description | ||
description="When True, returns the total data (energy/gradient/Hessian) of the system, otherwise returns " | ||
"interaction data. Default is False for energies, True for gradients and Hessians. Note that the calculation " | ||
"of counterpoise corrected total energies implies the calculation of the energies of monomers in the monomer " | ||
"basis, hence specifying ``return_total_data = True`` may carry out more computations than " | ||
"``return_total_data = False``. For gradients and Hessians, ``return_total_data = False`` is rarely useful.", | ||
) | ||
levels: Optional[Dict[Union[int, Literal["supersystem"]], str]] = Field( | ||
None, | ||
# definitive description. appended in Computer | ||
description="Dictionary of different levels of theory for different levels of expansion. Note that the primary " | ||
"method_string is not used when this keyword is given. ``supersystem`` computes all higher order n-body " | ||
"effects up to the number of fragments; this higher-order correction uses the nocp basis, regardless of " | ||
"bsse_type. A method fills in for any lower unlisted nbody levels. Note that if " | ||
"both this and max_nbody are provided, they must be consistent. Examples: " | ||
"SUPERSYSTEM definition suspect" | ||
"* {1: 'ccsd(t)', 2: 'mp2', 'supersystem': 'scf'} " | ||
"* {2: 'ccsd(t)/cc-pvdz', 3: 'mp2'} " | ||
"* Now invalid: {1: 2, 2: 'ccsd(t)/cc-pvdz', 3: 'mp2'} ", | ||
) | ||
max_nbody: Optional[int] = Field( | ||
None, | ||
validate_default=True, | ||
# definitive description | ||
description="Maximum number of bodies to include in the many-body treatment. Possible: max_nbody <= nfragments. " | ||
"Default: max_nbody = nfragments.", | ||
) | ||
supersystem_ie_only: Optional[bool] = Field( | ||
False, | ||
validate_default=True, | ||
# definitive description | ||
description="Target the supersystem total/interaction energy (IE) data over the many-body expansion (MBE) " | ||
"analysis, thereby omitting intermediate-body calculations. When False (default), compute each n-body level " | ||
"in the MBE up through ``max_nbody``. When True (only allowed for ``max_nbody = nfragments``), only compute " | ||
"enough for the overall interaction/total energy: max_nbody-body and 1-body. When True, properties " | ||
"``INTERACTION {driver} THROUGH {max_nbody}-BODY`` will always be available; ``TOTAL {driver} THROUGH " | ||
"{max_nbody}-BODY`` will be available depending on ``return_total_data``; and ``{max_nbody}-BODY " | ||
"CONTRIBUTION TO {driver}`` won't be available (except for dimers). This keyword produces no savings for a " | ||
"two-fragment molecule. But for the interaction energy of a three-fragment molecule, for example, 2-body " | ||
"subsystems can be skipped with ``supersystem_ie_only=True`` Do not use with ``vmfc`` in ``bsse_type``" | ||
"as it cannot produce savings." | ||
) | ||
|
||
# v2: @field_validator("bsse_type", mode="before") | ||
@validator("bsse_type", pre=True) | ||
@classmethod | ||
def set_bsse_type(cls, v: Any) -> List[BsseEnum]: | ||
if not isinstance(v, list): | ||
v = [v] | ||
# emulate ordered set | ||
# * bt.lower() as return (w/i `list(dict.fromkeys([bt.lower() ...`) | ||
# works until aliases added to BsseEnum | ||
# * BsseEnum[bt].value as return works for good vals, but passing bad | ||
# vals through as bt lets pydantic raise a clearer error message | ||
return list(dict.fromkeys([(BsseEnum[bt.lower()].value if bt.lower() in BsseEnum.__members__ else bt.lower()) for bt in v])) | ||
|
||
|
||
class ManyBodySpecification(ProtoModel): | ||
"""Combining the what (ManyBodyKeywords) with the how (AtomicSpecification).""" | ||
|
||
schema_name: Literal["qcschema_manybodyspecification"] = "qcschema_manybodyspecification" | ||
#provenance: Provenance = Field(Provenance(**provenance_stamp(__name__)), description=Provenance.__doc__) | ||
keywords: ManyBodyKeywords = Field(..., description=ManyBodyKeywords.__doc__) | ||
#program: str = Field(..., description="The program for which the Specification is intended.") | ||
driver: DriverEnum = Field( | ||
..., | ||
description="The computation driver; i.e., energy, gradient, hessian.", | ||
) | ||
#specification: Union[AtomicSpecification, Dict[str, AtomicSpecification]] = Field( | ||
specification: Dict[str, AtomicSpecification] = Field( | ||
..., | ||
description="??? TODO expand to cbs, fd", | ||
) | ||
|
||
# v2: @field_validator("specification", mode="before") | ||
@validator("specification", pre=True) | ||
@classmethod | ||
def set_specification(cls, v: Any) -> Dict[str, AtomicSpecification]: | ||
#print(f"hit atomicspecification validator with {type(v)=} {v}", end="") | ||
# v could be model instance or dict | ||
if isinstance(v, AtomicSpecification) or "model" in v: | ||
v = {"(auto)": v} | ||
#print(f" ... setting v={v}") | ||
return v | ||
|
||
|
||
class ManyBodyInput(ProtoModel): | ||
"""Combining the what and how (ManyBodySpecification) with the who (Molecule).""" | ||
|
||
schema_name: Literal["qcschema_manybodyinput"] = "qcschema_manybodyinput" | ||
#provenance: Provenance = Field(Provenance(**provenance_stamp(__name__)), description=Provenance.__doc__) | ||
specification: ManyBodySpecification = Field( | ||
..., | ||
description="???", | ||
) | ||
molecule: Molecule = Field( | ||
..., | ||
description="Target molecule for many-body expansion (MBE) or interaction energy (IE) analysis.", | ||
) | ||
#protocols |
Oops, something went wrong.