diff --git a/docs/changelog.rst b/docs/changelog.rst index 4d35bdca..8693b77f 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -33,40 +33,39 @@ New Features ++++++++++++ * Downstream code should ``from qcelemental.models.v1 import Molecule, AtomicResult`` etc. to assure medium-term availability of existing models. * New pydantic v2 models available as ``from qcelemental.models.v2 import Molecule, AtomicResult`` etc. -- (:pr:`361`) Switch from poetry to setuptools build backend. - -Enhancements -++++++++++++ -- (: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. +- (:pr:`361`) Switch from poetry to setuptools build backend. + +Enhancements +++++++++++++ +- (:pr:`367`) ``v1.TorsionDriveResult.final_energies`` replaced by larger ``v2.TorsionDriveResult.scan_properties``. the former is present in the latter as ``return_energy``. +- (:pr:`367`) ``v2.TorsionDriveProperties`` added mostly as a placeholder class (one obvious prop at present). +- (:pr:`366`) main storage for ``v2.TorsionDriveResult`` moved from ``optimization_history`` to ``scan_results``. +- (:pr:`366`) ``v2.TorsionDriveInput.initial_molecule`` restored from ``initial_molecules``. +- (:pr:`366`) default of OptimizationProtocol.trajectory_results changed to "none" from "all" in v1. much info can now come from properties. +- (:pr:`366`) v2.OptimizationProtocol renamed trajectory_results from trajectory in accordance with the protocol naming the controlled field. no default change yet. +- (:pr:`366`) v1/v2: import ElectronShell, BasisCenter, ECPPotential from top level models +- (:pr:`366`) molparse learns to pass through schema v3, though no new field for Mol yet. +- (:pr:`366`) ``v2.FailedOperation`` gained schema_name and schema_version=2. unversioned in v1 +- (:pr:`366`) ``v2.BasisSet.schema_version`` is now 2, with no layout change. +- (:pr:`366`) ``v2.Molecule.schema_version`` is now 3. convert_v of all the models learned to handle the new schema_version. +- (:pr:`366`) v2: standardizing on In/Res get versions, Ptcl/Kw/Spec get only schema_name. At, Opt, TD +- (:pr:`366`) 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:`366`) convert_v functions learned to handle model.basis=BasisSet, not just str. +- (:pr:`366`) ``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:`366`) 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:`366`) ``schema_name`` output chanded to result ``qcschema_output`` to ``qcschema_atomic_result``. also opt +- (:pr:`366`) ``TDKeywords`` renamed to ``TorsionDriveKeywords`` +- (:pr:`366`) ``AtomicResultProtocols`` renamed to ``AtomicProtocols`` and ``AtomicResultProperties`` to ``AtomicProperties`` +- (:pr:`366`) 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. +- (: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. - (:pr:`363`) ``v2.TDKeywords`` got a ``schema_name`` field. - (:pr:`363`) ``native_files`` field added to ``v2.OptimizationResult`` and ``v2.TorsionDriveResult`` gained a ``native_files`` field, though not protocols for user control. - (:pr:`363`) ``v2.AtomicResult.convert_v()`` learned external_protocols option to inject that field if known from OptIn diff --git a/pyproject.toml b/pyproject.toml index 596314ca..236486e8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta" [project.urls] homepage = "https://github.com/MolSSI/QCElemental" -changelog = "https://github.com/MolSSI/QCElemental/docs/changelog.rst" -documentation = "http://docs.qcarchive.molssi.org/projects/qcelemental/en/latest/" +changelog = "https://github.com/MolSSI/QCElemental/blob/master/docs/changelog.rst" +documentation = "https://molssi.github.io/QCElemental/" issues = "https://github.com/MolSSI/QCElemental/issues" [project] diff --git a/qcelemental/models/v1/procedures.py b/qcelemental/models/v1/procedures.py index 3efd80be..2ffc3df2 100644 --- a/qcelemental/models/v1/procedures.py +++ b/qcelemental/models/v1/procedures.py @@ -549,13 +549,24 @@ def convert_v( dtop["stdout"] = dself.pop("stdout") dtop["stderr"] = dself.pop("stderr") dtop["success"] = dself.pop("success") - dtop["final_energies"] = dself.pop("final_energies") + dtop["scan_properties"] = { + k: { + "nuclear_repulsion_energy": mol.nuclear_repulsion_energy(), + "return_energy": dself["final_energies"][k], + # "optimization_iterations": + } + for k, mol in self.final_molecules.items() + } + dself.pop("final_energies") dself.pop("final_molecules") dtop["final_molecules"] = {k: m.convert_v(target_version) for k, m in self.final_molecules.items()} dtop["scan_results"] = { k: [opthist_class(**res).convert_v(target_version) for res in lst] for k, lst in dself["optimization_history"].items() } + dtop["properties"] = { + "calcinfo_ngrid": len(dtop["scan_results"]), + } dself.pop("optimization_history") dself.pop("schema_name") dself.pop("schema_version") diff --git a/qcelemental/models/v2/__init__.py b/qcelemental/models/v2/__init__.py index 386ada5a..0ec10358 100644 --- a/qcelemental/models/v2/__init__.py +++ b/qcelemental/models/v2/__init__.py @@ -23,6 +23,7 @@ from .torsion_drive import ( TorsionDriveInput, TorsionDriveKeywords, + TorsionDriveProperties, TorsionDriveProtocols, TorsionDriveResult, TorsionDriveSpecification, diff --git a/qcelemental/models/v2/torsion_drive.py b/qcelemental/models/v2/torsion_drive.py index 9657d3c4..a451f378 100644 --- a/qcelemental/models/v2/torsion_drive.py +++ b/qcelemental/models/v2/torsion_drive.py @@ -7,7 +7,7 @@ from .basemodels import ExtendedConfigDict, ProtoModel, check_convertible_version from .common_models import DriverEnum, Provenance from .molecule import Molecule -from .optimization import OptimizationResult, OptimizationSpecification +from .optimization import OptimizationProperties, OptimizationResult, OptimizationSpecification from .types import Array if TYPE_CHECKING: @@ -176,8 +176,27 @@ def convert_v( # ==== Properties ============================================================= -# ======== Calcinfo ======================================================= -# ======== Canonical ====================================================== + +# This is mostly a placeholder class until properties developed + + +class TorsionDriveProperties(ProtoModel): + r""" + Named properties of torsion drive computations following the MolSSI QCSchema. + """ + + schema_name: Literal["qcschema_torsion_drive_properties"] = Field( + "qcschema_torsion_drive_properties", + description=f"The QCSchema specification to which this model conforms.", + ) + + # ======== Calcinfo ======================================================= + + calcinfo_ngrid: Optional[int] = Field(None, description="The number of dihedral constraints optimized.") + + # ======== Canonical ====================================================== + + model_config = ProtoModel._merge_config_with(force_skip_defaults=True) # ==== Results ================================================================ @@ -194,13 +213,14 @@ class TorsionDriveResult(ProtoModel): id: Optional[str] = Field(None, description="The optional ID for the computation.") input_data: TorsionDriveInput = Field(..., description=str(TorsionDriveInput.__doc__)) - # final_energies, final_molecules, optimization_history I'm hoping to refactor into scan_properties and scan_results but need to talk to OpenFF folks - final_energies: Dict[str, float] = Field( - ..., description="The final energy at each angle of the TorsionDrive scan." - ) final_molecules: Dict[str, Molecule] = Field( ..., description="The final molecule at each angle of the TorsionDrive scan." ) + # Note: scan_results includes all opts at each point (best and intermediate), while + # scan_properties and final_molecules include only the best opt. other choices could be made. + scan_properties: Dict[str, OptimizationProperties] = Field( + ..., description="The map of energies and other properties for each angle of the TorsionDrive scan." + ) scan_results: Dict[str, List[OptimizationResult]] = Field( ..., description="The map of each angle of the TorsionDrive scan to each optimization computations.", @@ -212,8 +232,7 @@ class TorsionDriveResult(ProtoModel): # native_files placeholder for when any td programs supply extra files or need an input file. no protocol at present native_files: Dict[str, Any] = Field({}, description="DSL files.") - # TODO add properties if a set can be collected - # properties: TorsionDriveProperties = Field(..., description=str(TorsionDriveProperties.__doc__)) + properties: TorsionDriveProperties = Field(..., description=str(TorsionDriveProperties.__doc__)) extras: Dict[str, Any] = Field( {}, @@ -278,7 +297,8 @@ def convert_v( input_data = self.input_data.convert_v(target_version).model_dump() input_data.pop("schema_name") # prevent inheriting - dtop["final_energies"] = dself.pop("final_energies") + dtop["final_energies"] = {k: prop["return_energy"] for k, prop in dself["scan_properties"].items()} + dself.pop("scan_properties") dself.pop("final_molecules") dtop["final_molecules"] = {k: m.convert_v(target_version) for k, m in self.final_molecules.items()} dtop["optimization_history"] = { @@ -288,6 +308,7 @@ def convert_v( dself.pop("scan_results") dself.pop("id") # unused in v1 + dself.pop("properties") # new in v2 dself.pop("native_files") # new in v2 dtop["provenance"] = dself.pop("provenance") dtop["stdout"] = dself.pop("stdout") diff --git a/qcelemental/tests/test_model_results.py b/qcelemental/tests/test_model_results.py index b13d3941..74e0b8fc 100644 --- a/qcelemental/tests/test_model_results.py +++ b/qcelemental/tests/test_model_results.py @@ -292,9 +292,10 @@ def torsiondrive_data_fixture(ethane_data_fixture, optimization_data_fixture, re "input_data": input_data, "success": True, "provenance": {"creator": "qcel"}, - "final_energies": {"180": -2.3, "0": -4.5}, + "scan_properties": {"180": {"return_energy": -2.3}, "0": {"return_energy": -4.5}}, "final_molecules": {"180": ethane, "0": ethane}, "scan_results": {"180": [optres, optres], "0": [optres]}, + "properties": {"calcinfo_ngrid": 1}, } else: ret = { @@ -897,7 +898,9 @@ def every_model_fixture(request): data = request.getfixturevalue("torsiondrive_data_fixture") datas[smodel] = data - # smodel = "TorsionDriveProperties" # DNE + smodel = "TorsionDriveProperties" + data = {"calcinfo_ngrid": 1} + datas[smodel] = data smodel = "ManyBodyInput" data = request.getfixturevalue("manybody_data_fixture") @@ -955,7 +958,7 @@ def every_model_fixture(request): pytest.param("TDKeywords", "TorsionDriveKeywords", id="TDKw"), pytest.param(None, "TorsionDriveProtocols", id="TDPtcl"), pytest.param("TorsionDriveResult", "TorsionDriveResult", id="TDRes"), - # pytest.param(None, "TorsionDriveProperties", id="TDProp"), + pytest.param(None, "TorsionDriveProperties", id="TDProp"), pytest.param("ManyBodyInput", None, id="MBIn", marks=using_qcmb), pytest.param("ManyBodySpecification", None, id="MBSpec", marks=using_qcmb), pytest.param("ManyBodyKeywords", None, id="MBKw", marks=using_qcmb), @@ -1166,7 +1169,7 @@ def test_model_survey_extras(smodel1, smodel2, every_model_fixture, request, sch "v1-TDKw" : None, "v2-TDKw" : None, "v1-TDPtcl" : None, "v2-TDPtcl" : None, # v1 DNE "v1-TDRes" : {}, "v2-TDRes" : {}, - "v1-TDProp" : None, "v2-TDProp" : None, # v1/v2 DNE + "v1-TDProp" : None, "v2-TDProp" : None, # v1 DNE "v1-MBIn" : {}, "v2-MBIn" : None, # v2 DNE "v1-MBSpec" : {}, "v2-MBSpec" : {}, # v2 DNE "v1-MBKw" : None, "v2-MBKw" : None, # v2 DNE @@ -1272,7 +1275,7 @@ def test_model_survey_convertible(smodel1, smodel2, every_model_fixture, request # "v1-TDKw" , "v2-TDKw" , "v1-TDPtcl" , "v2-TDPtcl" , "v1-TDRes" , "v2-TDRes" , - # "v1-TDProp" , "v2-TDProp" , + "v1-TDProp" , "v2-TDProp" , # "v1-MBIn" , "v2-MBIn" , # "v1-MBSpec" , "v2-MBSpec" , # "v1-MBKw" , "v2-MBKw" , @@ -1341,7 +1344,7 @@ def test_model_survey_schema_name(smodel1, smodel2, every_model_fixture, request "v1-TDKw" : None, "v2-TDKw" : "qcschema_torsion_drive_keywords", "v1-TDPtcl" : None, "v2-TDPtcl" : "qcschema_torsion_drive_protocols", # v1 DNE "v1-TDRes" : "qcschema_torsion_drive_output", "v2-TDRes" : "qcschema_torsion_drive_result", - "v1-TDProp" : None, "v2-TDProp" : None, # v1 DNE, v2 DNE + "v1-TDProp" : None, "v2-TDProp" : "qcschema_torsion_drive_properties", # v1 DNE "v1-MBIn" : "qcschema_manybodyinput", "v2-MBIn" : "qcschema_many_body_input", # v2 DNE "v1-MBSpec" : "qcschema_manybodyspecification", "v2-MBSpec" : "qcschema_many_body_specification", # v2 DNE "v1-MBKw" : "qcschema_manybodykeywords", "v2-MBKw" : "qcschema_many_body_keywords", # v2 DNE