-
Notifications
You must be signed in to change notification settings - Fork 15
/
chemicaljson.py
277 lines (199 loc) · 12.1 KB
/
chemicaljson.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
from __future__ import annotations
from typing import List, Optional
from pydantic import BaseModel, Field
class Elements(BaseModel):
"""List of elements for the atoms in the molecule.
Length must match the number of atoms.
"""
number: List[int] = Field(..., description="Required list of atomic numbers for the atoms in this file.")
class Coords(BaseModel):
"""Coordinates for the atoms in the molecule.
Length must match the number of atoms*3 (x, y, z).
"""
field3d: List[float] = Field(..., alias="3d", description="List of 3d Cartesian coordinates (in Angstrom) for the atoms [ x, y, z, x, y, z, ... ]")
field3dFractional: Optional[List[float]] = Field(None, alias="3dFractional", description="Optional list of 3d fractional coordinates for the atoms [ x, y, z, x, y, z, ... ]")
field3dSets: Optional[List[List[float]]] = Field(None, alias="3dSets", description="Optional list of lists of 3d Cartesian coordinates (in Angstrom) for the atoms [ [x, y, z], [x, y, z], ... ]")
class Atoms(BaseModel):
"""Atoms in the molecule."""
elements: Elements = Field(..., description="List of atomic numbers for the atoms.")
coords: Coords = Field(..., description="List of coordinates.")
formalCharges: Optional[List[int]] = Field(None, description="Optional list of formal charges for the atoms.")
labels: Optional[List[str]] = Field(None, description="Optional list of custom labels for atoms (e.g., 'R' / 'S' or '0.12', etc.)")
layer: Optional[List[int]] = Field(None, description="Optional list of layer numbers for the atoms (generally just 1 for most molecules).")
class Connections(BaseModel):
"""Connections - list of connections between atom indices.
Length must be the number of bonds * 2
"""
index: List[int]
class Bonds(BaseModel):
"""
Optional bonds between atoms, including connections and bond orders for the atoms in the molecule. (Optional)
"""
connections: Connections
order: List[int]
class BasisSet(BaseModel):
"""Basis Set information (optional)
At the moment, implied to be Gaussian basis sets.
"""
coefficients: List[float] = Field(..., description="List of coefficients for the basis functions.")
exponents: List[float] = Field(..., description="List of exponents for the basis functions.")
primitivesPerShell: List[int] = Field(..., description="List of number of primitives per shell.")
shellToAtomMap: List[int] = Field(..., description="List of atom indices for the basis functions.")
shellTypes: List[int] = Field(..., description="List of shell types for the basis functions (l-value, so s=0, p=1, d=2, etc.).")
class Orbitals(BaseModel):
"""Information about molecular orbital energies and coefficients. (Optional)
To be useful, this should include basis set information, electronCount, energies,
"""
electronCount: int = Field(..., description="Number of electrons in the species")
energies: Optional[List[float]] = Field(None, description="List of energies for the molecular orbitals (in eV)")
moCoefficients: Optional[List[float]] = Field(None, description="List of coefficients (flattened) for restricted molecular orbitals, i.e., alpha=beta (requires BasisSet to be present).")
alphaCoefficients: Optional[List[float]] = Field(None, description="List of coefficients (flattened) for alpha open-shell orbitals, (requires BasisSet to be present).")
betaCoefficients: Optional[List[float]] = Field(None, description="List of coefficients (flattened) for beta open-shell orbitals, (requires BasisSet to be present).")
occupations: Optional[List[int]] = Field(None, description="List of occupations for the molecular orbitals")
symmetries: Optional[List[List[str]]] = Field(None, description="Symmetry of the orbital (e.g., a1, eg, t1g, etc.)")
class Electronic(BaseModel):
"""Electronic spectra (optional)"""
energies: List[float] = Field(..., description="List of excitation energies for the electronic spectra (in eV)")
intensities: List[float] = Field(..., description="List of intensities for the electronic spectra")
rotation: Optional[List[float]] = Field(None, description="Optional list of rotation angles for the CD spectra (in degrees)")
class Nmr(BaseModel):
"""NMR spectra (optional)"""
shifts: List[float] = Field(..., description="List of absolute chemical shifts for the NMR spectra (in ppm)")
class Spectra(BaseModel):
"""Spectra (optional)
Objects for non-vibrational spectra, including electronic, NMR, and other spectra.
"""
electronic: Optional[Electronic] = Field(None, description="Optional electronic spectra")
nmr: Optional[Nmr] = Field(None, description="Optional NMR spectra")
class Properties(BaseModel):
"""Properties of the molecule / system. (Optional)
A set of key-value properties.
"""
molecularMass: Optional[float] = None
meltingPoint: Optional[float] = None
boilingPoint: Optional[float] = None
totalCharge: Optional[int] = Field(0, description="Total charge of the system. If omitted, assume 0 (charge neutral)")
spinMultiplicity: Optional[int] = Field(1, description="Total spin multiplicity of the system (2S+1, e.g., 1, 2, 3, etc.). If omitted, assume to be 1 (singlet)")
totalEnergy: Optional[float] = Field(None, description="Optional total energy of the system in eV")
class Metadata(BaseModel):
"""Metadata for the calculation. (Optional)
Attributes:
runDate: date calculation was done
"""
runDate: Optional[str] = None
class InputParameters(BaseModel):
"""Input parameters for the calculation. (Optional)
Attributes:
basis: Basis set used for the calculation (e.g. "6-31G(d)" or "Custom").
dispersion: Dispersion correction used for the calculation (e.g. "D3" or "D3BJ")
functional: Functional used for the calculation if DFT (e.g. "B3LYP" or "Custom").
grid: Keyword describing the DFT grid keyword usedf if DFT.
memory: The amount of memory requested for the calculation.
processors: The number of processors requested for the calculation.
task: "Energy" or "Optimize" or "Frequencies" or "Transition State" or "Custom".
theory: Method used for the calculation (e.g. "DFT" or "HF" or "MP2").
"""
basis: Optional[str] = None
dispersion: Optional[str] = None
functional: Optional[str] = None
grid: Optional[str] = None
memory: Optional[str] = None
processors: Optional[str] = None
task: Optional[str] = None
theory: Optional[str] = None
class PartialCharges(BaseModel):
"""Partial charges for the atoms in the molecule. (Optional)
Keys represent the partial charge method, followed by the computed partial charges.
e.g.
- "Mulliken": [ 0.01, 0.02, 0.03, ... ]
- "Gasteiger": [ 0.01, 0.02, 0.03, ... ]
"""
mulliken: List[float]
class UnitCell(BaseModel):
"""Unit cell for the system. (Optional)
Current versions of Avogadro will output and preferentially use cellVectors,
since they fully specify the unit cell, but will also output a, b, c,
alpha, beta, gamma parameters and use them if no cellVectors field is found.
"""
a: float = Field(..., description="Unit cell a-axis length (in Angstrom).")
b: float = Field(..., description="Unit cell b-axis length (in Angstrom).")
c: float = Field(..., description="Unit cell c-axis length (in Angstrom).")
alpha: float = Field(..., description="Unit cell alpha angle (in degrees).")
beta: float = Field(..., description="Unit cell beta angle (in degrees).")
gamma: float = Field(..., description="Unit cell gamma angle (in degrees).")
cellVectors: Optional[List[float]] = Field(
min_items=9, max_items=9, default_factory=lambda: [0.0 for _ in range(9)], description="Optional list of cell vectors (in Angstrom): [ x1, y1, z1, x2, y2, z2, ... ]"
)
class Vibrations(BaseModel):
"""Vibrations for the molecule. (Optional)
Attributes:
ramanIntensities: Optional list of Raman intensities for the vibrations.
modes: Optional list of mode numbers (e.g, [ 1, 2, 3, 4, 5, 6, ... ])
symmetries: Optional list of symmetries for the vibrations (e.g., 'a1g', 'eg' ...)
"""
frequencies: List[float] = Field(... , description="List of frequencies (in cm-1) for the vibrations.")
intensities: List[float] = Field(... , description="List of IR intensities for the vibrations.")
eigenVectors: List[List[float]] = Field(..., description="List of eigenvectors (displacements in Angstroms) for the vibrations.")
ramanIntensities: Optional[List[float]]
symmetries: Optional[List[str]]
modes: Optional[List[int]]
class Enable(BaseModel):
"""Optional enable flags for different render types for each layer
Length of each much match the number of layers.
"""
ballAndStick: Optional[List[bool]] = Field(alias="Ball and Stick")
cartoons: Optional[List[bool]]
closeContacts: Optional[List[bool]] = Field(alias="Close Contacts")
labels: Optional[List[bool]]
licorice: Optional[List[bool]]
vanDerWaals: Optional[List[bool]] = Field(alias="Van der Waals")
wireframe: Optional[List[bool]]
class Settings(BaseModel):
"""Settings for the render types. (Optional)"""
ballAndStick: Optional[List[str]] = Field(alias="Ball and Stick", description="Settings for the Ball and Stick rendering type")
cartoons: Optional[List[str]]
wireframe: Optional[List[str]]
class Layer(BaseModel):
"""Layer settings for the molecule. (Optional)
Attributes:
enable: Enable flags for different render types for each layer.
settings: Settings for the render types.
locked: List of locked layers (e.g., atoms in this layer should not change)
visible: List of visible layers (e.g., atoms in this layer should be visible / invisible)
"""
enable: Enable
locked: List[bool]
settings: Settings
visible: List[bool]
class CJSONModel(BaseModel):
"""Full Chemical JSON model.
A Chemical JSON (CJSON) model is intended to represent one molecular or periodic system.
Catenating multiple systems will result in invalid JSON - store separate systems as
separate files / JSON entries.
Attributes:
vibrations: Vibrations object. Optional.
unitCell: UnitCell object. Optional.
layer: Layer object. Optional (used by the GUI for rendering / settings)
basisSet: BasisSet object. Optional.
orbitals: Orbitals object. Optional. Requires BasisSet to be present.
"""
chemicalJson: int = Field(1, description="Version number of the Chemical JSON format. Currently 1. Only changed for backwards-incompatible changes to the schema.")
atoms: Atoms = Field(..., description="Atoms object, describing the atoms in this system.")
name: Optional[str] = Field(None, description="Optional name / title for the molecule")
inchi: Optional[str] = Field(None, description="Optional InChI descriptor for the molecule")
formula: Optional[str] = Field(None, description="Optional chemical formula in Hill order")
bonds: Optional[Bonds] = Field(None, description="Optional Bonds object, describing covalent bonds")
properties: Optional[Properties] = Field(None, description="Optional free-form Properties, including total charge and total spin multiplicity.")
inputParameters: Optional[InputParameters] = Field(None, description="Optional InputParameters object, including calculation metadata such as basis set, job type, etc.")
metadata: Optional[Metadata] = Field(None, description="Optional metadata object, including calculation details which do not directly impact results")
partialCharges: Optional[PartialCharges] = Field(None, description="Optional PartialCharges object. Includes atomic partial charges and population analysis.")
vibrations: Optional[Vibrations] = None
unitCell: Optional[UnitCell] = None
layer: Optional[Layer] = None
basisSet: Optional[BasisSet] = None
orbitals: Optional[Orbitals] = None
spectra: Optional[Spectra] = None
if __name__ == "__main__":
with open("cjson.schema", "w") as handle:
handle.write(CJSONModel.schema_json(indent=2))
handle.write("\n")