digichem-core 6.0.0rc1__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.
- digichem/__init__.py +75 -0
- digichem/basis.py +116 -0
- digichem/config/README +3 -0
- digichem/config/__init__.py +5 -0
- digichem/config/base.py +321 -0
- digichem/config/locations.py +14 -0
- digichem/config/parse.py +90 -0
- digichem/config/util.py +117 -0
- digichem/data/README +4 -0
- digichem/data/batoms/COPYING +18 -0
- digichem/data/batoms/LICENSE +674 -0
- digichem/data/batoms/README +2 -0
- digichem/data/batoms/__init__.py +0 -0
- digichem/data/batoms/batoms-renderer.py +351 -0
- digichem/data/config/digichem.yaml +714 -0
- digichem/data/functionals.csv +15 -0
- digichem/data/solvents.csv +185 -0
- digichem/data/tachyon/COPYING.md +5 -0
- digichem/data/tachyon/LICENSE +30 -0
- digichem/data/tachyon/tachyon_LINUXAMD64 +0 -0
- digichem/data/vmd/common.tcl +468 -0
- digichem/data/vmd/generate_combined_orbital_images.tcl +70 -0
- digichem/data/vmd/generate_density_images.tcl +45 -0
- digichem/data/vmd/generate_dipole_images.tcl +68 -0
- digichem/data/vmd/generate_orbital_images.tcl +57 -0
- digichem/data/vmd/generate_spin_images.tcl +66 -0
- digichem/data/vmd/generate_structure_images.tcl +40 -0
- digichem/datas.py +14 -0
- digichem/exception/__init__.py +7 -0
- digichem/exception/base.py +133 -0
- digichem/exception/uncatchable.py +63 -0
- digichem/file/__init__.py +1 -0
- digichem/file/base.py +364 -0
- digichem/file/cube.py +284 -0
- digichem/file/fchk.py +94 -0
- digichem/file/prattle.py +277 -0
- digichem/file/types.py +97 -0
- digichem/image/__init__.py +6 -0
- digichem/image/base.py +113 -0
- digichem/image/excited_states.py +335 -0
- digichem/image/graph.py +293 -0
- digichem/image/orbitals.py +239 -0
- digichem/image/render.py +617 -0
- digichem/image/spectroscopy.py +797 -0
- digichem/image/structure.py +115 -0
- digichem/image/vmd.py +826 -0
- digichem/input/__init__.py +3 -0
- digichem/input/base.py +78 -0
- digichem/input/digichem_input.py +500 -0
- digichem/input/gaussian.py +140 -0
- digichem/log.py +179 -0
- digichem/memory.py +166 -0
- digichem/misc/__init__.py +4 -0
- digichem/misc/argparse.py +44 -0
- digichem/misc/base.py +61 -0
- digichem/misc/io.py +239 -0
- digichem/misc/layered_dict.py +285 -0
- digichem/misc/text.py +139 -0
- digichem/misc/time.py +73 -0
- digichem/parse/__init__.py +13 -0
- digichem/parse/base.py +220 -0
- digichem/parse/cclib.py +138 -0
- digichem/parse/dump.py +253 -0
- digichem/parse/gaussian.py +130 -0
- digichem/parse/orca.py +96 -0
- digichem/parse/turbomole.py +201 -0
- digichem/parse/util.py +523 -0
- digichem/result/__init__.py +6 -0
- digichem/result/alignment/AA.py +114 -0
- digichem/result/alignment/AAA.py +61 -0
- digichem/result/alignment/FAP.py +148 -0
- digichem/result/alignment/__init__.py +3 -0
- digichem/result/alignment/base.py +310 -0
- digichem/result/angle.py +153 -0
- digichem/result/atom.py +742 -0
- digichem/result/base.py +258 -0
- digichem/result/dipole_moment.py +332 -0
- digichem/result/emission.py +402 -0
- digichem/result/energy.py +323 -0
- digichem/result/excited_state.py +821 -0
- digichem/result/ground_state.py +94 -0
- digichem/result/metadata.py +644 -0
- digichem/result/multi.py +98 -0
- digichem/result/nmr.py +1086 -0
- digichem/result/orbital.py +647 -0
- digichem/result/result.py +244 -0
- digichem/result/soc.py +272 -0
- digichem/result/spectroscopy.py +514 -0
- digichem/result/tdm.py +267 -0
- digichem/result/vibration.py +167 -0
- digichem/test/__init__.py +6 -0
- digichem/test/conftest.py +4 -0
- digichem/test/test_basis.py +71 -0
- digichem/test/test_calculate.py +30 -0
- digichem/test/test_config.py +78 -0
- digichem/test/test_cube.py +369 -0
- digichem/test/test_exception.py +16 -0
- digichem/test/test_file.py +104 -0
- digichem/test/test_image.py +337 -0
- digichem/test/test_input.py +64 -0
- digichem/test/test_parsing.py +79 -0
- digichem/test/test_prattle.py +36 -0
- digichem/test/test_result.py +489 -0
- digichem/test/test_translate.py +112 -0
- digichem/test/util.py +207 -0
- digichem/translate.py +591 -0
- digichem_core-6.0.0rc1.dist-info/METADATA +96 -0
- digichem_core-6.0.0rc1.dist-info/RECORD +111 -0
- digichem_core-6.0.0rc1.dist-info/WHEEL +4 -0
- digichem_core-6.0.0rc1.dist-info/licenses/COPYING.md +10 -0
- digichem_core-6.0.0rc1.dist-info/licenses/LICENSE +11 -0
digichem/translate.py
ADDED
|
@@ -0,0 +1,591 @@
|
|
|
1
|
+
"""This module contains translation tables between various different, but equivalent, terms expected by different calculation programs."""
|
|
2
|
+
|
|
3
|
+
import csv
|
|
4
|
+
|
|
5
|
+
from configurables.misc import is_int
|
|
6
|
+
|
|
7
|
+
import digichem.log
|
|
8
|
+
from digichem.datas import get_resource
|
|
9
|
+
|
|
10
|
+
# Hidden imports.
|
|
11
|
+
#import basis_set_exchange as bse
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# Load functional conversion table from file.
|
|
15
|
+
functional_db = {}
|
|
16
|
+
with open(get_resource('data/functionals.csv')) as csv_file:
|
|
17
|
+
_reader = csv.reader(csv_file)
|
|
18
|
+
headers = None
|
|
19
|
+
for line_num, line in enumerate(_reader):
|
|
20
|
+
if line_num == 0:
|
|
21
|
+
headers = line
|
|
22
|
+
|
|
23
|
+
else:
|
|
24
|
+
line = [line_part if line_part != "" else None for line_part in line]
|
|
25
|
+
|
|
26
|
+
# The various parts of the definition.
|
|
27
|
+
functional_name = line[0]
|
|
28
|
+
aliases = line[1].split(",") if line[1] is not None else []
|
|
29
|
+
functional = {"name": functional_name, "aliases": aliases, "turbomole": line[2], "gaussian": line[3]}
|
|
30
|
+
|
|
31
|
+
# Get all the names we can refer to this functional by.
|
|
32
|
+
all_names = [functional_name] + aliases
|
|
33
|
+
for program in ("turbomole", "gaussian"):
|
|
34
|
+
# Make sure we don't overwrite existing values with program specific ones.
|
|
35
|
+
if functional[program] is not None and functional[program] not in functional_db:
|
|
36
|
+
all_names.append(functional[program])
|
|
37
|
+
|
|
38
|
+
# Set into the DB, using all possible names.
|
|
39
|
+
for ref_name in all_names:
|
|
40
|
+
functional_db[ref_name] = functional
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# Load solvent conversion table from file.
|
|
44
|
+
solvent_db = {}
|
|
45
|
+
with open(get_resource('data/solvents.csv')) as csv_file:
|
|
46
|
+
_reader = csv.reader(csv_file)
|
|
47
|
+
headers = None
|
|
48
|
+
for line_num, line in enumerate(_reader):
|
|
49
|
+
if line_num == 0:
|
|
50
|
+
headers = line
|
|
51
|
+
|
|
52
|
+
else:
|
|
53
|
+
line = [line_part if line_part != "" else None for line_part in line]
|
|
54
|
+
|
|
55
|
+
# The various parts of the definition.
|
|
56
|
+
solvent = {"name": line[0], "aliases": [line[1]] if line[1] is not None else [], "gaussian": line[2], "epsilon": float(line[3]) if line[3] is not None else None, "refractive": float(line[4]) if line[4] is not None else None}
|
|
57
|
+
|
|
58
|
+
solvent_db[solvent['name']] = solvent
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class Translate():
|
|
62
|
+
"""
|
|
63
|
+
ABC for translate classes.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
def __init__(self, value):
|
|
67
|
+
"""
|
|
68
|
+
Constructor for Translate classes.
|
|
69
|
+
"""
|
|
70
|
+
if isinstance(value, type(self)):
|
|
71
|
+
self.value = value.value
|
|
72
|
+
|
|
73
|
+
else:
|
|
74
|
+
self.value = value
|
|
75
|
+
|
|
76
|
+
def translate(self, program):
|
|
77
|
+
"""
|
|
78
|
+
Translate into a name appropriate for a given program.
|
|
79
|
+
"""
|
|
80
|
+
raise NotImplementedError("Implement in subclass")
|
|
81
|
+
|
|
82
|
+
def to_turbomole(self):
|
|
83
|
+
"""
|
|
84
|
+
Translate into a name appropriate for Turbomole.
|
|
85
|
+
"""
|
|
86
|
+
return self.translate("turbomole")
|
|
87
|
+
|
|
88
|
+
def to_gaussian(self):
|
|
89
|
+
"""
|
|
90
|
+
Translate into a name appropriate for Turbomole.
|
|
91
|
+
"""
|
|
92
|
+
return self.translate("gaussian")
|
|
93
|
+
|
|
94
|
+
def to_orca(self):
|
|
95
|
+
"""
|
|
96
|
+
Translate into a name appropriate for ORCA.
|
|
97
|
+
"""
|
|
98
|
+
return self.translate("orca")
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class Basis_set(Translate):
|
|
102
|
+
"""
|
|
103
|
+
A class for converting between the names of basis sets.
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def basis_set(self):
|
|
108
|
+
return self.value
|
|
109
|
+
|
|
110
|
+
@classmethod
|
|
111
|
+
def build_db(self):
|
|
112
|
+
"""
|
|
113
|
+
Build a table of basis set names.
|
|
114
|
+
|
|
115
|
+
:returns: A dictionary, where each key is the name of a basis set and each value is a BSE metadata dict.
|
|
116
|
+
"""
|
|
117
|
+
import basis_set_exchange as bse
|
|
118
|
+
# Build a database of basis set metadata, adding the display name and alternative names as additional keys for each basis set.
|
|
119
|
+
# BSE's database has some semi-duplicate entries (such as '6-31g(d,p)' and '6-31g_st__st_').
|
|
120
|
+
# Although these basis sets are identical, they have reversed metadata (ie, for '6-31g(d,p)', 'display_name' is set to '6-31G(d,p)' and 'other_names'
|
|
121
|
+
# contains '6-31G**', while for '6-31g_st__st_' 'display_name' is '6-31G**' and 'other_names' is '6-31G(d,p)'.
|
|
122
|
+
# This is a problem for us because we need to predictably choose from these names (ie the star form '6-31G**' is suitable for both
|
|
123
|
+
# Gaussian and Turbomole), but the keys referring to these same names are different.
|
|
124
|
+
#
|
|
125
|
+
# Firstly, build a new dict using basename as the key, which appears to be the same even for duplicates.
|
|
126
|
+
db = {value["basename"]: value for key, value in bse.get_metadata().items()}
|
|
127
|
+
|
|
128
|
+
new_db = {}
|
|
129
|
+
|
|
130
|
+
for basis_key, basis_set_meta in db.items():
|
|
131
|
+
# First, add the normal key.
|
|
132
|
+
basis_set = {
|
|
133
|
+
"key": basis_set_meta['basename'],
|
|
134
|
+
"name": basis_set_meta['display_name'],
|
|
135
|
+
"aliases": basis_set_meta['other_names'],
|
|
136
|
+
"meta": basis_set_meta
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
# Decide on our program specific names.
|
|
140
|
+
basis_set["gaussian"] = basis_set['name']
|
|
141
|
+
basis_set["turbomole"] = basis_set['name']
|
|
142
|
+
|
|
143
|
+
# Turbomole doesn't like pople sets with polarisation function spelled out,
|
|
144
|
+
# it prefers the star format.
|
|
145
|
+
if basis_set['name'][-5:] == "(d,p)":
|
|
146
|
+
basis_set["turbomole"] = basis_set['name'][:-5] + "**"
|
|
147
|
+
|
|
148
|
+
# Gaussian has a strange, contracted style naming scheme for Karlsruhe,
|
|
149
|
+
# and a misleading/incorrect name for def2-SVP(P).
|
|
150
|
+
#print(basis_set['name'])
|
|
151
|
+
if basis_set['name'] == "def2-SV(P)":
|
|
152
|
+
basis_set['gaussian'] = "def2SVPP"
|
|
153
|
+
|
|
154
|
+
elif basis_set['name'][:5] == "def2-":
|
|
155
|
+
basis_set['gaussian'] = basis_set['name'][:4] + basis_set['name'][5:]
|
|
156
|
+
|
|
157
|
+
new_db[basis_key] = basis_set
|
|
158
|
+
|
|
159
|
+
# TODO: Need to support aux basis sets (which end in things like -rifit, -jfit, -jkfit etc.
|
|
160
|
+
# Orca, for example, uses /C, /J and /JK instead...
|
|
161
|
+
# Turbomole uses nothing.
|
|
162
|
+
|
|
163
|
+
# Basis set exchange seems to be missing some non-polarized Karlsruhe basis sets (or else Gaussian made them up).
|
|
164
|
+
# Add them manually so we can still convert from Gaussian's weird representation.
|
|
165
|
+
new_db.update({
|
|
166
|
+
"def2-TZV": {"key": "def2-TZV", "name": "def2-TZV", "aliases": [], "meta": {}, "gaussian": "def2TZV"},#, "turbomole": "def2-TZV"},
|
|
167
|
+
"def2-QZV": {"key": "def2-QZV", "name": "def2-QZV", "aliases": [], "meta": {}, "gaussian": "def2QZV"}#, "turbomole": "def2-TZV"}
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
return new_db
|
|
171
|
+
|
|
172
|
+
@classmethod
|
|
173
|
+
def find_in_db(self, hint):
|
|
174
|
+
"""
|
|
175
|
+
Try and find the entry for a basis set in the basis set exchange.
|
|
176
|
+
|
|
177
|
+
:raises ValueError: If the given hint could not be found.
|
|
178
|
+
:param hint: The name of the basis set to search for.
|
|
179
|
+
:return: A dictionary of BSE metadata.
|
|
180
|
+
"""
|
|
181
|
+
# Get the basis set DB.
|
|
182
|
+
db = self.build_db()
|
|
183
|
+
|
|
184
|
+
# First, try and see if we can just use hint as an exact match.
|
|
185
|
+
try:
|
|
186
|
+
return db[hint]
|
|
187
|
+
|
|
188
|
+
except KeyError:
|
|
189
|
+
pass
|
|
190
|
+
|
|
191
|
+
# Try again, this time ignoring case and looking through all possible names.
|
|
192
|
+
str_hint = "".join(str(hint).lower().split())
|
|
193
|
+
for basis_set in db.values():
|
|
194
|
+
names = [basis_set.get(name, "") for name in ("key", "name", "gaussian", "turbomole")] + basis_set['aliases']
|
|
195
|
+
if str_hint in [name.lower() for name in names if name is not None]:
|
|
196
|
+
return basis_set
|
|
197
|
+
|
|
198
|
+
# No luck.
|
|
199
|
+
raise ValueError("Could not find basis set definition for '{}'".format(hint))
|
|
200
|
+
|
|
201
|
+
def translate(self, program = "name"):
|
|
202
|
+
"""
|
|
203
|
+
Translate the name of this basis set into one appropriate for a calculation program.
|
|
204
|
+
|
|
205
|
+
Basis sets are currently handled the same for all programs.
|
|
206
|
+
"""
|
|
207
|
+
try:
|
|
208
|
+
basis_def = self.find_in_db(self.basis_set)
|
|
209
|
+
return basis_def[program]
|
|
210
|
+
|
|
211
|
+
except ValueError:
|
|
212
|
+
# Just return as is.
|
|
213
|
+
if self.basis_set != "auto":
|
|
214
|
+
digichem.log.get_logger().debug("Could not find basis set with name '{}' in the basis set exchange; using name unmodified".format(self.basis_set))
|
|
215
|
+
return self.basis_set
|
|
216
|
+
|
|
217
|
+
except KeyError:
|
|
218
|
+
return basis_def['name']
|
|
219
|
+
|
|
220
|
+
def __str__(self):
|
|
221
|
+
return str(self.translate())
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
class Functional(Translate):
|
|
225
|
+
"""
|
|
226
|
+
A class for converting between the names of DFT functionals.
|
|
227
|
+
"""
|
|
228
|
+
|
|
229
|
+
@property
|
|
230
|
+
def functional(self):
|
|
231
|
+
return self.value
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
@classmethod
|
|
235
|
+
def find_in_db(self, hint):
|
|
236
|
+
"""
|
|
237
|
+
Try and find an entry for a functional in the internal library.
|
|
238
|
+
|
|
239
|
+
:raises ValueError: If the functional could not be found.
|
|
240
|
+
:param hint: The name of the functional to look for.
|
|
241
|
+
:returns: The corresponding functional dict.
|
|
242
|
+
"""
|
|
243
|
+
# First try using exact name.
|
|
244
|
+
try:
|
|
245
|
+
return functional_db[hint]
|
|
246
|
+
|
|
247
|
+
except KeyError:
|
|
248
|
+
pass
|
|
249
|
+
|
|
250
|
+
# Try again, this time ignoring case.
|
|
251
|
+
functional_names = [functional_name.upper() for functional_name in functional_db]
|
|
252
|
+
|
|
253
|
+
try:
|
|
254
|
+
return functional_db[list(functional_db.keys())[functional_names.index(hint.upper())]]
|
|
255
|
+
|
|
256
|
+
except IndexError:
|
|
257
|
+
pass
|
|
258
|
+
|
|
259
|
+
# No luck.
|
|
260
|
+
raise ValueError("Could not find functional definition for '{}'".format(hint))
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def translate(self, program):
|
|
264
|
+
"""
|
|
265
|
+
Translate this functional name to one recognised by a given program.
|
|
266
|
+
|
|
267
|
+
:param program: The program to translate for.
|
|
268
|
+
"""
|
|
269
|
+
# Try and get a definition for the functional from our db.
|
|
270
|
+
try:
|
|
271
|
+
func_def = self.find_in_db(self.functional)
|
|
272
|
+
|
|
273
|
+
# If there's an explicit value for our program, use that.
|
|
274
|
+
if program in func_def and func_def[program] is not None:
|
|
275
|
+
return func_def[program]
|
|
276
|
+
|
|
277
|
+
# Otherwise, use the main common name.
|
|
278
|
+
return func_def['name']
|
|
279
|
+
|
|
280
|
+
except ValueError:
|
|
281
|
+
pass
|
|
282
|
+
|
|
283
|
+
# No definition found, use as is.
|
|
284
|
+
return self.functional
|
|
285
|
+
|
|
286
|
+
def to_turbomole(self):
|
|
287
|
+
"""
|
|
288
|
+
Translate into a name appropriate for Turbomole.
|
|
289
|
+
"""
|
|
290
|
+
func_name = self.translate("turbomole")
|
|
291
|
+
# All turbomole functionals are lower case, except for the word 'Gaussian'.
|
|
292
|
+
return func_name.lower().replace("gaussian", "Gaussian")
|
|
293
|
+
|
|
294
|
+
def __str__(self):
|
|
295
|
+
return str(self.translate("name"))
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
class Solvent(Translate):
|
|
299
|
+
"""A class for converting between different representations of solvents."""
|
|
300
|
+
|
|
301
|
+
@property
|
|
302
|
+
def solvent(self):
|
|
303
|
+
return self.value
|
|
304
|
+
|
|
305
|
+
@classmethod
|
|
306
|
+
def find_in_db(self, hint):
|
|
307
|
+
"""
|
|
308
|
+
Try and find an entry for a solvent in the internal library.
|
|
309
|
+
|
|
310
|
+
:raises ValueError: If the multiplicity could not be found.
|
|
311
|
+
:param hint: The name, number or symbol of the multiplicity to look for.
|
|
312
|
+
:returns: The corresponding multiplicity dict.
|
|
313
|
+
"""
|
|
314
|
+
# First, just try to lookup exactly.
|
|
315
|
+
try:
|
|
316
|
+
return solvent_db[hint]
|
|
317
|
+
|
|
318
|
+
except KeyError:
|
|
319
|
+
# No luck, look through for a match.
|
|
320
|
+
str_hint = "".join(str(hint).lower().split())
|
|
321
|
+
for solvent in solvent_db.values():
|
|
322
|
+
if hint == solvent['epsilon'] or str_hint in [name.lower() for name in solvent['aliases'] + [solvent['name']] + [solvent['gaussian']] if name is not None]:
|
|
323
|
+
return solvent
|
|
324
|
+
|
|
325
|
+
# No luck.
|
|
326
|
+
raise ValueError("Could not find solvent definition for '{}'".format(hint))
|
|
327
|
+
|
|
328
|
+
@classmethod
|
|
329
|
+
def epsilon_to_name(self, epsilon, threshold = 0.0001):
|
|
330
|
+
"""
|
|
331
|
+
Find the name of a solvent based on its epsilon value.
|
|
332
|
+
"""
|
|
333
|
+
close = {}
|
|
334
|
+
for solvent_def in solvent_db.values():
|
|
335
|
+
if solvent_def['epsilon'] == epsilon:
|
|
336
|
+
# Exact match, stop.
|
|
337
|
+
return solvent_def['name']
|
|
338
|
+
|
|
339
|
+
elif abs(solvent_def['epsilon'] - epsilon) < threshold:
|
|
340
|
+
# Close match.
|
|
341
|
+
close[abs(solvent_def['epsilon'] - epsilon)] = solvent_def['name']
|
|
342
|
+
|
|
343
|
+
# Sort values that were close based on how close they were.
|
|
344
|
+
close_names = [close[closeness] for closeness in sorted(close)]
|
|
345
|
+
|
|
346
|
+
try:
|
|
347
|
+
return close_names[0]
|
|
348
|
+
|
|
349
|
+
except IndexError:
|
|
350
|
+
raise ValueError("No solvent definitions within {} of {}".format(threshold, epsilon)) from None
|
|
351
|
+
|
|
352
|
+
@property
|
|
353
|
+
def epsilon(self):
|
|
354
|
+
return self.find_in_db(self.solvent)['epsilon']
|
|
355
|
+
|
|
356
|
+
@property
|
|
357
|
+
def refractive_index(self):
|
|
358
|
+
return self.find_in_db(self.solvent)['refractive']
|
|
359
|
+
|
|
360
|
+
def translate(self, program):
|
|
361
|
+
"""
|
|
362
|
+
Translate this solvent name to one recognised by a given program.
|
|
363
|
+
|
|
364
|
+
:param program: The program to translate for.
|
|
365
|
+
"""
|
|
366
|
+
if program == "turbomole":
|
|
367
|
+
program = "epsilon"
|
|
368
|
+
|
|
369
|
+
elif program == "orca":
|
|
370
|
+
program = "name"
|
|
371
|
+
|
|
372
|
+
try:
|
|
373
|
+
solvent_def = self.find_in_db(self.solvent)
|
|
374
|
+
|
|
375
|
+
if program in solvent_def and solvent_def[program] is not None:
|
|
376
|
+
return solvent_def[program]
|
|
377
|
+
|
|
378
|
+
else:
|
|
379
|
+
return solvent_def["name"]
|
|
380
|
+
|
|
381
|
+
except ValueError:
|
|
382
|
+
return self.solvent
|
|
383
|
+
|
|
384
|
+
def __str__(self):
|
|
385
|
+
return str(self.translate("name"))
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
class SCF_convergence(Translate):
|
|
389
|
+
"""
|
|
390
|
+
A class for converting between different shortcuts for SCF convergence thresholds.
|
|
391
|
+
"""
|
|
392
|
+
|
|
393
|
+
table = [
|
|
394
|
+
{"name": "Loose", "energy": -5, "density": -3, "orca": "LooseSCF"}, # Note energy stops changing here.
|
|
395
|
+
{"name": "Weak", "energy": -5, "density": -4, "orca": "SloppySCF"},
|
|
396
|
+
{"name": "Medium", "energy": -6, "density": -5, "orca": "MediumSCF"},
|
|
397
|
+
{"name": "Strong", "energy": -7, "density": -6, "orca": "StrongSCF"},
|
|
398
|
+
{"name": "Tight", "energy": -8, "density": -7, "orca": "TightSCF"}, # Typically a sensible default.
|
|
399
|
+
{"name": "VTight", "energy": -9, "density": -8, "orca": "VeryTightSCF"},
|
|
400
|
+
{"name": "VVTight", "energy": -10, "density": -9, "orca": "VeryVeryTightSCF"},
|
|
401
|
+
{"name": "Extreme", "energy": -14, "density": -14, "orca": "ExtremeSCF"},
|
|
402
|
+
]
|
|
403
|
+
|
|
404
|
+
@classmethod
|
|
405
|
+
def names(self):
|
|
406
|
+
return [row['name'] for row in self.table]
|
|
407
|
+
|
|
408
|
+
@classmethod
|
|
409
|
+
def choices(self):
|
|
410
|
+
return [self(row['name']) for row in self.table]
|
|
411
|
+
|
|
412
|
+
@classmethod
|
|
413
|
+
def find_in_db(self, hint):
|
|
414
|
+
"""
|
|
415
|
+
Try and find an entry in the internal library.
|
|
416
|
+
|
|
417
|
+
:raises ValueError: If the value could not be found.
|
|
418
|
+
:param hint: The name to look for
|
|
419
|
+
:returns: The corresponding value dict.
|
|
420
|
+
"""
|
|
421
|
+
for row in self.table:
|
|
422
|
+
name, energy, density, orca = row.values()
|
|
423
|
+
|
|
424
|
+
if str(hint).upper() == name.upper() or str(hint).upper() == orca.upper():
|
|
425
|
+
return row
|
|
426
|
+
|
|
427
|
+
if str(hint) == str(energy) or str(hint).upper() == "ENERGY{}".format(energy) or str(hint).upper() == "DENSITY{}".format(density):
|
|
428
|
+
return row
|
|
429
|
+
|
|
430
|
+
# No luck.
|
|
431
|
+
raise ValueError("Could not find convergence definition for '{}'".format(hint))
|
|
432
|
+
|
|
433
|
+
def translate(self, to_type):
|
|
434
|
+
"""
|
|
435
|
+
Translate into a name appropriate for a given program.
|
|
436
|
+
"""
|
|
437
|
+
try:
|
|
438
|
+
return self.find_in_db(self.value)[to_type]
|
|
439
|
+
|
|
440
|
+
except ValueError:
|
|
441
|
+
# Couldn't find in conversion table.
|
|
442
|
+
return self.value
|
|
443
|
+
|
|
444
|
+
def __str__(self):
|
|
445
|
+
return str(self.translate("name"))
|
|
446
|
+
|
|
447
|
+
def __eq__(self, other):
|
|
448
|
+
return str(self) == str(other)
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
class Cube_grid_points(Translate):
|
|
452
|
+
"""A class for converting between cube grid sizes."""
|
|
453
|
+
|
|
454
|
+
table = [
|
|
455
|
+
{"name": "Default", "points": 100, "gaussian": 0, "turbomole": 100, "orca": 100}, # Default depends on the calc program. Gaussian uses a special value of 'zero' to select a default algorithm.
|
|
456
|
+
{"name": "Tiny", "points": 25},
|
|
457
|
+
{"name": "Small", "points": 50},
|
|
458
|
+
{"name": "Medium", "points": 100},
|
|
459
|
+
{"name": "Large", "points": 200},
|
|
460
|
+
{"name": "Huge", "points": 500},
|
|
461
|
+
|
|
462
|
+
]
|
|
463
|
+
|
|
464
|
+
@classmethod
|
|
465
|
+
def find_in_db(self, hint):
|
|
466
|
+
"""
|
|
467
|
+
Try and find an entry in the internal library.
|
|
468
|
+
|
|
469
|
+
:raises ValueError: If the value could not be found.
|
|
470
|
+
:param hint: The name to look for
|
|
471
|
+
:returns: The corresponding value dict.
|
|
472
|
+
"""
|
|
473
|
+
for row in self.table:
|
|
474
|
+
if str(hint).upper() == row['name'].upper():
|
|
475
|
+
return row
|
|
476
|
+
|
|
477
|
+
# No luck.
|
|
478
|
+
raise ValueError("Could not find grid point definition for '{}'".format(hint))
|
|
479
|
+
|
|
480
|
+
def translate(self, to_type):
|
|
481
|
+
"""
|
|
482
|
+
Translate into a name appropriate for a given program.
|
|
483
|
+
"""
|
|
484
|
+
try:
|
|
485
|
+
grid_def = self.find_in_db(self.value)
|
|
486
|
+
return grid_def[to_type]
|
|
487
|
+
|
|
488
|
+
except KeyError:
|
|
489
|
+
# Just return the equivalent number of points.
|
|
490
|
+
return grid_def['points']
|
|
491
|
+
|
|
492
|
+
except ValueError:
|
|
493
|
+
# Couldn't find in conversion table.
|
|
494
|
+
return self.value
|
|
495
|
+
|
|
496
|
+
def __str__(self):
|
|
497
|
+
return str(self.translate("name"))
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
# TODO: Use this.
|
|
501
|
+
class Multiplicity(Translate):
|
|
502
|
+
"""A class for converting between different representations of multiplicity."""
|
|
503
|
+
|
|
504
|
+
table = [
|
|
505
|
+
{"name": "no multiplicity", "number": 0, "symbol": "?"},
|
|
506
|
+
{"name": "Singlet", "number": 1, "symbol": "S"},
|
|
507
|
+
{"name": "Doublet", "number": 2, "symbol": "D"},
|
|
508
|
+
{"name": "Triplet", "number": 3, "symbol": "T"},
|
|
509
|
+
{"name": "Quartet", "number": 4, "symbol": "Q"}
|
|
510
|
+
]
|
|
511
|
+
|
|
512
|
+
@property
|
|
513
|
+
def multiplicity(self):
|
|
514
|
+
return self.value
|
|
515
|
+
|
|
516
|
+
@classmethod
|
|
517
|
+
def find_in_db(self, hint):
|
|
518
|
+
"""
|
|
519
|
+
Try and find an entry for a multiplicity in the internal library.
|
|
520
|
+
|
|
521
|
+
:raises ValueError: If the multiplicity could not be found.
|
|
522
|
+
:param hint: The name, number or symbol of the multiplicity to look for.
|
|
523
|
+
:returns: The corresponding multiplicity dict.
|
|
524
|
+
"""
|
|
525
|
+
for row in self.table:
|
|
526
|
+
name, number, symbol = row.values()
|
|
527
|
+
if hint == number or str(hint).upper() in (str(number), name.upper(), symbol.upper()):
|
|
528
|
+
return row
|
|
529
|
+
|
|
530
|
+
# No luck.
|
|
531
|
+
raise ValueError("Could not find multiplicity definition for '{}'".format(hint))
|
|
532
|
+
|
|
533
|
+
@property
|
|
534
|
+
def symbol(self):
|
|
535
|
+
"""
|
|
536
|
+
Get this multiplicity as a symbol.
|
|
537
|
+
"""
|
|
538
|
+
# Get a shorthand symbol if we can.
|
|
539
|
+
try:
|
|
540
|
+
return self.find_in_db(self.multiplicity)['symbol']
|
|
541
|
+
|
|
542
|
+
except ValueError:
|
|
543
|
+
if self.multiplicity % 1 == 0:
|
|
544
|
+
# Multiplicity is an integer, so return as a stringy whole number.
|
|
545
|
+
return str(int(self.multiplicity))
|
|
546
|
+
else:
|
|
547
|
+
return str(self.multiplicity)
|
|
548
|
+
|
|
549
|
+
@property
|
|
550
|
+
def string(self):
|
|
551
|
+
"""
|
|
552
|
+
Get this multiplicity as a string.
|
|
553
|
+
"""
|
|
554
|
+
return self.translate("name")
|
|
555
|
+
|
|
556
|
+
def __str__(self):
|
|
557
|
+
return self.string
|
|
558
|
+
|
|
559
|
+
@property
|
|
560
|
+
def number(self):
|
|
561
|
+
"""
|
|
562
|
+
Get this multiplicity as a number.
|
|
563
|
+
"""
|
|
564
|
+
try:
|
|
565
|
+
return self.find_in_db(self.multiplicity)['number']
|
|
566
|
+
|
|
567
|
+
except ValueError:
|
|
568
|
+
# No pre-defined number, see if it is an int.
|
|
569
|
+
if is_int(self.multiplicity):
|
|
570
|
+
return int(self.multiplicity)
|
|
571
|
+
|
|
572
|
+
else:
|
|
573
|
+
return float(self.multiplicity)
|
|
574
|
+
|
|
575
|
+
def translate(self, to_type):
|
|
576
|
+
"""
|
|
577
|
+
Translate into a name appropriate for a given program.
|
|
578
|
+
"""
|
|
579
|
+
if to_type == "gaussian":
|
|
580
|
+
to_type = "name"
|
|
581
|
+
|
|
582
|
+
elif to_type == "turbomole":
|
|
583
|
+
to_type = "number"
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
try:
|
|
587
|
+
return self.find_in_db(self.multiplicity)[to_type]
|
|
588
|
+
|
|
589
|
+
except ValueError:
|
|
590
|
+
# Couldn't find in conversion table.
|
|
591
|
+
return self.multiplicity
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: digichem-core
|
|
3
|
+
Version: 6.0.0rc1
|
|
4
|
+
Summary: Open-source library for Digichem core components
|
|
5
|
+
Project-URL: Homepage, https://github.com/Digichem-Project/digichem-core
|
|
6
|
+
Project-URL: Documentation, https://doc.digi-chem.co.uk
|
|
7
|
+
Project-URL: Issues, https://github.com/Digichem-Project/digichem-core/issues
|
|
8
|
+
Author-email: "Oliver S. Lee" <osl@digi-chem.ac.uk>
|
|
9
|
+
License: Copyright 2024 Digichem
|
|
10
|
+
|
|
11
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
|
12
|
+
|
|
13
|
+
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
|
14
|
+
|
|
15
|
+
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
|
16
|
+
|
|
17
|
+
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
20
|
+
License-File: COPYING.md
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
23
|
+
Classifier: Operating System :: Unix
|
|
24
|
+
Classifier: Programming Language :: Python :: 3
|
|
25
|
+
Requires-Python: >=3.9
|
|
26
|
+
Requires-Dist: adjusttext
|
|
27
|
+
Requires-Dist: basis-set-exchange
|
|
28
|
+
Requires-Dist: cclib
|
|
29
|
+
Requires-Dist: colour-science
|
|
30
|
+
Requires-Dist: configurables
|
|
31
|
+
Requires-Dist: deepmerge
|
|
32
|
+
Requires-Dist: dill
|
|
33
|
+
Requires-Dist: matplotlib
|
|
34
|
+
Requires-Dist: openprattle
|
|
35
|
+
Requires-Dist: periodictable
|
|
36
|
+
Requires-Dist: pillow
|
|
37
|
+
Requires-Dist: pyyaml
|
|
38
|
+
Requires-Dist: rdkit
|
|
39
|
+
Requires-Dist: scipy
|
|
40
|
+
Provides-Extra: test
|
|
41
|
+
Requires-Dist: pytest; extra == 'test'
|
|
42
|
+
Requires-Dist: pytest-lazy-fixture; extra == 'test'
|
|
43
|
+
Description-Content-Type: text/markdown
|
|
44
|
+
|
|
45
|
+
<img src="Banner.png" alt="Banner" />
|
|
46
|
+
|
|
47
|
+
# Digichem-core
|
|
48
|
+
|
|
49
|
+
Welcome to Digichem: the computational chemistry management suite!
|
|
50
|
+
|
|
51
|
+
This is Digichem-core, the open-source library for Digichem. If you are looking to build your own computational workflows using the tools that Digichem has to offer, then you have come to the right place.
|
|
52
|
+
|
|
53
|
+
- Alternatively, if you are looking for the full Digichem program, try [Build-boy](https://github.com/Digichem-Project/build-boy)
|
|
54
|
+
- If you'd like more information on the Digichem project, check out the [website](https://www.digi-chem.co.uk)
|
|
55
|
+
|
|
56
|
+
## Dependencies
|
|
57
|
+
|
|
58
|
+
- adjustText
|
|
59
|
+
- basis_set_exchange
|
|
60
|
+
- cclib
|
|
61
|
+
- colour-science
|
|
62
|
+
- deepmerge
|
|
63
|
+
- dill
|
|
64
|
+
- matplotlib
|
|
65
|
+
- openprattle
|
|
66
|
+
- periodictable
|
|
67
|
+
- pillow
|
|
68
|
+
- pyyaml
|
|
69
|
+
- rdkit
|
|
70
|
+
- scipy
|
|
71
|
+
|
|
72
|
+
## Installation
|
|
73
|
+
|
|
74
|
+
### Pip
|
|
75
|
+
|
|
76
|
+
Digichem-core can be installed using pip. Simply run the following command:
|
|
77
|
+
|
|
78
|
+
```Shell
|
|
79
|
+
pip install digichem-core
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Or, on some platforms:
|
|
83
|
+
|
|
84
|
+
```Shell
|
|
85
|
+
pip3 install digichem-core
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Usage
|
|
89
|
+
|
|
90
|
+
Documentation coming soon.
|
|
91
|
+
|
|
92
|
+
## License
|
|
93
|
+
|
|
94
|
+
Digichem-core is licensed under the BSD-3-Clause license, but some files are licensed separately. See [COPYING.md](COPYING.md) for full details.
|
|
95
|
+
|
|
96
|
+
The Digichem logo and branding is Copyright Digichem 2024, you may not use them in any way (although you are welcome to look at them).
|