webmo 1.3.2__tar.gz → 1.4.1__tar.gz
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.
- {webmo-1.3.2/webmo.egg-info → webmo-1.4.1}/PKG-INFO +3 -2
- {webmo-1.3.2 → webmo-1.4.1}/setup.py +2 -1
- {webmo-1.3.2 → webmo-1.4.1}/webmo/__init__.py +1 -1
- {webmo-1.3.2 → webmo-1.4.1}/webmo/gui.py +12 -0
- {webmo-1.3.2 → webmo-1.4.1}/webmo/spectrum.py +7 -4
- webmo-1.4.1/webmo/util.py +287 -0
- {webmo-1.3.2 → webmo-1.4.1}/webmo/webmo_rest.py +13 -24
- {webmo-1.3.2 → webmo-1.4.1/webmo.egg-info}/PKG-INFO +3 -2
- {webmo-1.3.2 → webmo-1.4.1}/webmo.egg-info/requires.txt +6 -0
- webmo-1.3.2/webmo/util.py +0 -25
- {webmo-1.3.2 → webmo-1.4.1}/LICENSE +0 -0
- {webmo-1.3.2 → webmo-1.4.1}/README.md +0 -0
- {webmo-1.3.2 → webmo-1.4.1}/setup.cfg +0 -0
- {webmo-1.3.2 → webmo-1.4.1}/webmo.egg-info/SOURCES.txt +0 -0
- {webmo-1.3.2 → webmo-1.4.1}/webmo.egg-info/dependency_links.txt +0 -0
- {webmo-1.3.2 → webmo-1.4.1}/webmo.egg-info/top_level.txt +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: webmo
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.1
|
|
4
4
|
Summary: A Python-based interface to WebMO
|
|
5
5
|
Home-page: https://github.com/jrschmidt2/webmo-python
|
|
6
|
-
Download-URL: https://github.com/jrschmidt2/webmo-python/archive/refs/tags/1.
|
|
6
|
+
Download-URL: https://github.com/jrschmidt2/webmo-python/archive/refs/tags/1.4.1.tar.gz
|
|
7
7
|
Author: J.R. Schmidt
|
|
8
8
|
Author-email: schmidt@webmo.net
|
|
9
9
|
License: MIT
|
|
@@ -11,4 +11,5 @@ Keywords: WEBMO,REST,API
|
|
|
11
11
|
Provides-Extra: spectrum
|
|
12
12
|
Provides-Extra: util
|
|
13
13
|
Provides-Extra: gui
|
|
14
|
+
Provides-Extra: all
|
|
14
15
|
License-File: LICENSE
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
from .webmo_rest import WebMOREST
|
|
2
|
-
__version__ = "1.
|
|
2
|
+
__version__ = "1.4.1"
|
|
@@ -50,6 +50,8 @@ class JupyterGUI(_WebMOGUIBase):
|
|
|
50
50
|
#fail silently, since this should never happen in Jupyter
|
|
51
51
|
pass #print('ipywidgets not found; disabling support for JupyterGUI')
|
|
52
52
|
|
|
53
|
+
import time
|
|
54
|
+
|
|
53
55
|
def __init__(self, template, query_vars, additional_vars=None):
|
|
54
56
|
"""Constructor for JupyterGUI
|
|
55
57
|
|
|
@@ -66,6 +68,7 @@ class JupyterGUI(_WebMOGUIBase):
|
|
|
66
68
|
|
|
67
69
|
_WebMOGUIBase.__init__(self, template, query_vars, additional_vars)
|
|
68
70
|
self._widgets = []
|
|
71
|
+
self._display_time = -1
|
|
69
72
|
|
|
70
73
|
for var in query_vars:
|
|
71
74
|
if template['variables'][var]['type'] == 'checkbox':
|
|
@@ -93,11 +96,20 @@ class JupyterGUI(_WebMOGUIBase):
|
|
|
93
96
|
|
|
94
97
|
def display(self):
|
|
95
98
|
|
|
99
|
+
self._display_time = self.time.time()
|
|
100
|
+
|
|
96
101
|
for widget in self._widgets:
|
|
97
102
|
display(widget)
|
|
98
103
|
|
|
99
104
|
def get_variables(self):
|
|
100
105
|
|
|
106
|
+
MIN_INPUT_TIME = 1.0
|
|
107
|
+
# sanity check to make sure user has had time to input something
|
|
108
|
+
# fail this call during re-execution of entire notebook at once
|
|
109
|
+
elapsed_time = self.time.time() - self._display_time
|
|
110
|
+
if self._display_time == -1 or elapsed_time < MIN_INPUT_TIME:
|
|
111
|
+
raise RuntimeError('Must provide JupyterGUI input before calling get_variables')
|
|
112
|
+
|
|
101
113
|
# add in additional user-specified variables
|
|
102
114
|
if (self._additional_vars != None):
|
|
103
115
|
variables = {**self._additional_vars}
|
|
@@ -330,8 +330,10 @@ def voigt_line(center, intensity, width=10, q=0.5, start=0, stop=4000, step=1):
|
|
|
330
330
|
Returns:
|
|
331
331
|
(x,y): a tuple of the x and y numpy arrays of the specified line
|
|
332
332
|
"""
|
|
333
|
-
if (
|
|
334
|
-
|
|
333
|
+
if (q <= 0):
|
|
334
|
+
return(gauss_line(center, intensity, width, start, stop, step))
|
|
335
|
+
elif (q >= 1):
|
|
336
|
+
return(lorentz_line(center,intensity,width,start,stop,step))
|
|
335
337
|
|
|
336
338
|
x = np.arange(start=start, stop=stop, step=step)
|
|
337
339
|
vfunc = np.vectorize(_arb_voigt(center, intensity, q, width))
|
|
@@ -403,7 +405,7 @@ def lorentz_line(center, intensity, width=10, start=0, stop=4000, step=1):
|
|
|
403
405
|
vfunc = np.vectorize(_arb_lorentz(center, intensity, width))
|
|
404
406
|
return((x,vfunc(x)))
|
|
405
407
|
|
|
406
|
-
def construct_spectrum(points, intensities, lineshape="gauss", width=10, start=0, stop=4000, step=1):
|
|
408
|
+
def construct_spectrum(points, intensities, lineshape="gauss", frac_loren=0.5, width=10, start=0, stop=4000, step=1):
|
|
407
409
|
"""Construct an entire spectrum plot of data
|
|
408
410
|
|
|
409
411
|
This function returns a pair of numpy arrays that make a spectrum; an x axis
|
|
@@ -415,6 +417,7 @@ def construct_spectrum(points, intensities, lineshape="gauss", width=10, start=0
|
|
|
415
417
|
intensities([float]): a list of corresponding peak intensities - the order must
|
|
416
418
|
match `points`
|
|
417
419
|
lineshape(string,optional): the desired lineshape. The options are "gauss", "lorentz", and "voigt"
|
|
420
|
+
frac_loren(float,optional): the fraction lorentzian of the voigt lineshape, if specified
|
|
418
421
|
width(float,optional): the full width half max of each peak
|
|
419
422
|
start(float,optional): the starting point of the returned array
|
|
420
423
|
stop(float,optional): the ending point of the returned array
|
|
@@ -428,7 +431,7 @@ def construct_spectrum(points, intensities, lineshape="gauss", width=10, start=0
|
|
|
428
431
|
linefuncs = {
|
|
429
432
|
"gauss": gauss_line,
|
|
430
433
|
"lorentz": lorentz_line,
|
|
431
|
-
"voigt": voigt_line
|
|
434
|
+
"voigt": lambda x, y, **kwargs: voigt_line(x,y,q=frac_loren, **kwargs)
|
|
432
435
|
}
|
|
433
436
|
if (lineshape not in linefuncs.keys()):
|
|
434
437
|
linefunc = gauss_line
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
def xyz_from_name(name):
|
|
4
|
+
"""Obtains an XYZ-formatted geometry for the molecule name via PubChem lookup.
|
|
5
|
+
|
|
6
|
+
Arguments:
|
|
7
|
+
name(str): The name of the molecule.
|
|
8
|
+
|
|
9
|
+
Returns:
|
|
10
|
+
A string containing the molecule's XYZ-formatted geometry.
|
|
11
|
+
"""
|
|
12
|
+
import pubchempy as pcp
|
|
13
|
+
|
|
14
|
+
results = pcp.get_compounds(name, 'name', record_type='3d')
|
|
15
|
+
if (len(results) == 0):
|
|
16
|
+
raise ValueError("Specified molecule could not be found in PubChem")
|
|
17
|
+
|
|
18
|
+
record = results[0]
|
|
19
|
+
geom = ""
|
|
20
|
+
for atom in record.atoms:
|
|
21
|
+
element = atom.element
|
|
22
|
+
x = float(atom.x)
|
|
23
|
+
y = float(atom.y)
|
|
24
|
+
z = float(atom.z)
|
|
25
|
+
geom += f"{element}\t{x:f}\t{y:f}\t{z:f}\n"
|
|
26
|
+
|
|
27
|
+
return geom
|
|
28
|
+
|
|
29
|
+
def get_energy(results, include_units=False):
|
|
30
|
+
"""Returns the molecular energy from the job results. This should correspond to the ground state energy
|
|
31
|
+
at molecule at the requested level of theory.
|
|
32
|
+
|
|
33
|
+
Requires WebMO 25 or higher.
|
|
34
|
+
|
|
35
|
+
Arguments:
|
|
36
|
+
dict(results): A dictionary of job results, obtained from get_job_results.
|
|
37
|
+
bool(include_units): Whether to include the units with the returned energy (e.g. 1.5 vs. "1.5 Hartree")
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
The molecular energy.
|
|
41
|
+
"""
|
|
42
|
+
properties = results['properties']
|
|
43
|
+
|
|
44
|
+
if not 'method_energy_name' in properties.keys():
|
|
45
|
+
raise RuntimeError('Cannot locate "method_energy_name" in results')
|
|
46
|
+
method_energy_name = properties['method_energy_name'].lower().replace(' ', '_') + '_energy'
|
|
47
|
+
|
|
48
|
+
found = False
|
|
49
|
+
#also check related key names, since Gaussian prepends some things occasionally
|
|
50
|
+
for key in [method_energy_name, 'r' + method_energy_name, 'u' + method_energy_name, 'ro' + method_energy_name]:
|
|
51
|
+
if key in properties:
|
|
52
|
+
energy = properties[key]['value']
|
|
53
|
+
units = properties[key]['units']
|
|
54
|
+
found = True
|
|
55
|
+
|
|
56
|
+
if not found:
|
|
57
|
+
raise RuntimeError('Could not locate "' + method_energy_name + '" in results')
|
|
58
|
+
|
|
59
|
+
if include_units:
|
|
60
|
+
energy = f"{energy} {units}"
|
|
61
|
+
|
|
62
|
+
return energy
|
|
63
|
+
|
|
64
|
+
def get_energies(results, include_units=False):
|
|
65
|
+
"""Returns a dictionary of available energies from the job results.
|
|
66
|
+
|
|
67
|
+
Arguments:
|
|
68
|
+
dict(results): A dictionary of job results, obtained from get_job_results.
|
|
69
|
+
bool(include_units): Whether to include the units with the returned energy (e.g. 1.5 vs. "1.5 Hartree")
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
A dictionary of available energies.
|
|
73
|
+
"""
|
|
74
|
+
properties = results['properties']
|
|
75
|
+
energies = {}
|
|
76
|
+
|
|
77
|
+
for key in properties.keys():
|
|
78
|
+
index = key.find('_energy')
|
|
79
|
+
|
|
80
|
+
if key.endswith('_energy'):
|
|
81
|
+
index = key.find('_energy')
|
|
82
|
+
energy_type = key[:index]
|
|
83
|
+
|
|
84
|
+
if include_units:
|
|
85
|
+
energies[energy_type] = f"{properties[key]['value']} {properties[key]['units']}"
|
|
86
|
+
else:
|
|
87
|
+
energies[energy_type] = properties[key]['value']
|
|
88
|
+
|
|
89
|
+
return energies
|
|
90
|
+
|
|
91
|
+
def get_geometry(results):
|
|
92
|
+
"""Returns the XYZ-formatted final geometry from the job results.
|
|
93
|
+
|
|
94
|
+
Arguments:
|
|
95
|
+
dict(results): A dictionary of job results, obtained from get_job_results.
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
The XYZ-formatted final geometry as a string.
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
geom = ""
|
|
103
|
+
index = 0
|
|
104
|
+
for element in results['symbols']:
|
|
105
|
+
|
|
106
|
+
x = results['geometry'][index]
|
|
107
|
+
y = results['geometry'][index + 1]
|
|
108
|
+
z = results['geometry'][index + 2]
|
|
109
|
+
|
|
110
|
+
geom += f"{element}\t{x:f}\t{y:f}\t{z:f}\n"
|
|
111
|
+
index += 3
|
|
112
|
+
|
|
113
|
+
return geom
|
|
114
|
+
|
|
115
|
+
def get_stoichiometry(results):
|
|
116
|
+
"""Returns the stoichiometry from the job results.
|
|
117
|
+
|
|
118
|
+
Arguments:
|
|
119
|
+
dict(results): A dictionary of job results, obtained from get_job_results.
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
The stoichiometry as a string.
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
properties = results['properties']
|
|
126
|
+
return properties['stoichiometry']
|
|
127
|
+
|
|
128
|
+
def get_dipole_moment(results):
|
|
129
|
+
"""Returns the dipole moment from the job results.
|
|
130
|
+
|
|
131
|
+
Arguments:
|
|
132
|
+
dict(results): A dictionary of job results, obtained from get_job_results.
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
The magnitude of the dipole moment.
|
|
136
|
+
"""
|
|
137
|
+
|
|
138
|
+
from math import sqrt
|
|
139
|
+
|
|
140
|
+
properties = results['properties']
|
|
141
|
+
dipole = properties['dipole_moment']
|
|
142
|
+
mag = sqrt(dipole[0]**2 + dipole[1]**2 + dipole[2]**2)
|
|
143
|
+
|
|
144
|
+
return mag
|
|
145
|
+
|
|
146
|
+
def get_property(results, property_name):
|
|
147
|
+
"""Returns an arbitrary calculated property from the job results.
|
|
148
|
+
|
|
149
|
+
Arguments:
|
|
150
|
+
dict(results): A dictionary of job results, obtained from get_job_results.
|
|
151
|
+
str(property_name): The name of the desired property
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
The value of the calculated property (scalar or dictionary).
|
|
155
|
+
"""
|
|
156
|
+
|
|
157
|
+
properties = results['properties']
|
|
158
|
+
return properties[property_name]
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def get_bond_length(results, index1, index2):
|
|
162
|
+
"""Returns the specified bond length between two atoms from the job results OR an xyz-formatted string.
|
|
163
|
+
|
|
164
|
+
Arguments:
|
|
165
|
+
dict(results): A dictionary of job results obtained from get_job_results OR an xyz-formatted string.
|
|
166
|
+
index1(int): The one-based index of the first atom of the bonded pair
|
|
167
|
+
index2(int): The one-based index of the second atom of the bonded pair
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
The calculated bond length.
|
|
171
|
+
"""
|
|
172
|
+
|
|
173
|
+
if not isinstance(results, dict):
|
|
174
|
+
geom = _xyz_to_array(results)
|
|
175
|
+
else:
|
|
176
|
+
geom = results['geometry']
|
|
177
|
+
|
|
178
|
+
#convert to 0 index
|
|
179
|
+
index1 -= 1
|
|
180
|
+
index2 -= 1
|
|
181
|
+
|
|
182
|
+
atom1 = np.array(geom[index1*3:index1*3 + 3])
|
|
183
|
+
atom2 = np.array(geom[index2*3:index2*3 + 3])
|
|
184
|
+
|
|
185
|
+
return np.linalg.norm(atom1 - atom2)
|
|
186
|
+
|
|
187
|
+
def get_bond_angle(results, index1, index2, index3):
|
|
188
|
+
"""Returns the specified bond angle between three atoms from the job results OR an xyz-formatted string.
|
|
189
|
+
|
|
190
|
+
Arguments:
|
|
191
|
+
dict(results): A dictionary of job results obtained from get_job_results OR an xyz-formatted string.
|
|
192
|
+
index1(int): The one-based index of the first atom
|
|
193
|
+
index2(int): The one-based index of the second atom
|
|
194
|
+
index3(int): The one-based index of the third atom
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
The calculated bond angle.
|
|
198
|
+
"""
|
|
199
|
+
|
|
200
|
+
if not isinstance(results, dict):
|
|
201
|
+
geom = _xyz_to_array(results)
|
|
202
|
+
else:
|
|
203
|
+
geom = results['geometry']
|
|
204
|
+
|
|
205
|
+
#convert to 0 index
|
|
206
|
+
index1 -= 1
|
|
207
|
+
index2 -= 1
|
|
208
|
+
index3 -= 1
|
|
209
|
+
|
|
210
|
+
atom1 = np.array(geom[index1*3:index1*3 + 3])
|
|
211
|
+
atom2 = np.array(geom[index2*3:index2*3 + 3])
|
|
212
|
+
atom3 = np.array(geom[index3*3:index3*3 + 3])
|
|
213
|
+
|
|
214
|
+
vec1 = atom1 - atom2
|
|
215
|
+
vec2 = atom3 - atom2
|
|
216
|
+
|
|
217
|
+
vec1 /= np.linalg.norm(vec1)
|
|
218
|
+
vec2 /= np.linalg.norm(vec2)
|
|
219
|
+
|
|
220
|
+
return np.arccos(np.dot(vec1,vec2))*180./np.pi
|
|
221
|
+
|
|
222
|
+
def get_dihedral_angle(results, index1, index2, index3, index4):
|
|
223
|
+
"""Returns the specified dihedral angle between four atoms from the job results OR an xyz-formatted string.
|
|
224
|
+
|
|
225
|
+
Arguments:
|
|
226
|
+
dict(results): A dictionary of job results obtained from get_job_results OR an xyz-formatted string.
|
|
227
|
+
index1(int): The one-based index of the first atom
|
|
228
|
+
index2(int): The one-based index of the second atom
|
|
229
|
+
index3(int): The one-based index of the third atom
|
|
230
|
+
index4(int): The one-based index of the fourth atom
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
The calculated dihedral angle.
|
|
234
|
+
"""
|
|
235
|
+
|
|
236
|
+
if not isinstance(results, dict):
|
|
237
|
+
geom = _xyz_to_array(results)
|
|
238
|
+
else:
|
|
239
|
+
geom = results['geometry']
|
|
240
|
+
|
|
241
|
+
#convert to 0 index
|
|
242
|
+
index1 -= 1
|
|
243
|
+
index2 -= 1
|
|
244
|
+
index3 -= 1
|
|
245
|
+
index4 -= 1
|
|
246
|
+
|
|
247
|
+
atom1 = np.array(geom[index1*3:index1*3 + 3])
|
|
248
|
+
atom2 = np.array(geom[index2*3:index2*3 + 3])
|
|
249
|
+
atom3 = np.array(geom[index3*3:index3*3 + 3])
|
|
250
|
+
atom4 = np.array(geom[index4*3:index4*3 + 3])
|
|
251
|
+
|
|
252
|
+
b1 = atom1 - atom2
|
|
253
|
+
b2 = atom2 - atom3
|
|
254
|
+
b3 = atom3 - atom4
|
|
255
|
+
|
|
256
|
+
b1 /= np.linalg.norm(b1)
|
|
257
|
+
b2 /= np.linalg.norm(b2)
|
|
258
|
+
b3 /= np.linalg.norm(b3)
|
|
259
|
+
|
|
260
|
+
n1 = np.cross(b1, b2)
|
|
261
|
+
n2 = np.cross(b2, b3)
|
|
262
|
+
m = np.cross(n1, b2)
|
|
263
|
+
|
|
264
|
+
x = np.dot(n1, n2)
|
|
265
|
+
y = np.dot(m, n2)
|
|
266
|
+
|
|
267
|
+
return np.arctan2(y, x)*180./np.pi
|
|
268
|
+
|
|
269
|
+
def _xyz_to_array(xyz):
|
|
270
|
+
xyz = xyz.strip()
|
|
271
|
+
|
|
272
|
+
try:
|
|
273
|
+
#throw exception if NOT a number, indicating that our XYZ file has no header
|
|
274
|
+
tokens = xyz.split()
|
|
275
|
+
int(tokens[0])
|
|
276
|
+
#this XYZ formatted string is proper, containing a header that must be removed
|
|
277
|
+
xyz = xyz.split("\n",2)[2]
|
|
278
|
+
except ValueError:
|
|
279
|
+
pass
|
|
280
|
+
|
|
281
|
+
#process the actual geometry of the XYZ file, converting into an array
|
|
282
|
+
geom = xyz.split()
|
|
283
|
+
#remove the element names, leaving only an array of positions
|
|
284
|
+
del geom[0::4]
|
|
285
|
+
|
|
286
|
+
#convert to an array of floats and return
|
|
287
|
+
return np.array(geom,dtype=float)
|
|
@@ -16,7 +16,7 @@ class WebMOREST:
|
|
|
16
16
|
creation/destruction.
|
|
17
17
|
"""
|
|
18
18
|
|
|
19
|
-
def __init__(self, base_url, username, password=""):
|
|
19
|
+
def __init__(self, base_url, username, password="", expiration=24):
|
|
20
20
|
"""Constructor for WebMOREST
|
|
21
21
|
|
|
22
22
|
This constructor generates a WebMOREST object and also generates and stores a newly
|
|
@@ -26,6 +26,7 @@ class WebMOREST:
|
|
|
26
26
|
base_url(str): The base URL (ending in rest.cgi) of the WeBMO rest endpoint
|
|
27
27
|
username(str): The WebMO username
|
|
28
28
|
password(str, optional): The WebMO password; if omitted, this is supplied interactively
|
|
29
|
+
expiration(int, optional): The expiration time (in hours) of the REST session token
|
|
29
30
|
|
|
30
31
|
Returns:
|
|
31
32
|
object: The newly constructed WebMO object
|
|
@@ -36,7 +37,7 @@ class WebMOREST:
|
|
|
36
37
|
if not password:
|
|
37
38
|
password=getpass(prompt="Enter WebMO password for user %s:" % username)
|
|
38
39
|
#obtain a REST token using the specified credentials
|
|
39
|
-
login={'username' : username, 'password' : password
|
|
40
|
+
login={'username' : username, 'password' : password, 'expiration' : expiration}
|
|
40
41
|
r = requests.post(base_url + '/sessions', data=login)
|
|
41
42
|
r.raise_for_status() #raise an exception if there is a problem with the request
|
|
42
43
|
|
|
@@ -315,7 +316,7 @@ class WebMOREST:
|
|
|
315
316
|
r = requests.delete(self._base_url + "/jobs/%d" % job_number, params=self._auth)
|
|
316
317
|
r.raise_for_status()
|
|
317
318
|
|
|
318
|
-
def import_job(self, job_name,
|
|
319
|
+
def import_job(self, job_name, output_filename, engine):
|
|
319
320
|
"""Imports an existing output file into WebMO
|
|
320
321
|
|
|
321
322
|
This call imports an existing computational output file, parsing the output and creating a newly
|
|
@@ -323,7 +324,7 @@ class WebMOREST:
|
|
|
323
324
|
|
|
324
325
|
Arguments:
|
|
325
326
|
job_name(str): The name of the new WebMO job
|
|
326
|
-
|
|
327
|
+
output_filename(str): The filename (full path) of an existing output file to import
|
|
327
328
|
engine(str): The name of the computational engine
|
|
328
329
|
|
|
329
330
|
Returns:
|
|
@@ -332,12 +333,12 @@ class WebMOREST:
|
|
|
332
333
|
|
|
333
334
|
params = self._auth.copy()
|
|
334
335
|
params.update({'jobName' : job_name, 'engine' : engine})
|
|
335
|
-
output_file = {'outputFile' : ('output.log', open(
|
|
336
|
+
output_file = {'outputFile' : ('output.log', open(output_filename, 'rb'), 'text/plain')}
|
|
336
337
|
r = requests.post(self._base_url + '/jobs', data=params, files=output_file)
|
|
337
338
|
r.raise_for_status()
|
|
338
339
|
return r.json()["jobNumber"]
|
|
339
340
|
|
|
340
|
-
def submit_job(self, job_name,
|
|
341
|
+
def submit_job(self, job_name, input_contents, engine, queue=None, nodes=1, ppn=1, **kwargs):
|
|
341
342
|
"""Submits and executes a new WebMO job
|
|
342
343
|
|
|
343
344
|
This call submits and executes a new job to a computational engine, generating a new WebMO job,
|
|
@@ -345,7 +346,7 @@ class WebMOREST:
|
|
|
345
346
|
|
|
346
347
|
Arguments:
|
|
347
348
|
job_name(str): The name of the new WebMO job
|
|
348
|
-
|
|
349
|
+
input_contents(str): The valid input for submission to the computational engine as its input file
|
|
349
350
|
engine(str): The name of the computational engine
|
|
350
351
|
queue(str, optional): The name of the queue in which to submit the job_name (required for external queues)
|
|
351
352
|
nodes(int, optional): The number of nodes to use; defaults to 1
|
|
@@ -358,7 +359,7 @@ class WebMOREST:
|
|
|
358
359
|
"""
|
|
359
360
|
|
|
360
361
|
params = self._auth.copy()
|
|
361
|
-
params.update({'jobName' : job_name, 'engine' : engine, 'inputFile':
|
|
362
|
+
params.update({'jobName' : job_name, 'engine' : engine, 'inputFile': input_contents,
|
|
362
363
|
'queue': queue, 'nodes': nodes, 'ppn': ppn, **kwargs})
|
|
363
364
|
r = requests.post(self._base_url + '/jobs', data=params)
|
|
364
365
|
r.raise_for_status()
|
|
@@ -415,7 +416,7 @@ class WebMOREST:
|
|
|
415
416
|
# Engines resource
|
|
416
417
|
#
|
|
417
418
|
def get_engines(self, target_user=""):
|
|
418
|
-
"""Fetches a list of
|
|
419
|
+
"""Fetches a list of computational engines available to the current user or specified target user
|
|
419
420
|
|
|
420
421
|
This call returns a JSON-formatted list of available engines and associated queues.
|
|
421
422
|
|
|
@@ -620,22 +621,10 @@ class WebMOREST:
|
|
|
620
621
|
#
|
|
621
622
|
# Helper functions
|
|
622
623
|
#
|
|
623
|
-
def wait_for_job(self,
|
|
624
|
-
"""Waits for completion of the specified WebMO
|
|
624
|
+
def wait_for_job(self, *job_numbers, poll_frequency=5):
|
|
625
|
+
"""Waits for completion of the specified WebMO jobs(s)
|
|
625
626
|
|
|
626
|
-
This call blocks until the specified WebMO job
|
|
627
|
-
|
|
628
|
-
Arguments:
|
|
629
|
-
job_number(int): The job number to wait for
|
|
630
|
-
poll_frequency(int, optional): The frequency at which to check the job status (default is 5s)
|
|
631
|
-
"""
|
|
632
|
-
|
|
633
|
-
self.wait_for_jobs([job_number], poll_frequency)
|
|
634
|
-
|
|
635
|
-
def wait_for_jobs(self, job_numbers, poll_frequency=5):
|
|
636
|
-
"""Waits for completion of the specified list of WebMO jobs
|
|
637
|
-
|
|
638
|
-
This call blocks until the specified WebMO jobs have all finished executing (successfully or not)
|
|
627
|
+
This call blocks until the specified WebMO job(s) have all finished executing (successfully or not)
|
|
639
628
|
|
|
640
629
|
Arguments:
|
|
641
630
|
job_numbers(list): A list of job numbers which will be waited upon
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: webmo
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.1
|
|
4
4
|
Summary: A Python-based interface to WebMO
|
|
5
5
|
Home-page: https://github.com/jrschmidt2/webmo-python
|
|
6
|
-
Download-URL: https://github.com/jrschmidt2/webmo-python/archive/refs/tags/1.
|
|
6
|
+
Download-URL: https://github.com/jrschmidt2/webmo-python/archive/refs/tags/1.4.1.tar.gz
|
|
7
7
|
Author: J.R. Schmidt
|
|
8
8
|
Author-email: schmidt@webmo.net
|
|
9
9
|
License: MIT
|
|
@@ -11,4 +11,5 @@ Keywords: WEBMO,REST,API
|
|
|
11
11
|
Provides-Extra: spectrum
|
|
12
12
|
Provides-Extra: util
|
|
13
13
|
Provides-Extra: gui
|
|
14
|
+
Provides-Extra: all
|
|
14
15
|
License-File: LICENSE
|
webmo-1.3.2/webmo/util.py
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
def xyz_from_name(name):
|
|
2
|
-
"""Obtains an XYZ-formatted geometry for the molecule name via PubChem lookup.
|
|
3
|
-
|
|
4
|
-
Arguments:
|
|
5
|
-
name(str): The name of the molecule.
|
|
6
|
-
|
|
7
|
-
Returns:
|
|
8
|
-
A string containing the molecule's XYZ-formatted geometry.
|
|
9
|
-
"""
|
|
10
|
-
import pubchempy as pcp
|
|
11
|
-
|
|
12
|
-
results = pcp.get_compounds(name, 'name', record_type='3d')
|
|
13
|
-
if (len(results) == 0):
|
|
14
|
-
raise ValueError("Specified molecule could not be found in PubChem")
|
|
15
|
-
|
|
16
|
-
record = results[0]
|
|
17
|
-
geom = ""
|
|
18
|
-
for atom in record.atoms:
|
|
19
|
-
element = atom.element
|
|
20
|
-
x = float(atom.x)
|
|
21
|
-
y = float(atom.y)
|
|
22
|
-
z = float(atom.z)
|
|
23
|
-
geom += f"{element}\t{x:f}\t{y:f}\t{z:f}\n"
|
|
24
|
-
|
|
25
|
-
return geom
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|