qcdata 0.17.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- qcdata/__init__.py +78 -0
- qcdata/helper_types.py +21 -0
- qcdata/models/__init__.py +5 -0
- qcdata/models/base_models.py +402 -0
- qcdata/models/data.py +387 -0
- qcdata/models/inputs.py +224 -0
- qcdata/models/outputs.py +220 -0
- qcdata/models/results.py +4 -0
- qcdata/models/structure.py +665 -0
- qcdata/models/utils.py +98 -0
- qcdata/py.typed +0 -0
- qcdata/qcel.py +91 -0
- qcdata/utils.py +49 -0
- qcdata/view.py +773 -0
- qcdata-0.17.0.dist-info/METADATA +263 -0
- qcdata-0.17.0.dist-info/RECORD +18 -0
- qcdata-0.17.0.dist-info/WHEEL +4 -0
- qcdata-0.17.0.dist-info/licenses/LICENSE +21 -0
qcdata/models/data.py
ADDED
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
"""Structured output data objects."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import warnings
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import TYPE_CHECKING, Any, TypeVar, Union
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
from pydantic import BaseModel, ValidationInfo, field_validator, model_validator
|
|
11
|
+
from typing_extensions import Self
|
|
12
|
+
|
|
13
|
+
from qcdata.helper_types import SerializableNDArray
|
|
14
|
+
|
|
15
|
+
from .base_models import CalcType, Files, QCDataBaseModel
|
|
16
|
+
from .inputs import ProgramInput
|
|
17
|
+
from .structure import Structure
|
|
18
|
+
from .utils import deprecated_class, to_multi_xyz
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from .outputs import ProgramOutput
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"Wavefunction",
|
|
25
|
+
"SinglePointData",
|
|
26
|
+
"OptimizationData",
|
|
27
|
+
"ConformerSearchData",
|
|
28
|
+
"StructuredData",
|
|
29
|
+
"StructuredDataType",
|
|
30
|
+
"Data",
|
|
31
|
+
"DataType",
|
|
32
|
+
"SinglePointResults",
|
|
33
|
+
"OptimizationResults",
|
|
34
|
+
"ConformerSearchResults",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class Wavefunction(QCDataBaseModel):
|
|
39
|
+
"""The wavefunction for a single point calculation.
|
|
40
|
+
|
|
41
|
+
Attributes:
|
|
42
|
+
scf_eigenvalues_a: The SCF alpha-spin orbital eigenvalues.
|
|
43
|
+
scf_eigenvalues_b: The SCF beta-spin orbital eigenvalues.
|
|
44
|
+
scf_occupations_a: The SCF alpha-spin orbital occupations.
|
|
45
|
+
scf_occupations_b: The SCF beta-spin orbital occupations.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
scf_eigenvalues_a: SerializableNDArray | None = None
|
|
49
|
+
scf_eigenvalues_b: SerializableNDArray | None = None
|
|
50
|
+
scf_occupations_a: SerializableNDArray | None = None
|
|
51
|
+
scf_occupations_b: SerializableNDArray | None = None
|
|
52
|
+
|
|
53
|
+
@field_validator(
|
|
54
|
+
"scf_eigenvalues_a",
|
|
55
|
+
"scf_eigenvalues_b",
|
|
56
|
+
"scf_occupations_a",
|
|
57
|
+
"scf_occupations_b",
|
|
58
|
+
)
|
|
59
|
+
@classmethod
|
|
60
|
+
def to_numpy(cls, val, _info) -> np.ndarray | None:
|
|
61
|
+
return np.asarray(val) if val is not None else None
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class CalcInfoData(BaseModel):
|
|
65
|
+
"""Mixin for calcinfo attributes.
|
|
66
|
+
|
|
67
|
+
Attributes:
|
|
68
|
+
calcinfo_natoms: The number of atoms as computed by the program.
|
|
69
|
+
calcinfo_nalpha: The number of alpha electrons as computed by the program.
|
|
70
|
+
calcinfo_nbeta: The number of beta electrons as computed by the program.
|
|
71
|
+
calcinfo_nbasis: The number of basis functions in the calculation.
|
|
72
|
+
calcinfo_nmo: The number of molecular orbitals in the calculation.
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
calcinfo_natoms: int | None = None
|
|
76
|
+
calcinfo_nalpha: int | None = None
|
|
77
|
+
calcinfo_nbeta: int | None = None
|
|
78
|
+
calcinfo_nbasis: int | None = None
|
|
79
|
+
calcinfo_nmo: int | None = None
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class SinglePointData(Files, CalcInfoData):
|
|
83
|
+
"""The computed data from a single point calculation.
|
|
84
|
+
|
|
85
|
+
Attributes:
|
|
86
|
+
energy: The electronic energy of the structure in `Hartrees`.
|
|
87
|
+
gradient: The gradient of the structure in `Hartrees/Bohr`.
|
|
88
|
+
hessian: The hessian of the structure in `Hartrees/Bohr^2`.
|
|
89
|
+
nuclear_repulsion_energy: The nuclear repulsion energy of the structure in
|
|
90
|
+
Hartrees.
|
|
91
|
+
|
|
92
|
+
wavefunction: Wavefunction data from the calculation.
|
|
93
|
+
|
|
94
|
+
freqs_wavenumber: The frequencies of the structure in wavenumbers.
|
|
95
|
+
normal_modes_cartesian: 3D n_vibmodes x n_atoms x 3 array containing
|
|
96
|
+
un-mass-weighted Cartesian displacements of each normal mode in Bohr.
|
|
97
|
+
gibbs_free_energy: Gibbs free energy (i.e. thermochemical analysis) in Hartrees
|
|
98
|
+
of a system where translation / rotation / vibration degrees of freedom are
|
|
99
|
+
approximated using ideal gas / rigid rotor / harmonic oscillator
|
|
100
|
+
respectively.
|
|
101
|
+
scf_dipole_moment: The x, y, z component of the dipole moment of the structure
|
|
102
|
+
in units of e a0 (NOT Debye!).
|
|
103
|
+
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
energy: float | None = None
|
|
107
|
+
gradient: SerializableNDArray | None = None
|
|
108
|
+
hessian: SerializableNDArray | None = None
|
|
109
|
+
nuclear_repulsion_energy: float | None = None
|
|
110
|
+
|
|
111
|
+
wavefunction: Wavefunction | None = None
|
|
112
|
+
|
|
113
|
+
freqs_wavenumber: list[float] = []
|
|
114
|
+
normal_modes_cartesian: SerializableNDArray | None = None
|
|
115
|
+
gibbs_free_energy: float | None = None
|
|
116
|
+
|
|
117
|
+
scf_dipole_moment: list[float] | None = None
|
|
118
|
+
|
|
119
|
+
@field_validator("normal_modes_cartesian")
|
|
120
|
+
@classmethod
|
|
121
|
+
def _validate_normal_modes_cartesian_shape(
|
|
122
|
+
cls, v: SerializableNDArray, info: ValidationInfo
|
|
123
|
+
):
|
|
124
|
+
if v is not None:
|
|
125
|
+
freqs = info.data.get("freqs_wavenumber")
|
|
126
|
+
n_normal_modes = len(freqs) if freqs else len(v)
|
|
127
|
+
return np.asarray(v).reshape(n_normal_modes, -1, 3)
|
|
128
|
+
return v
|
|
129
|
+
|
|
130
|
+
@field_validator("gradient")
|
|
131
|
+
@classmethod
|
|
132
|
+
def _validate_gradient_shape(cls, v: SerializableNDArray):
|
|
133
|
+
"""Validate gradient is n x 3."""
|
|
134
|
+
if v is not None:
|
|
135
|
+
return np.asarray(v).reshape(-1, 3)
|
|
136
|
+
|
|
137
|
+
@field_validator("hessian")
|
|
138
|
+
@classmethod
|
|
139
|
+
def _validate_hessian_shape(cls, v: SerializableNDArray):
|
|
140
|
+
"""Validate hessian is square."""
|
|
141
|
+
if v is not None:
|
|
142
|
+
v = np.asarray(v)
|
|
143
|
+
n = int(np.sqrt(v.size))
|
|
144
|
+
return v.reshape((n, n))
|
|
145
|
+
|
|
146
|
+
def return_result(self, calctype: CalcType) -> float | SerializableNDArray:
|
|
147
|
+
"""Return the primary result of the calculation."""
|
|
148
|
+
return getattr(self, calctype.value)
|
|
149
|
+
|
|
150
|
+
@model_validator(mode="after")
|
|
151
|
+
def _ensure_results(self) -> Self:
|
|
152
|
+
"""Ensure that at least one result is present."""
|
|
153
|
+
if all(
|
|
154
|
+
result is None
|
|
155
|
+
for result in [self.energy, self.gradient, self.hessian]
|
|
156
|
+
):
|
|
157
|
+
raise ValueError(
|
|
158
|
+
"SinglePointResults requires either an energy, gradient, or hessian "
|
|
159
|
+
"value."
|
|
160
|
+
)
|
|
161
|
+
return self
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
class OptimizationData(Files, CalcInfoData):
|
|
165
|
+
"""Computed data for an optimization (may be for a minimum or transition state).
|
|
166
|
+
|
|
167
|
+
Attributes:
|
|
168
|
+
energies: The energies for each step of the optimization.
|
|
169
|
+
structures: The Structure objects for each step of the optimization.
|
|
170
|
+
final_structure: The final, optimized Structure.
|
|
171
|
+
trajectory: The ProgramOutput objects for each step of the optimization.
|
|
172
|
+
"""
|
|
173
|
+
|
|
174
|
+
trajectory: list[
|
|
175
|
+
"ProgramOutput[ProgramInput, SinglePointData] | ProgramOutput[ProgramInput, Files]"
|
|
176
|
+
] = []
|
|
177
|
+
|
|
178
|
+
@property
|
|
179
|
+
def final_structure(self) -> Structure:
|
|
180
|
+
"""The final Structure in the optimization."""
|
|
181
|
+
return self.structures[-1]
|
|
182
|
+
|
|
183
|
+
@property
|
|
184
|
+
def final_molecule(self) -> Structure:
|
|
185
|
+
warnings.warn(
|
|
186
|
+
".final_molecule is being deprecated and will be removed in a future. "
|
|
187
|
+
"Please use .final_structure instead.",
|
|
188
|
+
category=FutureWarning,
|
|
189
|
+
stacklevel=2,
|
|
190
|
+
)
|
|
191
|
+
return self.final_structure
|
|
192
|
+
|
|
193
|
+
@property
|
|
194
|
+
def final_energy(self) -> float | None:
|
|
195
|
+
"""
|
|
196
|
+
The final energy in the optimization. Is `np.nan` if final calculation failed.
|
|
197
|
+
"""
|
|
198
|
+
return self.energies[-1]
|
|
199
|
+
|
|
200
|
+
@property
|
|
201
|
+
def energies(self) -> np.ndarray:
|
|
202
|
+
"""The energies for each step of the optimization."""
|
|
203
|
+
return np.array(
|
|
204
|
+
[
|
|
205
|
+
output.data.energy if output.success else np.nan # type: ignore
|
|
206
|
+
for output in self.trajectory
|
|
207
|
+
],
|
|
208
|
+
dtype=float,
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
@property
|
|
212
|
+
def structures(self) -> list[Structure]:
|
|
213
|
+
"""The Structure objects for each step of the optimization."""
|
|
214
|
+
return [output.input_data.structure for output in self.trajectory]
|
|
215
|
+
|
|
216
|
+
def return_result(self, calctype: CalcType) -> Structure | None:
|
|
217
|
+
return self.final_structure
|
|
218
|
+
|
|
219
|
+
def __repr_args__(self):
|
|
220
|
+
"""Custom repr to avoid printing the entire collection of objects."""
|
|
221
|
+
return [
|
|
222
|
+
("final_structure", f"{self.final_structure}"),
|
|
223
|
+
("trajectory", "[...]"),
|
|
224
|
+
("energies", "[...]"),
|
|
225
|
+
("structures", "[...]"),
|
|
226
|
+
]
|
|
227
|
+
|
|
228
|
+
def to_xyz(self) -> str:
|
|
229
|
+
"""Return the trajectory as an `xyz` string."""
|
|
230
|
+
return to_multi_xyz(
|
|
231
|
+
prog_output.input_data.structure for prog_output in self.trajectory
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
def save(
|
|
235
|
+
self,
|
|
236
|
+
filepath: Path | str,
|
|
237
|
+
exclude_none: bool = True,
|
|
238
|
+
exclude_unset: bool = True,
|
|
239
|
+
indent: int = 4,
|
|
240
|
+
**kwargs: dict[str, Any],
|
|
241
|
+
) -> None:
|
|
242
|
+
"""Save an OptimizationOutput to a file.
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
filepath: The path to save the molecule to.
|
|
246
|
+
exclude_none: If True, attributes with a value of None will not be written
|
|
247
|
+
to the file.
|
|
248
|
+
exclude_unset: If True, attributes that have not been set will not be
|
|
249
|
+
written to the file.
|
|
250
|
+
**kwargs: Additional keyword arguments to pass to the json serializer.
|
|
251
|
+
|
|
252
|
+
Note:
|
|
253
|
+
If the filepath has a `.xyz` extension, the trajectory will be saved to a
|
|
254
|
+
multi-structure `xyz` file.
|
|
255
|
+
"""
|
|
256
|
+
filepath = Path(filepath)
|
|
257
|
+
if filepath.suffix == ".xyz":
|
|
258
|
+
filepath.write_text(self.to_xyz())
|
|
259
|
+
return
|
|
260
|
+
super().save(filepath, exclude_none, exclude_unset, indent, **kwargs)
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
class ConformerSearchData(Files):
|
|
264
|
+
"""Data from a conformer search calculation.
|
|
265
|
+
|
|
266
|
+
Conformers and rotamers are sorted by energy.
|
|
267
|
+
|
|
268
|
+
Attributes:
|
|
269
|
+
conformers: The conformers found in the search.
|
|
270
|
+
conformer_energies: The energies for each conformer.
|
|
271
|
+
rotamers: The rotamers found in the search.
|
|
272
|
+
rotamer_energies: The energies for each rotamer.
|
|
273
|
+
"""
|
|
274
|
+
|
|
275
|
+
conformers: list[Structure] = []
|
|
276
|
+
conformer_energies: SerializableNDArray = np.array([])
|
|
277
|
+
rotamers: list[Structure] = []
|
|
278
|
+
rotamer_energies: SerializableNDArray = np.array([])
|
|
279
|
+
|
|
280
|
+
@model_validator(mode="after")
|
|
281
|
+
def _energies_size(self) -> Self:
|
|
282
|
+
"""Ensure the energies are the same size as the conformers and rotamers."""
|
|
283
|
+
if self.conformer_energies.size > 0 and (
|
|
284
|
+
self.conformer_energies.size != len(self.conformers)
|
|
285
|
+
):
|
|
286
|
+
raise ValueError(
|
|
287
|
+
"The number of conformer energies must match the number of conformers."
|
|
288
|
+
)
|
|
289
|
+
if self.rotamer_energies.size > 0 and (
|
|
290
|
+
self.rotamer_energies.size != len(self.rotamers)
|
|
291
|
+
):
|
|
292
|
+
raise ValueError(
|
|
293
|
+
"The number of rotamer energies must match the number of rotamers."
|
|
294
|
+
)
|
|
295
|
+
return self
|
|
296
|
+
|
|
297
|
+
@model_validator(mode="after")
|
|
298
|
+
def _sort_by_energy(self) -> Self:
|
|
299
|
+
"""Sort conformers and rotamers by energy."""
|
|
300
|
+
if self.conformer_energies.size > 0:
|
|
301
|
+
sorted_indices = np.argsort(self.conformer_energies)
|
|
302
|
+
self.conformers[:] = [self.conformers[i] for i in sorted_indices]
|
|
303
|
+
self.conformer_energies[:] = self.conformer_energies[sorted_indices]
|
|
304
|
+
|
|
305
|
+
if self.rotamer_energies.size > 0:
|
|
306
|
+
sorted_indices = np.argsort(self.rotamer_energies)
|
|
307
|
+
self.rotamers[:] = [self.rotamers[i] for i in sorted_indices]
|
|
308
|
+
self.rotamer_energies[:] = self.rotamer_energies[sorted_indices]
|
|
309
|
+
|
|
310
|
+
return self
|
|
311
|
+
|
|
312
|
+
@property
|
|
313
|
+
def conformer_energies_relative(self) -> np.ndarray:
|
|
314
|
+
"""The relative energies for each conformer in the search."""
|
|
315
|
+
if self.conformer_energies.size == 0:
|
|
316
|
+
return np.array([])
|
|
317
|
+
return self.conformer_energies - self.conformer_energies.min()
|
|
318
|
+
|
|
319
|
+
@property
|
|
320
|
+
def rotamer_energies_relative(self) -> np.ndarray:
|
|
321
|
+
"""The relative energies for each rotamer in the search."""
|
|
322
|
+
if self.rotamer_energies.size == 0:
|
|
323
|
+
return np.array([])
|
|
324
|
+
return self.rotamer_energies - self.rotamer_energies.min()
|
|
325
|
+
|
|
326
|
+
def conformers_filtered(
|
|
327
|
+
self,
|
|
328
|
+
threshold: float = 1.0,
|
|
329
|
+
**rmsd_kwargs,
|
|
330
|
+
) -> tuple[list[Structure], SerializableNDArray]:
|
|
331
|
+
"""
|
|
332
|
+
!!! warning "Moved since *qcdata* 0.15.0"
|
|
333
|
+
This convenience method has moved to
|
|
334
|
+
[`qcinf.filter_conformers`][qcinf.filter_conformers]
|
|
335
|
+
and this stub will be **removed** from *qcdata* in a future release.
|
|
336
|
+
|
|
337
|
+
```python
|
|
338
|
+
from qcinf import filter_conformers
|
|
339
|
+
|
|
340
|
+
filtered_csr = filter_conformers(
|
|
341
|
+
conformers=csr
|
|
342
|
+
threshold=1.0, # Bohr
|
|
343
|
+
backend="qcinf", # or "rdkit",
|
|
344
|
+
**rmsd_kwargs,
|
|
345
|
+
)
|
|
346
|
+
```
|
|
347
|
+
"""
|
|
348
|
+
warnings.warn(
|
|
349
|
+
"`ConformerSearchResults.conformers_filtered()` is deprecated. "
|
|
350
|
+
"Install *qcinf* and use `qcinf.filter_conformers` instead.",
|
|
351
|
+
DeprecationWarning,
|
|
352
|
+
stacklevel=2,
|
|
353
|
+
)
|
|
354
|
+
raise NotImplementedError(
|
|
355
|
+
"Method removed. Replace with:\n\n"
|
|
356
|
+
" from qcinf import filter_conformers\n\n"
|
|
357
|
+
" filtered_csr = filter_conformers(\n"
|
|
358
|
+
" prog_output.results,\n"
|
|
359
|
+
" threshold=1.0,\n"
|
|
360
|
+
" backend='qcinf',\n"
|
|
361
|
+
" **rmsd_kwargs\n"
|
|
362
|
+
" )"
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
StructuredData = Union[SinglePointData, OptimizationData, ConformerSearchData]
|
|
367
|
+
StructuredDataType = TypeVar("StructuredDataType", bound=StructuredData)
|
|
368
|
+
Data = Union[Files, StructuredData]
|
|
369
|
+
DataType = TypeVar("DataType", bound=Data)
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
@deprecated_class("SinglePointData")
|
|
373
|
+
class SinglePointResults(SinglePointData):
|
|
374
|
+
"""This class is deprecated and will be removed in a future release. Please use
|
|
375
|
+
`SinglePointData` instead."""
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
@deprecated_class("OptimizationData")
|
|
379
|
+
class OptimizationResults(OptimizationData):
|
|
380
|
+
"""This class is deprecated and will be removed in a future release. Please use
|
|
381
|
+
`OptimizationData` instead."""
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
@deprecated_class("ConformerSearchData")
|
|
385
|
+
class ConformerSearchResults(ConformerSearchData):
|
|
386
|
+
"""This class is deprecated and will be removed in a future release. Please use
|
|
387
|
+
`ConformerSearchData` instead."""
|
qcdata/models/inputs.py
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"""Input models for quantum chemistry calculations."""
|
|
2
|
+
|
|
3
|
+
import warnings
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any, TypeVar, Union
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, field_serializer, model_validator
|
|
8
|
+
from typing_extensions import Self
|
|
9
|
+
|
|
10
|
+
from .base_models import CalcType, Files, Model
|
|
11
|
+
from .structure import Structure
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"FileInput",
|
|
15
|
+
"ProgramInput",
|
|
16
|
+
"DualProgramInput",
|
|
17
|
+
"ProgramArgs",
|
|
18
|
+
"ProgramArgsSub",
|
|
19
|
+
"Inputs",
|
|
20
|
+
"InputType",
|
|
21
|
+
"StructuredInputs",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class FileInput(Files):
|
|
26
|
+
"""File and command line argument inputs for a calculation.
|
|
27
|
+
|
|
28
|
+
Attributes:
|
|
29
|
+
files Files: A dict mapping filename to str or bytes data.
|
|
30
|
+
cmdline_args: A list of command line arguments to be passed to the program.
|
|
31
|
+
extras Dict[str, Any]: Additional information to bundle with the object. Use for
|
|
32
|
+
schema development and scratch space.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
cmdline_args: list[str] = []
|
|
36
|
+
|
|
37
|
+
@classmethod
|
|
38
|
+
def from_directory(cls, directory: Path | str, **kwargs) -> Self:
|
|
39
|
+
"""Create a new FileInput and collect all files in the directory."""
|
|
40
|
+
obj = cls(**kwargs)
|
|
41
|
+
directory = Path(directory)
|
|
42
|
+
obj.add_files(directory)
|
|
43
|
+
return obj
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class _KeywordsMixin(BaseModel):
|
|
47
|
+
"""Mixin for keywords attribute.
|
|
48
|
+
|
|
49
|
+
Attributes:
|
|
50
|
+
Keywords: dict[str, Any]: A dict of keywords to be passed to the program
|
|
51
|
+
excluding model and calctype. Defaults to an empty dict.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
keywords: dict[str, Any] = {}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class _StructureKeywordsMixin(_KeywordsMixin):
|
|
58
|
+
"""
|
|
59
|
+
Attributes:
|
|
60
|
+
structure: The structure to be used in the calculation.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
structure: Structure
|
|
64
|
+
|
|
65
|
+
def __init__(self, **data: Any):
|
|
66
|
+
"""Backwards compatibility for 'molecule' attribute."""
|
|
67
|
+
|
|
68
|
+
# TODO: Remove in future versions.
|
|
69
|
+
if "molecule" in data:
|
|
70
|
+
warnings.warn(
|
|
71
|
+
"Use of 'molecule' attribute is deprecated. Use 'structure' instead.",
|
|
72
|
+
FutureWarning,
|
|
73
|
+
stacklevel=2,
|
|
74
|
+
)
|
|
75
|
+
data["structure"] = data.pop("molecule")
|
|
76
|
+
super().__init__(**data)
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def molecule(self) -> Structure:
|
|
80
|
+
"""Backwards compatibility for 'molecule' attribute."""
|
|
81
|
+
warnings.warn(
|
|
82
|
+
"Use of 'molecule' attribute is deprecated. Use 'structure' instead.",
|
|
83
|
+
FutureWarning,
|
|
84
|
+
stacklevel=2,
|
|
85
|
+
)
|
|
86
|
+
return self.structure
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class ProgramArgs(FileInput, _KeywordsMixin):
|
|
90
|
+
"""Core arguments for a calculation without a calctype or structure.
|
|
91
|
+
|
|
92
|
+
This class is used by `DualProgramInput` or multi-step calculations to
|
|
93
|
+
specify `subprogram_args` or basic program arguments for a multistep algorithm in
|
|
94
|
+
BigChem. It is not intended to be used directly for single-step calculations since
|
|
95
|
+
it lacks a `calctype` and `structure`.
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
Attributes:
|
|
99
|
+
model Model: The model for the quantum chemistry calculation.
|
|
100
|
+
keywords Dict[str, Any]: A dict of keywords to be passed to the program
|
|
101
|
+
excluding model and calctype. Defaults to an empty dict.
|
|
102
|
+
files Files: Files to be passed to the QC program.
|
|
103
|
+
extras Dict[str, Any]: Additional information to bundle with the object. Use
|
|
104
|
+
for schema development and scratch space.
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
model: Model
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class ProgramArgsSub(FileInput, _KeywordsMixin):
|
|
111
|
+
"""Generic arguments for a calculation that also calls a sub-calculation.
|
|
112
|
+
|
|
113
|
+
This class is needed for multi-step calculations where the calctype and structure
|
|
114
|
+
are specified only once for the entire calculation, e.g., multistep_opt in BigChem.
|
|
115
|
+
|
|
116
|
+
Attributes:
|
|
117
|
+
model: The model for the quantum chemistry calculation
|
|
118
|
+
keywords: A dict of keywords to be passed to the program excluding model and
|
|
119
|
+
calctype. Defaults to an empty dict.
|
|
120
|
+
files: Files to be passed to the QC program.
|
|
121
|
+
subprogram: The name of the subprogram to use.
|
|
122
|
+
subprogram_args: The ProgramArgs for the subprogram.
|
|
123
|
+
extras: Additional information to bundle with the object. Use for schema
|
|
124
|
+
development and scratch space.
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
model: Model | None = None
|
|
128
|
+
subprogram: str
|
|
129
|
+
subprogram_args: ProgramArgs
|
|
130
|
+
|
|
131
|
+
@model_validator(mode="before")
|
|
132
|
+
@classmethod
|
|
133
|
+
def _backcompat(cls, payload: dict[str, Any]) -> dict[str, Any]:
|
|
134
|
+
"""Backwards compatibility for 'subprogram_args' attribute."""
|
|
135
|
+
if (
|
|
136
|
+
isinstance(payload, dict)
|
|
137
|
+
and "subprogram_args" in payload
|
|
138
|
+
and "subprogram_args" not in payload
|
|
139
|
+
):
|
|
140
|
+
payload = dict(payload)
|
|
141
|
+
payload["subprogram_args"] = payload.pop("subprogram_args")
|
|
142
|
+
warnings.warn(
|
|
143
|
+
"'subprogram_args' has been renamed to 'subprogram_args' (ProgramArgs).",
|
|
144
|
+
FutureWarning,
|
|
145
|
+
stacklevel=2,
|
|
146
|
+
)
|
|
147
|
+
return payload
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
class ProgramInput(ProgramArgs, _StructureKeywordsMixin):
|
|
151
|
+
"""Core input for a quantum chemistry calculation. This is the most common input type.
|
|
152
|
+
|
|
153
|
+
Attributes:
|
|
154
|
+
calctype CalcType: The type of calculation to perform.
|
|
155
|
+
model Model: The model for the quantum chemistry calculation.
|
|
156
|
+
keywords Dict[str, Any]: A dict of keywords to be passed to the program
|
|
157
|
+
excluding model and calctype. Defaults to an empty dict.
|
|
158
|
+
structure Structure: The structure to be used in the calculation.
|
|
159
|
+
files Files: Files to be passed to the QC program.
|
|
160
|
+
extras Dict[str, Any]: Additional information to bundle with the object. Use
|
|
161
|
+
for schema development and scratch space.
|
|
162
|
+
|
|
163
|
+
Example:
|
|
164
|
+
```python
|
|
165
|
+
from qcdata.models import ProgramInput, Structure
|
|
166
|
+
|
|
167
|
+
struct = Structure.open("path/to/structure.xyz")
|
|
168
|
+
|
|
169
|
+
prog_inp = ProgramInput(
|
|
170
|
+
calctype = "energy",
|
|
171
|
+
structure = struct,
|
|
172
|
+
model = {"method": "hf", "basis": "6-31G"},
|
|
173
|
+
keywords = {"maxsteps": "250"}, # Optional
|
|
174
|
+
files = {"file1": b"binary data"} # Optional
|
|
175
|
+
)
|
|
176
|
+
```
|
|
177
|
+
"""
|
|
178
|
+
|
|
179
|
+
calctype: CalcType
|
|
180
|
+
|
|
181
|
+
@field_serializer("calctype")
|
|
182
|
+
def _serialize_calctype(self, calctype: CalcType, _info) -> str:
|
|
183
|
+
"""Serialize CalcType to string"""
|
|
184
|
+
return calctype.value
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
class DualProgramInput(ProgramArgsSub, ProgramInput):
|
|
188
|
+
"""Input for a two program calculation.
|
|
189
|
+
|
|
190
|
+
Attributes:
|
|
191
|
+
calctype CalcType: The type of calculation to perform.
|
|
192
|
+
model Model: The model for the quantum chemistry calculation.
|
|
193
|
+
keywords Dict[str, Any]: A dict of keywords to be passed to the program
|
|
194
|
+
excluding model and calctype. Defaults to an empty dict.
|
|
195
|
+
structure Structure: The structure to be used in the calculation.
|
|
196
|
+
files Files: Files to be passed to the QC program.
|
|
197
|
+
subprogram: The name of the subprogram to use.
|
|
198
|
+
subprogram_args ProgramArgs: The ProgramArgs for the subprogram.
|
|
199
|
+
extras Dict[str, Any]: Additional information to bundle with the object. Use
|
|
200
|
+
for schema development and scratch space.
|
|
201
|
+
|
|
202
|
+
Example:
|
|
203
|
+
```python
|
|
204
|
+
from qcdata.models import DualProgramInput, ProgramArgs, Structure
|
|
205
|
+
|
|
206
|
+
struct = Structure.open("path/to/structure.xyz")
|
|
207
|
+
|
|
208
|
+
prog_inp = DualProgramInput(
|
|
209
|
+
calctype = "optimization",
|
|
210
|
+
structure = struct,
|
|
211
|
+
keywords = {"maxiter": "250"}, # Optional
|
|
212
|
+
subprogram = "orca",
|
|
213
|
+
subprogram_args = ProgramArgs(
|
|
214
|
+
model = {"method": "wb97x-d3", "basis": "def2-SVP"},
|
|
215
|
+
keywords = {"convthre": "1e-6"}, # Optional
|
|
216
|
+
)
|
|
217
|
+
)
|
|
218
|
+
```
|
|
219
|
+
"""
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
Inputs = Union[FileInput, ProgramInput, DualProgramInput]
|
|
223
|
+
InputType = TypeVar("InputType", bound=Inputs)
|
|
224
|
+
StructuredInputs = Union[ProgramInput, DualProgramInput]
|