Skip to content

Commit

Permalink
model reorganization (#366)
Browse files Browse the repository at this point in the history
bare rearrangement

bare rearrangement 2

fix up imports

new molecule fns

mol v2

convert foundational models

clear away version_stamp and placeholder Mol v3

basisset move and OptRes.trajectory rename

post-Christmas, mostly opt/td edits
  • Loading branch information
loriab authored Jan 21, 2025
1 parent 6d5ffb1 commit 11a52a3
Show file tree
Hide file tree
Showing 25 changed files with 966 additions and 628 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/CI.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ on:
branches:
- master
- next2024
- next2024_atop_v29
- next2025
pull_request:
branches:
- master
- next2024
- next2024_atop_v29
- next2025
schedule:
- cron: "9 16 * * 1"

Expand Down
29 changes: 27 additions & 2 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,33 @@ New Features

Enhancements
++++++++++++
- (:pr:`363`)
- (:pr:`363`)
- (:pr:`364`)
- (:pr:`364`)
- (:pr:`364`)
- (:pr:`364`) main storage for ``v2.TorsionDriveResult`` moved from ``optimization_history`` to ``scan_results``.
- (:pr:`364`) ``v2.TorsionDriveInput.initial_molecule`` restored from ``initial_molecules``.
- (:pr:`364`) default of OptimizationProtocol.trajectory_results changed to "none" from "all" in v1. much info can now come from properties.
- (:pr:`364`) v2.OptimizationProtocol renamed trajectory_results from trajectory in accordance with the protocol naming the controlled field. no default change yet.
- (:pr:`364`) v1/v2: import ElectronShell, BasisCenter, ECPPotential from top level models
- (:pr:`364`) molparse learns to pass through schema v3, though no new field for Mol yet.
- (:pr:`364`) ``v2.FailedOperation`` gained schema_name and schema_version=2. unversioned in v1
- (:pr:`364`) ``v2.BasisSet.schema_version`` is now 2, with no layout change.
- (:pr:`364`) ``v2.Molecule.schema_version`` is now 3. convert_v of all the models learned to handle the new schema_version.
- (:pr:`364`) v2: standardizing on In/Res get versions, Ptcl/Kw/Spec get only schema_name. At, Opt, TD
- (:pr:`364`) v1/v2: removing the version_stamps from the models: At, Opt, TD, Fail, BAsis, Mol. so it will error rather than clobber if constructed with wrong version. convert_v now handles.
- (:pr:`364`) convert_v functions learned to handle model.basis=BasisSet, not just str.
- (:pr:`364`) ``Molecule`` and ``BasisSet`` and ``WavefunctionProperties`` learned to ``convert_v`` to interconvert between v1 and v2. No layout changes.
``BasisSet.schema_name`` standardized to ``qcschema_basis_set``.
Both classes get their ``schema_name`` as Literal now
- (:pr:`360`) ``Molecule`` learned new functions ``element_composition`` and ``molecular_weight``.
The first gives a dictionary of element symbols and counts, while the second gives the weight in amu.
Both can access the whole molecule or per-fragment like the existing ``nelectrons`` and
``nuclear_repulsion_energy``. All four can now select all atoms or exclude ghosts (default).
- (:pr:`364`) separated procedures.py and renamed results.py so models are separated into atomic.py, optimization.py, torsion_drive.py, failure models moved to failed_operation.py. basis.py to basis_set.py
- (:pr:`364`) ``schema_name`` output chanded to result ``qcschema_output`` to ``qcschema_atomic_result``. also opt
- (:pr:`364`) ``TDKeywords`` renamed to ``TorsionDriveKeywords``
- (:pr:`364`) ``AtomicResultProtocols`` renamed to ``AtomicProtocols`` and ``AtomicResultProperties`` to ``AtomicProperties``
- (:pr:`364`) new ``v2.TorsionDriveProtocols`` model with field ``scan_results`` to control all/none/lowest saving of optimizationresults at each grid point. Use "all" for proper conversion to v1.
- (:pr:`363`) ``v2.TorsionDriveResult`` no longer inherits from Input and now has indep id and extras and new native_files.
- (:pr:`363`) ``v2.TorsionDriveInput.initial_molecule`` now ``initial_molecules`` as it's a list of >=1 molecules. keep change?
- (:pr:`363`) ``v2. TorsionDriveSpecification`` is a new model. instead of ``v2.TorsionDriveInput`` having a ``input_specification`` and an ``optimization_spec`` fields, it has a ``specification`` field that is a ``TorsionDriveSpecification`` which in turn hold opt info and in turn gradient/atomic info.
Expand Down
2 changes: 1 addition & 1 deletion qcelemental/models/v1/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from .align import AlignmentMill
from .basemodels import AutodocBaseSettings # remove when QCFractal merges `next`
from .basemodels import ProtoModel
from .basis import BasisSet
from .basis import BasisCenter, BasisSet, ECPPotential, ElectronShell
from .common_models import ComputeError, DriverEnum, FailedOperation, Model, Provenance
from .molecule import Molecule
from .procedures import Optimization # scheduled for removal
Expand Down
13 changes: 12 additions & 1 deletion qcelemental/models/v1/basemodels.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,11 +200,22 @@ def compare(self, other: Union["ProtoModel", BaseModel], **kwargs) -> bool:
bool
True if the objects match.
"""
from ..testing import compare_recursive
from ...testing import compare_recursive

return compare_recursive(self, other, **kwargs)


def check_convertible_version(ver: int, error: str):
"""Standardize version/error handling for v1 QCSchema."""

if ver == 1:
return "self"
elif ver == 2:
return True
else:
raise ValueError(f"QCSchema {error} version={ver} does not exist for conversion.")


# remove when QCFractal merges `next`
class AutodocBaseSettings(BaseSettings):
"""Old class for pydantic docstring before autodoc-pydantic came about."""
Expand Down
31 changes: 25 additions & 6 deletions qcelemental/models/v1/basis.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
from enum import Enum
from typing import Dict, List, Literal, Optional
from typing import TYPE_CHECKING, Dict, List, Literal, Optional, Union

from pydantic.v1 import ConstrainedInt, Field, constr, validator

from ...exceptions import ValidationError
from .basemodels import ProtoModel, qcschema_draft
from .basemodels import ProtoModel, check_convertible_version, qcschema_draft

if TYPE_CHECKING:
import qcelemental


class NonnegativeInt(ConstrainedInt):
Expand Down Expand Up @@ -175,10 +178,6 @@ class Config(ProtoModel.Config):
def schema_extra(schema, model):
schema["$schema"] = qcschema_draft

@validator("schema_version", pre=True)
def _version_stamp(cls, v):
return 1

@validator("atom_map")
def _check_atom_map(cls, v, values):
sv = set(v)
Expand Down Expand Up @@ -230,3 +229,23 @@ def _calculate_nbf(cls, atom_map, center_data) -> int:
ret += center_count[center]

return ret

def convert_v(
self, target_version: int, /
) -> Union["qcelemental.models.v1.BasisSet", "qcelemental.models.v2.BasisSet"]:
"""Convert to instance of particular QCSchema version."""
import qcelemental as qcel

if check_convertible_version(target_version, error="BasisSet") == "self":
return self

dself = self.model_dump()
if target_version == 2:
dself.pop("schema_name") # changes in v2
dself.pop("schema_version") # changes in v2

self_vN = qcel.models.v2.BasisSet(**dself)
else:
assert False, target_version

return self_vN
13 changes: 3 additions & 10 deletions qcelemental/models/v1/common_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
import numpy as np
from pydantic.v1 import Field

from .basemodels import ProtoModel, qcschema_draft
from .basemodels import ProtoModel, check_convertible_version, qcschema_draft
from .basis import BasisSet

if TYPE_CHECKING:
from pydantic.v1.typing import ReprArgs

import qcelemental


# Encoders, to be deprecated
ndarray_encoder = {np.ndarray: lambda v: v.flatten().tolist()}
Expand Down Expand Up @@ -147,15 +149,6 @@ def convert_v(
return self_vN


def check_convertible_version(ver: int, error: str):
if ver == 1:
return "self"
elif ver == 2:
return True
else:
raise ValueError(f"QCSchema {error} version={version} does not exist for conversion.")


qcschema_input_default = "qcschema_input"
qcschema_output_default = "qcschema_output"
qcschema_optimization_input_default = "qcschema_optimization_input"
Expand Down
32 changes: 25 additions & 7 deletions qcelemental/models/v1/molecule.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,15 @@
from ...physical_constants import constants
from ...testing import compare, compare_values
from ...util import deserialize, measure_coordinates, msgpackext_loads, provenance_stamp, which_import
from .basemodels import ProtoModel, qcschema_draft
from .basemodels import ProtoModel, check_convertible_version, qcschema_draft
from .common_models import Provenance, qcschema_molecule_default
from .types import Array

if TYPE_CHECKING:
from pydantic.v1.typing import ReprArgs

import qcelemental

# Rounding quantities for hashing
GEOMETRY_NOISE = 8
MASS_NOISE = 6
Expand Down Expand Up @@ -376,12 +378,6 @@ def __init__(self, orient: bool = False, validate: Optional[bool] = None, **kwar
elif validate or geometry_prep:
values["geometry"] = float_prep(values["geometry"], geometry_noise)

@validator("schema_version", pre=True)
def _version_stamp(cls, v):
# seemingly unneeded, this lets conver_v re-label the model w/o discarding model and
# submodel version fields first.
return 2

@validator("geometry")
def _must_be_3n(cls, v, values, **kwargs):
n = len(values["symbols"])
Expand Down Expand Up @@ -1562,6 +1558,28 @@ def scramble(

return cmol, {"rmsd": rmsd, "mill": perturbation}

def convert_v(
self, target_version: int, /
) -> Union["qcelemental.models.v1.Molecule", "qcelemental.models.v2.Molecule"]:
"""Convert to instance of particular QCSchema version."""
import qcelemental as qcel

if check_convertible_version(target_version, error="Molecule") == "self":
return self

loss_store = {}
dself = self.model_dump()
if target_version == 2:
# below is assignment rather than popping so Mol() records as set and future Mol.model_dump() includes the field.
# QCEngine _build_model convert_v(2) can lose it otherwise, and molparse machinery wants to see the field.
dself["schema_version"] = 3

self_vN = qcel.models.v2.Molecule(**dself)
else:
assert False, target_version

return self_vN


def _filter_defaults(dicary):
nat = len(dicary["symbols"])
Expand Down
Loading

0 comments on commit 11a52a3

Please sign in to comment.