diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 116384561..262183abd 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -101,10 +101,11 @@ jobs: qcore --accept-license # note: psi4 on c-f pins to a single qcel and qcng, so this may be handy for solve-and-replace - #- name: Special Config - QCElemental Dep + - name: Special Config - QCElemental Dep # if: (matrix.cfg.label == 'ADCC') - # run: | - # conda remove qcelemental --force + run: | + conda remove qcelemental --force + python -m pip install git+https://github.com/Lnaden/QCElemental.git@Pydanticv2Overhaul --no-deps # python -m pip install qcelemental>=0.26.0 --no-deps # note: conda remove --force, not mamba remove --force b/c https://github.com/mamba-org/mamba/issues/412 diff --git a/devtools/conda-envs/adcc.yaml b/devtools/conda-envs/adcc.yaml index cb10306fa..3f6205979 100644 --- a/devtools/conda-envs/adcc.yaml +++ b/devtools/conda-envs/adcc.yaml @@ -13,7 +13,8 @@ dependencies: - py-cpuinfo - psutil - qcelemental >=0.24.0 - - pydantic=1 + - pydantic>=2.1 + - pydantic-settings - msgpack-python # Testing diff --git a/devtools/conda-envs/base.yaml b/devtools/conda-envs/base.yaml index 2a74047f9..38bcea49e 100644 --- a/devtools/conda-envs/base.yaml +++ b/devtools/conda-envs/base.yaml @@ -8,7 +8,8 @@ dependencies: - py-cpuinfo - psutil - qcelemental >=0.12.0 - - pydantic>=1.0.0 + - pydantic>=2.1 + - pydantic-settings # Testing - pytest diff --git a/devtools/conda-envs/docs-cf.yaml b/devtools/conda-envs/docs-cf.yaml index 19b37fa32..2c396e2da 100644 --- a/devtools/conda-envs/docs-cf.yaml +++ b/devtools/conda-envs/docs-cf.yaml @@ -5,7 +5,8 @@ channels: dependencies: - python - networkx - - pydantic=1 + - pydantic>=2.1 + - pydantic-settings - numpy - pint diff --git a/devtools/conda-envs/mrchem.yaml b/devtools/conda-envs/mrchem.yaml index d0d0b752b..4319ca151 100644 --- a/devtools/conda-envs/mrchem.yaml +++ b/devtools/conda-envs/mrchem.yaml @@ -13,7 +13,8 @@ dependencies: - py-cpuinfo - psutil - qcelemental>=0.24 - - pydantic + - pydantic>=2.1 + - pydantic-settings # Testing - pytest diff --git a/devtools/conda-envs/nwchem.yaml b/devtools/conda-envs/nwchem.yaml index e86623e79..294416546 100644 --- a/devtools/conda-envs/nwchem.yaml +++ b/devtools/conda-envs/nwchem.yaml @@ -8,7 +8,8 @@ dependencies: - py-cpuinfo - psutil - qcelemental >=0.24.0 - - pydantic>=1.0.0 + - pydantic>=2.1 + - pydantic-settings - networkx>=2.4.0 # Testing diff --git a/devtools/conda-envs/openmm.yaml b/devtools/conda-envs/openmm.yaml index ddd49e3b4..0c02ef1ff 100644 --- a/devtools/conda-envs/openmm.yaml +++ b/devtools/conda-envs/openmm.yaml @@ -16,7 +16,8 @@ dependencies: - py-cpuinfo - psutil - qcelemental >=0.11.1 - - pydantic >=1.8.2 + - pydantic>=2.1 + - pydantic-settings - pint <0.22 # Testing diff --git a/devtools/conda-envs/opt-disp.yaml b/devtools/conda-envs/opt-disp.yaml index 7e1b8641a..2bf981819 100644 --- a/devtools/conda-envs/opt-disp.yaml +++ b/devtools/conda-envs/opt-disp.yaml @@ -25,7 +25,8 @@ dependencies: - py-cpuinfo - psutil - qcelemental >=0.26.0 - - pydantic>=1.0.0 + - pydantic>=2.1 + - pydantic-settings - msgpack-python # Testing diff --git a/devtools/conda-envs/psi-nightly.yaml b/devtools/conda-envs/psi-nightly.yaml index b175ec049..5b9401d64 100644 --- a/devtools/conda-envs/psi-nightly.yaml +++ b/devtools/conda-envs/psi-nightly.yaml @@ -12,7 +12,8 @@ dependencies: - py-cpuinfo - psutil - qcelemental >=0.26.0 - - pydantic>=1.0.0 + - pydantic>=2.1 + - pydantic-settings - msgpack-python # Testing diff --git a/devtools/conda-envs/psi.yaml b/devtools/conda-envs/psi.yaml index dcff46c1e..7f5e7653c 100644 --- a/devtools/conda-envs/psi.yaml +++ b/devtools/conda-envs/psi.yaml @@ -16,7 +16,8 @@ dependencies: - py-cpuinfo - psutil - qcelemental=0.24.0 - - pydantic=1.8.2 # test minimun stated version. + - pydantic>=2.1 # test minimum stated version. + - pydantic-settings - msgpack-python # Testing diff --git a/devtools/conda-envs/qcore.yaml b/devtools/conda-envs/qcore.yaml index 8e848bc27..a252f3cca 100644 --- a/devtools/conda-envs/qcore.yaml +++ b/devtools/conda-envs/qcore.yaml @@ -11,7 +11,8 @@ dependencies: - py-cpuinfo - psutil - qcelemental >=0.24 - - pydantic >=1.8.2 + - pydantic>=2.1 + - pydantic-settings - tbb<2021 # Testing diff --git a/devtools/conda-envs/rdkit.yaml b/devtools/conda-envs/rdkit.yaml index 1ed05aff9..1792a3938 100644 --- a/devtools/conda-envs/rdkit.yaml +++ b/devtools/conda-envs/rdkit.yaml @@ -11,7 +11,8 @@ dependencies: - py-cpuinfo - psutil - qcelemental >=0.12.0 - - pydantic>=1.0.0 + - pydantic>=2.1 + - pydantic-settings # Testing - pytest diff --git a/devtools/conda-envs/torchani.yaml b/devtools/conda-envs/torchani.yaml index 40ba67d10..bfdc2cf2d 100644 --- a/devtools/conda-envs/torchani.yaml +++ b/devtools/conda-envs/torchani.yaml @@ -11,7 +11,8 @@ dependencies: - py-cpuinfo - psutil - qcelemental >=0.12.0 - - pydantic>=1.0.0 + - pydantic>=2.1 + - pydantic-settings - pytorch diff --git a/devtools/conda-envs/xtb.yaml b/devtools/conda-envs/xtb.yaml index fb710e50d..96f9d56b1 100644 --- a/devtools/conda-envs/xtb.yaml +++ b/devtools/conda-envs/xtb.yaml @@ -11,7 +11,8 @@ dependencies: - py-cpuinfo - psutil - qcelemental >=0.11.1 - - pydantic >=1.8.2 + - pydantic>=2.1 + - pydantic-settings # Extras - gcp-correction diff --git a/qcengine/compute.py b/qcengine/compute.py index 00e94a329..af4754bf4 100644 --- a/qcengine/compute.py +++ b/qcengine/compute.py @@ -13,10 +13,7 @@ from .util import compute_wrapper, environ_context, handle_output_metadata, model_wrapper if TYPE_CHECKING: - try: - from pydantic.v1.main import BaseModel - except ImportError: - from pydantic.main import BaseModel + from pydantic.main import BaseModel from qcelemental.models import AtomicResult diff --git a/qcengine/config.py b/qcengine/config.py index aa98745f4..72b1d430b 100644 --- a/qcengine/config.py +++ b/qcengine/config.py @@ -9,10 +9,8 @@ import socket from typing import Any, Dict, Optional, Union -try: - import pydantic.v1 as pydantic -except ImportError: - import pydantic +from pydantic import BaseModel, ConfigDict, Field +from pydantic_settings import BaseSettings, SettingsConfigDict from .extras import get_information @@ -64,7 +62,7 @@ def get_global(key: Optional[str] = None) -> Union[str, Dict[str, Any]]: return _global_values[key] -class NodeDescriptor(pydantic.BaseModel): +class NodeDescriptor(BaseModel): """ Description of an individual node """ @@ -78,7 +76,7 @@ class NodeDescriptor(pydantic.BaseModel): memory_safety_factor: int = 10 # Percentage of memory as a safety factor # Specifications - ncores: Optional[int] = pydantic.Field( + ncores: Optional[int] = Field( None, description="""Number of cores accessible to each task on this node @@ -88,7 +86,7 @@ class NodeDescriptor(pydantic.BaseModel): retries: int = 0 # Cluster options - is_batch_node: bool = pydantic.Field( + is_batch_node: bool = Field( False, help="""Whether the node running QCEngine is a batch node @@ -103,7 +101,7 @@ class NodeDescriptor(pydantic.BaseModel): ``mpiexec_command`` must always be used even for serial jobs (e.g., getting the version number) """, ) - mpiexec_command: Optional[str] = pydantic.Field( + mpiexec_command: Optional[str] = Field( None, description="""Invocation for launching node-parallel tasks with MPI @@ -140,31 +138,40 @@ def __init__(self, **data: Dict[str, Any]): if "{ranks_per_node}" not in self.mpiexec_command: raise ValueError("mpiexec_command must explicitly state the number of ranks per node") - class Config: - extra = "forbid" + model_config = ConfigDict( + extra="forbid", + ) + + +# class Config: +# extra = "forbid" +# allow_mutation = False +# json_encoders = {np.ndarray: lambda v: v.flatten().tolist(), complex: lambda v: (v.real, v.imag)} +# --- +# model_config = ConfigDict( +# extra="forbid", +# frozen=True, +# ) -class TaskConfig(pydantic.BaseSettings): +class TaskConfig(BaseSettings): """Description of the configuration used to launch a task.""" # Specifications - ncores: int = pydantic.Field(None, description="Number cores per task on each node") - nnodes: int = pydantic.Field(None, description="Number of nodes per task") - memory: float = pydantic.Field( - None, description="Amount of memory in GiB (2^30 bytes; not GB = 10^9 bytes) per node." - ) + ncores: int = Field(None, description="Number cores per task on each node") + nnodes: int = Field(None, description="Number of nodes per task") + memory: float = Field(None, description="Amount of memory in GiB (2^30 bytes; not GB = 10^9 bytes) per node.") scratch_directory: Optional[str] # What location to use as scratch retries: int # Number of retries on random failures mpiexec_command: Optional[str] # Command used to launch MPI tasks, see NodeDescriptor use_mpiexec: bool = False # Whether it is necessary to use MPI to run an executable - cores_per_rank: int = pydantic.Field(1, description="Number of cores per MPI rank") - scratch_messy: bool = pydantic.Field( - False, description="Leave scratch directory and contents on disk after completion." - ) + cores_per_rank: int = Field(1, description="Number of cores per MPI rank") + scratch_messy: bool = Field(False, description="Leave scratch directory and contents on disk after completion.") - class Config(pydantic.BaseSettings.Config): - extra = "forbid" - env_prefix = "QCENGINE_" + model_config = SettingsConfigDict( + extra="forbid", + env_prefix="QCENGINE_", + ) def _load_defaults() -> None: diff --git a/qcengine/procedures/berny.py b/qcengine/procedures/berny.py index 84345f203..8c618aeca 100644 --- a/qcengine/procedures/berny.py +++ b/qcengine/procedures/berny.py @@ -3,7 +3,7 @@ import sys import traceback from io import StringIO -from typing import Any, Dict, Union +from typing import Any, ClassVar, Dict, Union import numpy as np from qcelemental.models import OptimizationInput, OptimizationResult, FailedOperation @@ -16,7 +16,7 @@ class BernyProcedure(ProcedureHarness): - _defaults = {"name": "Berny", "procedure": "optimization"} + _defaults: ClassVar[Dict[str, Any]] = {"name": "Berny", "procedure": "optimization"} def found(self, raise_error: bool = False) -> bool: return which_import( diff --git a/qcengine/procedures/geometric.py b/qcengine/procedures/geometric.py index 59bc26c86..d8b19e40a 100644 --- a/qcengine/procedures/geometric.py +++ b/qcengine/procedures/geometric.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Union +from typing import Any, ClassVar, Dict, Union from qcelemental.models import OptimizationInput, OptimizationResult from qcelemental.util import safe_version, which_import @@ -8,13 +8,10 @@ class GeometricProcedure(ProcedureHarness): - _defaults = {"name": "geomeTRIC", "procedure": "optimization"} + _defaults: ClassVar[Dict[str, Any]] = {"name": "geomeTRIC", "procedure": "optimization"} version_cache: Dict[str, str] = {} - class Config(ProcedureHarness.Config): - pass - def found(self, raise_error: bool = False) -> bool: return which_import( "geometric", diff --git a/qcengine/procedures/model.py b/qcengine/procedures/model.py index 0e540114d..e5a334ddf 100644 --- a/qcengine/procedures/model.py +++ b/qcengine/procedures/model.py @@ -1,10 +1,7 @@ import abc from typing import Any, Dict, Union -try: - from pydantic.v1 import BaseModel -except ImportError: - from pydantic import BaseModel +from pydantic import BaseModel, ConfigDict from ..util import model_wrapper @@ -14,9 +11,10 @@ class ProcedureHarness(BaseModel, abc.ABC): name: str procedure: str - class Config: - allow_mutation: False - extra: "forbid" + model_config = ConfigDict( + frozen=True, + extra="forbid", + ) def __init__(self, **kwargs): super().__init__(**{**self._defaults, **kwargs}) diff --git a/qcengine/procedures/nwchem_opt/__init__.py b/qcengine/procedures/nwchem_opt/__init__.py index 08baabc50..d9ae29e00 100644 --- a/qcengine/procedures/nwchem_opt/__init__.py +++ b/qcengine/procedures/nwchem_opt/__init__.py @@ -1,4 +1,4 @@ -from typing import Union, Dict, Any +from typing import Any, ClassVar, Dict, Union from qcelemental.models import OptimizationInput, AtomicInput, OptimizationResult, Provenance @@ -12,10 +12,7 @@ class NWChemDriverProcedure(ProcedureHarness): """Structural relaxation using NWChem's optimizer""" - _defaults = {"name": "NWChemDriver", "procedure": "optimization"} - - class Config(ProcedureHarness.Config): - pass + _defaults: ClassVar[Dict[str, Any]] = {"name": "NWChemDriver", "procedure": "optimization"} def found(self, raise_error: bool = False) -> bool: nwc_harness = NWChemHarness() diff --git a/qcengine/procedures/optking.py b/qcengine/procedures/optking.py index 91de0ab62..2a0dc06ca 100644 --- a/qcengine/procedures/optking.py +++ b/qcengine/procedures/optking.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Union +from typing import Any, ClassVar, Dict, Union from qcelemental.models import OptimizationInput, OptimizationResult from qcelemental.util import safe_version, which_import @@ -8,13 +8,10 @@ class OptKingProcedure(ProcedureHarness): - _defaults = {"name": "OptKing", "procedure": "optimization"} + _defaults: ClassVar[Dict[str, Any]] = {"name": "OptKing", "procedure": "optimization"} version_cache: Dict[str, str] = {} - class Config(ProcedureHarness.Config): - pass - def found(self, raise_error: bool = False) -> bool: return which_import( "optking", diff --git a/qcengine/procedures/torsiondrive.py b/qcengine/procedures/torsiondrive.py index 0fc1327d7..ad7f88059 100644 --- a/qcengine/procedures/torsiondrive.py +++ b/qcengine/procedures/torsiondrive.py @@ -1,7 +1,7 @@ import io from collections import defaultdict from contextlib import redirect_stderr, redirect_stdout -from typing import TYPE_CHECKING, Any, Dict, List, Tuple, Union +from typing import TYPE_CHECKING, Any, ClassVar, Dict, List, Tuple, Union import numpy as np from qcelemental.models import FailedOperation, Molecule @@ -16,10 +16,7 @@ class TorsionDriveProcedure(ProcedureHarness): - _defaults = {"name": "TorsionDrive", "procedure": "torsiondrive"} - - class Config(ProcedureHarness.Config): - pass + _defaults: ClassVar[Dict[str, Any]] = {"name": "TorsionDrive", "procedure": "torsiondrive"} def found(self, raise_error: bool = False) -> bool: return which_import( diff --git a/qcengine/programs/adcc.py b/qcengine/programs/adcc.py index 809503306..252d8f426 100644 --- a/qcengine/programs/adcc.py +++ b/qcengine/programs/adcc.py @@ -1,7 +1,7 @@ """ Calls adcc """ -from typing import TYPE_CHECKING, Dict +from typing import TYPE_CHECKING, Any, ClassVar, Dict from qcelemental.models import AtomicResult, BasisSet, Provenance from qcelemental.util import safe_version, which_import @@ -19,7 +19,7 @@ class AdccHarness(ProgramHarness): - _defaults = { + _defaults: ClassVar[Dict[str, Any]] = { "name": "adcc", "scratch": False, "thread_safe": False, @@ -29,9 +29,6 @@ class AdccHarness(ProgramHarness): } version_cache: Dict[str, str] = {} - class Config(ProgramHarness.Config): - pass - @staticmethod def found(raise_error: bool = False) -> bool: """Whether adcc harness is ready for operation. diff --git a/qcengine/programs/base.py b/qcengine/programs/base.py index 8d9cfc9ff..f9c648175 100644 --- a/qcengine/programs/base.py +++ b/qcengine/programs/base.py @@ -23,8 +23,9 @@ from .qcore import EntosHarness, QcoreHarness from .rdkit import RDKitHarness from .terachem import TeraChemHarness -from .terachem_frontend import TeraChemFrontEndHarness -from .terachem_pbs import TeraChemPBSHarness + +# from .terachem_frontend import TeraChemFrontEndHarness +# from .terachem_pbs import TeraChemPBSHarness from .torchani import TorchANIHarness from .turbomole import TurbomoleHarness from .xtb import XTBHarness @@ -116,8 +117,8 @@ def list_available_programs() -> Set[str]: register_program(QcoreHarness()) register_program(TeraChemHarness()) register_program(TurbomoleHarness()) -register_program(TeraChemFrontEndHarness()) -register_program(TeraChemPBSHarness()) +# register_program(TeraChemFrontEndHarness()) +# register_program(TeraChemPBSHarness()) # Semi-empirical register_program(MopacHarness()) diff --git a/qcengine/programs/cfour/runner.py b/qcengine/programs/cfour/runner.py index 6fe67e2b6..e181f6db3 100644 --- a/qcengine/programs/cfour/runner.py +++ b/qcengine/programs/cfour/runner.py @@ -41,9 +41,6 @@ class CFOURHarness(ProgramHarness): } version_cache: Dict[str, str] = {} - class Config(ProgramHarness.Config): - pass - @staticmethod def found(raise_error: bool = False) -> bool: return which( diff --git a/qcengine/programs/dftd3.py b/qcengine/programs/dftd3.py index 2f67fcfee..4aff9a1be 100644 --- a/qcengine/programs/dftd3.py +++ b/qcengine/programs/dftd3.py @@ -7,7 +7,7 @@ import socket import sys from decimal import Decimal -from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple +from typing import TYPE_CHECKING, Any, ClassVar, Dict, Optional, Tuple import numpy as np import qcelemental as qcel @@ -30,7 +30,7 @@ class DFTD3Harness(ProgramHarness): - _defaults = { + _defaults: ClassVar[Dict[str, Any]] = { "name": "DFTD3", "scratch": True, "thread_safe": True, @@ -40,9 +40,6 @@ class DFTD3Harness(ProgramHarness): } version_cache: Dict[str, str] = {} - class Config(ProgramHarness.Config): - pass - @staticmethod def found(raise_error: bool = False) -> bool: return which( diff --git a/qcengine/programs/dftd_ng.py b/qcengine/programs/dftd_ng.py index 8fe0af980..870491827 100644 --- a/qcengine/programs/dftd_ng.py +++ b/qcengine/programs/dftd_ng.py @@ -7,7 +7,7 @@ respective dispersion correction. """ -from typing import Dict +from typing import Any, ClassVar, Dict from qcelemental.models import AtomicInput, AtomicResult from qcelemental.util import parse_version, safe_version, which_import @@ -21,7 +21,7 @@ class DFTD4Harness(ProgramHarness): """Calculation harness for the DFT-D4 dispersion correction.""" - _defaults = { + _defaults: ClassVar[Dict[str, Any]] = { "name": "dftd4", "scratch": False, "thread_safe": True, @@ -31,9 +31,6 @@ class DFTD4Harness(ProgramHarness): } version_cache: Dict[str, str] = {} - class Config(ProgramHarness.Config): - pass - @staticmethod def found(raise_error: bool = False) -> bool: """Check for the availability of the Python API of dftd4""" @@ -185,7 +182,7 @@ class SDFTD3Harness(ProgramHarness): it must be explicitly disabled by setting the *s9* value to zero. """ - _defaults = { + _defaults: ClassVar[Dict[str, Any]] = { "name": "s-dftd3", "scratch": False, "thread_safe": True, @@ -195,9 +192,6 @@ class SDFTD3Harness(ProgramHarness): } version_cache: Dict[str, str] = {} - class Config(ProgramHarness.Config): - pass - @staticmethod def found(raise_error: bool = False) -> bool: """Check for the availability of the Python API of dftd3""" diff --git a/qcengine/programs/gamess/runner.py b/qcengine/programs/gamess/runner.py index b456dbc4d..18a874501 100644 --- a/qcengine/programs/gamess/runner.py +++ b/qcengine/programs/gamess/runner.py @@ -42,9 +42,6 @@ class GAMESSHarness(ProgramHarness): } version_cache: Dict[str, str] = {} - class Config(ProgramHarness.Config): - pass - @staticmethod def found(raise_error: bool = False) -> bool: return which( diff --git a/qcengine/programs/gcp.py b/qcengine/programs/gcp.py index 368f82521..8712ae716 100644 --- a/qcengine/programs/gcp.py +++ b/qcengine/programs/gcp.py @@ -7,7 +7,7 @@ import socket import sys from decimal import Decimal -from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple +from typing import TYPE_CHECKING, Any, ClassVar, Dict, Optional, Tuple import numpy as np import qcelemental as qcel @@ -29,7 +29,7 @@ class GCPHarness(ProgramHarness): - _defaults = { + _defaults: ClassVar[Dict[str, Any]] = { "name": "GCP", "scratch": True, "thread_safe": True, @@ -39,9 +39,6 @@ class GCPHarness(ProgramHarness): } version_cache: Dict[str, str] = {} - class Config(ProgramHarness.Config): - pass - @staticmethod def found(raise_error: bool = False) -> bool: return which( @@ -283,7 +280,7 @@ def parse_output(self, outfiles: Dict[str, str], input_model: "AtomicInput") -> class MCTCGCPHarness(GCPHarness): - _defaults = { + _defaults: ClassVar[Dict[str, Any]] = { "name": "MCTC-GCP", "scratch": True, "thread_safe": True, diff --git a/qcengine/programs/model.py b/qcengine/programs/model.py index 96e953d10..d5c3e9564 100644 --- a/qcengine/programs/model.py +++ b/qcengine/programs/model.py @@ -1,11 +1,8 @@ import abc import logging -from typing import Any, Dict, List, Optional, Tuple, Union +from typing import Any, ClassVar, Dict, List, Optional, Tuple, Union -try: - from pydantic.v1 import BaseModel -except ImportError: - from pydantic import BaseModel +from pydantic import BaseModel, ConfigDict from qcelemental.models import AtomicInput, AtomicResult, FailedOperation from qcengine.exceptions import KnownErrorException @@ -16,18 +13,19 @@ class ProgramHarness(BaseModel, abc.ABC): - _defaults: Dict[str, Any] = {} + _defaults: ClassVar[Dict[str, Any]] = {} name: str scratch: bool thread_safe: bool thread_parallel: bool node_parallel: bool managed_memory: bool - extras: Optional[Dict[str, Any]] + extras: Optional[Dict[str, Any]] = None - class Config: - allow_mutation: False - extra: "forbid" + model_config = ConfigDict( + frozen=True, + extra="forbid", + ) def __init__(self, **kwargs): super().__init__(**{**self._defaults, **kwargs}) @@ -153,7 +151,9 @@ def compute(self, input_data: AtomicInput, config: TaskConfig) -> AtomicResult: keyword_updates = e.create_keyword_update(local_input_data) new_keywords = local_input_data.keywords.copy() new_keywords.update(keyword_updates) - local_input_data = AtomicInput(**local_input_data.dict(exclude={"keywords"}), keywords=new_keywords) + local_input_data = AtomicInput( + **local_input_data.model_dump(exclude={"keywords"}), keywords=new_keywords + ) # Store the error details and mitigations employed observed_errors[e.error_name] = {"details": e.details, "keyword_updates": keyword_updates} diff --git a/qcengine/programs/molpro.py b/qcengine/programs/molpro.py index 0025ae0f4..09e14a369 100644 --- a/qcengine/programs/molpro.py +++ b/qcengine/programs/molpro.py @@ -3,7 +3,7 @@ """ import string -from typing import Any, Dict, List, Optional, Set, Tuple +from typing import Any, ClassVar, Dict, List, Optional, Set, Tuple from xml.etree import ElementTree as ET from qcelemental.models import AtomicResult @@ -15,7 +15,7 @@ class MolproHarness(ProgramHarness): - _defaults: Dict[str, Any] = { + _defaults: ClassVar[Dict[str, Any]] = { "name": "Molpro", "scratch": True, "thread_safe": False, @@ -63,9 +63,6 @@ class MolproHarness(ProgramHarness): # _unrestricted_post_hf_methods: Set[str] = {"UMP2", "UCCSD", "UCCSD(T)"} _post_hf_methods: Set[str] = {*_restricted_post_hf_methods} - class Config(ProgramHarness.Config): - pass - def found(self, raise_error: bool = False) -> bool: return which( "molpro", return_bool=True, raise_error=raise_error, raise_msg="Please install via https://www.molpro.net/" diff --git a/qcengine/programs/mopac.py b/qcengine/programs/mopac.py index e97a319c8..06e9bb040 100644 --- a/qcengine/programs/mopac.py +++ b/qcengine/programs/mopac.py @@ -2,7 +2,7 @@ Calls the Psi4 executable. """ import os -from typing import Any, Dict, List, Optional, Tuple +from typing import Any, ClassVar, Dict, List, Optional, Tuple from qcelemental.models import AtomicResult from qcelemental.util import which @@ -14,7 +14,7 @@ class MopacHarness(ProgramHarness): - _defaults = { + _defaults: ClassVar[Dict[str, Any]] = { "name": "MOPAC", "scratch": True, # Input/output file "thread_safe": True, @@ -24,9 +24,6 @@ class MopacHarness(ProgramHarness): } version_cache: Dict[str, str] = {} - class Config(ProgramHarness.Config): - pass - def __init__(self, **kwargs): extras = { # All units taken from within MOPAC "bohr_to_angstroms": 0.5291772083, diff --git a/qcengine/programs/mp2d.py b/qcengine/programs/mp2d.py index 2fd08c8aa..e68578962 100644 --- a/qcengine/programs/mp2d.py +++ b/qcengine/programs/mp2d.py @@ -4,7 +4,7 @@ import re import sys from decimal import Decimal -from typing import Any, Dict, Optional, Tuple +from typing import Any, ClassVar, Dict, Optional, Tuple import numpy as np import qcelemental as qcel @@ -21,7 +21,7 @@ class MP2DHarness(ProgramHarness): - _defaults = { + _defaults: ClassVar[Dict[str, Any]] = { "name": "MP2D", "scratch": True, "thread_safe": True, @@ -31,9 +31,6 @@ class MP2DHarness(ProgramHarness): } version_cache: Dict[str, str] = {} - class Config(ProgramHarness.Config): - pass - @staticmethod def found(raise_error: bool = False) -> bool: return which( diff --git a/qcengine/programs/mrchem.py b/qcengine/programs/mrchem.py index e52c889ee..67ea582f6 100644 --- a/qcengine/programs/mrchem.py +++ b/qcengine/programs/mrchem.py @@ -9,7 +9,7 @@ from collections import Counter from functools import reduce from pathlib import Path -from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple +from typing import TYPE_CHECKING, Any, ClassVar, Dict, Optional, Tuple from qcelemental.models import AtomicResult from qcelemental.util import safe_version, which @@ -29,7 +29,7 @@ class MRChemHarness(ProgramHarness): - _defaults = { + _defaults: ClassVar[Dict[str, Any]] = { "name": "MRChem", "scratch": False, "thread_safe": False, @@ -39,9 +39,6 @@ class MRChemHarness(ProgramHarness): } version_cache: Dict[str, str] = {} - class Config(ProgramHarness.Config): - pass - @staticmethod def found(raise_error: bool = False) -> bool: """Whether MRChem harness is ready for operation. diff --git a/qcengine/programs/nwchem/runner.py b/qcengine/programs/nwchem/runner.py index 65a08aa05..15cd186cd 100644 --- a/qcengine/programs/nwchem/runner.py +++ b/qcengine/programs/nwchem/runner.py @@ -7,7 +7,7 @@ import pprint import re from decimal import Decimal -from typing import Any, Dict, Optional, Tuple +from typing import Any, ClassVar, Dict, Optional, Tuple import numpy as np from qcelemental.models import AtomicInput, AtomicResult, BasisSet, Provenance @@ -39,7 +39,7 @@ class NWChemHarness(ErrorCorrectionProgramHarness): """ - _defaults = { + _defaults: ClassVar[Dict[str, Any]] = { "name": "NWChem", "scratch": True, "thread_safe": False, @@ -50,9 +50,6 @@ class NWChemHarness(ErrorCorrectionProgramHarness): # ATL: OpenMP only >=6.6 and only for Phi; potential for Mac using MKL and Intel compilers version_cache: Dict[str, str] = {} - class Config(ErrorCorrectionProgramHarness.Config): - pass - @staticmethod def found(raise_error: bool = False) -> bool: """Whether NWChem harness is ready for operation, with both the QC program and any particular dependencies found. @@ -321,7 +318,7 @@ def parse_output( build_out(qcvars) atprop = build_atomicproperties(qcvars) - provenance = Provenance(creator="NWChem", version=self.get_version(), routine="nwchem").dict() + provenance = Provenance(creator="NWChem", version=self.get_version(), routine="nwchem").model_dump() if module is not None: provenance["module"] = module @@ -346,4 +343,4 @@ def parse_output( k.upper(): str(v) if isinstance(v, Decimal) else v for k, v in qcvars.items() } - return AtomicResult(**{**input_model.dict(), **output_data}) + return AtomicResult(**{**input_model.model_dump(), **output_data}) diff --git a/qcengine/programs/openmm.py b/qcengine/programs/openmm.py index bb54567b3..7a2038b2d 100644 --- a/qcengine/programs/openmm.py +++ b/qcengine/programs/openmm.py @@ -6,7 +6,7 @@ import datetime import hashlib import os -from typing import TYPE_CHECKING, Dict +from typing import TYPE_CHECKING, Any, ClassVar, Dict import numpy as np from qcelemental.models import AtomicResult, BasisSet, Provenance @@ -28,7 +28,7 @@ class OpenMMHarness(ProgramHarness): _CACHE = {} _CACHE_MAX_SIZE = 10 - _defaults = { + _defaults: ClassVar[Dict[str, Any]] = { "name": "OpenMM", "scratch": True, "thread_safe": True, # true if we use separate `openmm.Context` objects per thread @@ -39,9 +39,6 @@ class OpenMMHarness(ProgramHarness): version_cache: Dict[str, str] = {} - class Config(ProgramHarness.Config): - pass - # def _get_off_forcefield(self, hashstring, offxml): # # from openff.toolkit.typing.engines import smirnoff diff --git a/qcengine/programs/psi4.py b/qcengine/programs/psi4.py index 99b725c64..5080e9c54 100644 --- a/qcengine/programs/psi4.py +++ b/qcengine/programs/psi4.py @@ -5,7 +5,7 @@ import os import sys from pathlib import Path -from typing import TYPE_CHECKING, Dict +from typing import TYPE_CHECKING, Any, ClassVar, Dict from qcelemental.models import AtomicResult, BasisSet from qcelemental.util import deserialize, parse_version, safe_version, which, which_import @@ -22,7 +22,7 @@ class Psi4Harness(ProgramHarness): - _defaults = { + _defaults: ClassVar[Dict[str, Any]] = { "name": "Psi4", "scratch": True, "thread_safe": False, @@ -32,9 +32,6 @@ class Psi4Harness(ProgramHarness): } version_cache: Dict[str, str] = {} - class Config(ProgramHarness.Config): - pass - @staticmethod def found(raise_error: bool = False) -> bool: """Whether Psi4 harness is ready for operation. diff --git a/qcengine/programs/qchem.py b/qcengine/programs/qchem.py index 79a03bdc8..22aeb406f 100644 --- a/qcengine/programs/qchem.py +++ b/qcengine/programs/qchem.py @@ -7,7 +7,7 @@ import tempfile import warnings from collections import defaultdict -from typing import Any, Dict, List, Optional, Tuple +from typing import Any, ClassVar, Dict, List, Optional, Tuple import numpy as np from qcelemental import constants @@ -25,7 +25,7 @@ class QChemHarness(ProgramHarness): - _defaults: Dict[str, Any] = { + _defaults: ClassVar[Dict[str, Any]] = { "name": "QChem", "scratch": True, "thread_safe": False, diff --git a/qcengine/programs/qcore.py b/qcengine/programs/qcore.py index 172f8dbb7..b8a51ebdb 100644 --- a/qcengine/programs/qcore.py +++ b/qcengine/programs/qcore.py @@ -2,7 +2,7 @@ The qcore QCEngine Harness """ -from typing import TYPE_CHECKING, Any, Dict, List, Set +from typing import TYPE_CHECKING, Any, ClassVar, Dict, List, Set import numpy as np from qcelemental.models import AtomicResult, BasisSet @@ -31,7 +31,7 @@ def qcore_ao_order_spherical(max_angular_momentum: int) -> Dict[int, List[int]]: class QcoreHarness(ProgramHarness): - _defaults: Dict[str, Any] = { + _defaults: ClassVar[Dict[str, Any]] = { "name": "Qcore", "scratch": False, "thread_safe": False, @@ -88,9 +88,6 @@ class QcoreHarness(ProgramHarness): # Entos spherical basis ordering for each angular momentum. Follows reverse order of CCA. _qcore_to_cca_ao_order = {"spherical": get_ao_conversion(cca_ao_order_spherical(10), qcore_ao_order_spherical(10))} - class Config(ProgramHarness.Config): - pass - def found(self, raise_error: bool = False) -> bool: return which_import( "qcore", @@ -250,7 +247,7 @@ def parse_output(self, output: Dict[str, Any], input_model: "AtomicInput") -> "A class EntosHarness(QcoreHarness): - _defaults: Dict[str, Any] = { + _defaults: ClassVar[Dict[str, Any]] = { "name": "Entos", "scratch": True, "thread_safe": False, diff --git a/qcengine/programs/rdkit.py b/qcengine/programs/rdkit.py index 1a01a0253..a78c66137 100644 --- a/qcengine/programs/rdkit.py +++ b/qcengine/programs/rdkit.py @@ -2,7 +2,7 @@ Calls the RDKit package. """ -from typing import TYPE_CHECKING, Dict +from typing import TYPE_CHECKING, Any, ClassVar, Dict from qcelemental.models import AtomicResult, Provenance from qcelemental.util import safe_version, which_import @@ -19,7 +19,7 @@ class RDKitHarness(ProgramHarness): - _defaults = { + _defaults: ClassVar[Dict[str, Any]] = { "name": "RDKit", "scratch": False, "thread_safe": True, @@ -30,9 +30,6 @@ class RDKitHarness(ProgramHarness): version_cache: Dict[str, str] = {} - class Config(ProgramHarness.Config): - pass - @staticmethod def _process_molecule_rdkit(jmol): from rdkit import Chem diff --git a/qcengine/programs/terachem.py b/qcengine/programs/terachem.py index 98dfcaabe..05f6ed727 100644 --- a/qcengine/programs/terachem.py +++ b/qcengine/programs/terachem.py @@ -3,7 +3,7 @@ """ import re -from typing import Any, Dict, Optional +from typing import Any, ClassVar, Dict, Optional from qcelemental.models import AtomicResult, FailedOperation from qcelemental.molparse.regex import DECIMAL, NUMBER @@ -18,7 +18,7 @@ class TeraChemHarness(ProgramHarness): - _defaults = { + _defaults: ClassVar[Dict[str, Any]] = { "name": "TeraChem", "scratch": True, "thread_safe": False, @@ -28,9 +28,6 @@ class TeraChemHarness(ProgramHarness): } version_cache: Dict[str, str] = {} - class Config(ProgramHarness.Config): - pass - @staticmethod def found(raise_error: bool = False) -> bool: return which( diff --git a/qcengine/programs/terachem_frontend.py b/qcengine/programs/terachem_frontend.py index 09b8503a3..5d1ab0d6d 100644 --- a/qcengine/programs/terachem_frontend.py +++ b/qcengine/programs/terachem_frontend.py @@ -1,7 +1,7 @@ """Harness for TeraChem Frontend""" import logging from os import getenv -from typing import Any, Dict +from typing import Any, ClassVar, Dict from .terachem_pbs import TeraChemPBSHarness, _pbs_defaults diff --git a/qcengine/programs/terachem_pbs.py b/qcengine/programs/terachem_pbs.py index 4cee0a31e..26010d504 100644 --- a/qcengine/programs/terachem_pbs.py +++ b/qcengine/programs/terachem_pbs.py @@ -4,7 +4,7 @@ import logging from importlib import import_module from os import getenv -from typing import TYPE_CHECKING, Any, Dict, Union +from typing import TYPE_CHECKING, Any, ClassVar, Dict, Union from qcelemental.models import AtomicResult, FailedOperation from qcelemental.util import which_import @@ -31,7 +31,7 @@ class TeraChemPBSHarness(ProgramHarness): """QCEngine Harness for interfacing with the TeraChem running in Protocol Buffer Server Mode""" - _defaults = _pbs_defaults + _defaults: ClassVar[Dict[str, Any]] = _pbs_defaults _tcpb_package: str = "tcpb" _tcpb_min_version: str = "0.7.0" _tcpb_client: str = "TCProtobufClient" @@ -41,9 +41,6 @@ class TeraChemPBSHarness(ProgramHarness): } _env_vars_external: str = "TERACHEM_PBS_HOST, TERACHEM_PBS_PORT" - class Config(ProgramHarness.Config): - pass - @classmethod def found(cls, raise_error: bool = False) -> bool: """Whether TeraChemPBS harness is ready for operation. diff --git a/qcengine/programs/torchani.py b/qcengine/programs/torchani.py index 2e81a5d9e..c8ea19b0a 100644 --- a/qcengine/programs/torchani.py +++ b/qcengine/programs/torchani.py @@ -2,7 +2,7 @@ Calls the TorchANI package. """ -from typing import TYPE_CHECKING, Dict +from typing import TYPE_CHECKING, Any, ClassVar, Dict from qcelemental.models import AtomicResult, Provenance from qcelemental.util import parse_version, safe_version, which_import @@ -21,7 +21,7 @@ class TorchANIHarness(ProgramHarness): _CACHE = {} - _defaults = { + _defaults: ClassVar[Dict[str, Any]] = { "name": "TorchANI", "scratch": False, "thread_safe": True, @@ -31,9 +31,6 @@ class TorchANIHarness(ProgramHarness): } version_cache: Dict[str, str] = {} - class Config(ProgramHarness.Config): - pass - @staticmethod def found(raise_error: bool = False) -> bool: return which_import( diff --git a/qcengine/programs/xtb.py b/qcengine/programs/xtb.py index d9114b54d..7cf6b6196 100644 --- a/qcengine/programs/xtb.py +++ b/qcengine/programs/xtb.py @@ -9,7 +9,7 @@ visit `its documentation `_. """ -from typing import Dict +from typing import Any, ClassVar, Dict from qcelemental.models import AtomicInput, AtomicResult from qcelemental.util import safe_version, which_import @@ -21,7 +21,7 @@ class XTBHarness(ProgramHarness): """Calculation harness for the extended tight binding (xtb) package.""" - _defaults = { + _defaults: ClassVar[Dict[str, Any]] = { "name": "xtb", "scratch": False, "thread_safe": True, @@ -31,9 +31,6 @@ class XTBHarness(ProgramHarness): } version_cache: Dict[str, str] = {} - class Config(ProgramHarness.Config): - pass - @staticmethod def found(raise_error: bool = False) -> bool: """Check for the availability of the Python API of xtb""" diff --git a/qcengine/testing.py b/qcengine/testing.py index 3f7ff1ff7..5304a4e90 100644 --- a/qcengine/testing.py +++ b/qcengine/testing.py @@ -9,6 +9,7 @@ import qcelemental as qcel from pkg_resources import parse_version from qcelemental.util import which, which_import +from pydantic import ConfigDict import qcengine as qcng @@ -95,8 +96,9 @@ class FailEngine(qcng.programs.ProgramHarness): "managed_memory": False, } - class Config(qcng.programs.ProgramHarness.Config): - allow_mutation: True + model_config = ConfigDict( + frozen=False, + ) @staticmethod def found(raise_error: bool = False) -> bool: diff --git a/qcengine/tests/test_config.py b/qcengine/tests/test_config.py index bae420d7b..4a7cef8f0 100644 --- a/qcengine/tests/test_config.py +++ b/qcengine/tests/test_config.py @@ -4,10 +4,7 @@ import copy -try: - import pydantic.v1 as pydantic -except ImportError: - import pydantic +import pydantic import pytest import qcengine as qcng diff --git a/qcengine/util.py b/qcengine/util.py index 352193885..c0700c2ea 100644 --- a/qcengine/util.py +++ b/qcengine/util.py @@ -18,10 +18,7 @@ from threading import Thread from typing import Any, BinaryIO, Dict, List, Optional, TextIO, Tuple, Union -try: - from pydantic.v1 import BaseModel, ValidationError -except ImportError: - from pydantic import BaseModel, ValidationError +from pydantic import BaseModel, ValidationError from qcelemental.models import AtomicResult, FailedOperation, OptimizationResult from qcengine.config import TaskConfig diff --git a/setup.py b/setup.py index 92965b94c..0fdae913a 100644 --- a/setup.py +++ b/setup.py @@ -28,7 +28,7 @@ cmdclass=versioneer.get_cmdclass(), packages=setuptools.find_packages(), setup_requires=[] + pytest_runner, - install_requires=["pyyaml", "py-cpuinfo", "psutil", "qcelemental>=0.24.0,<0.27.0", "pydantic>=1.8.2"], + install_requires=["pyyaml", "py-cpuinfo", "psutil", "qcelemental>=0.26.0", "pydantic>=2.1.0, pydantic-settings"], entry_points={"console_scripts": ["qcengine=qcengine.cli:main"]}, extras_require={ "docs": [