cr-hydra 0.2.0__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.
- cr_hydra-0.2.0/MANIFEST.in +1 -0
- cr_hydra-0.2.0/PKG-INFO +7 -0
- cr_hydra-0.2.0/debian/changelog +5 -0
- cr_hydra-0.2.0/debian/compat +1 -0
- cr_hydra-0.2.0/debian/control +14 -0
- cr_hydra-0.2.0/debian/rules +7 -0
- cr_hydra-0.2.0/lib/cr_hydra/__init__.py +7 -0
- cr_hydra-0.2.0/lib/cr_hydra/settings.py +42 -0
- cr_hydra-0.2.0/setup.cfg +4 -0
- cr_hydra-0.2.0/setup.py +43 -0
- cr_hydra-0.2.0/src/cr_hydra.egg-info/PKG-INFO +7 -0
- cr_hydra-0.2.0/src/cr_hydra.egg-info/SOURCES.txt +18 -0
- cr_hydra-0.2.0/src/cr_hydra.egg-info/dependency_links.txt +1 -0
- cr_hydra-0.2.0/src/cr_hydra.egg-info/entry_points.txt +5 -0
- cr_hydra-0.2.0/src/cr_hydra.egg-info/requires.txt +5 -0
- cr_hydra-0.2.0/src/cr_hydra.egg-info/top_level.txt +5 -0
- cr_hydra-0.2.0/src/crh_add.py +322 -0
- cr_hydra-0.2.0/src/crh_get_file.py +69 -0
- cr_hydra-0.2.0/src/crh_retrieve.py +180 -0
- cr_hydra-0.2.0/src/crh_worker.py +352 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
include debian/*
|
cr_hydra-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
9
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
Source: cr-hydra
|
|
2
|
+
Section: python
|
|
3
|
+
Maintainer: Maximilian Weigand <mweigand@geo.uni-bonn.de>
|
|
4
|
+
Architecture: all
|
|
5
|
+
Build-Depends: debhelper (>= 9), dh-python,
|
|
6
|
+
python3-all-dev (>= 3.1.2-8~),
|
|
7
|
+
python-setuptools (>= 0.6b3-1~), python3-setuptools,
|
|
8
|
+
Depends: ${python3:Depends}, ${misc:Depends}
|
|
9
|
+
Description: cr_hydra program suite for distributed CRTomo inversions
|
|
10
|
+
|
|
11
|
+
Package: python3-crhydra
|
|
12
|
+
Architecture: all
|
|
13
|
+
Description: cr_hydra program suite for distributed CRTomo inversions
|
|
14
|
+
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Manage settings, for now only by means of a config file
|
|
2
|
+
"""
|
|
3
|
+
import os
|
|
4
|
+
import configparser
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _find_best_config_file():
|
|
8
|
+
"""Look for a configuration file in certain locations of the file system
|
|
9
|
+
|
|
10
|
+
Returns
|
|
11
|
+
-------
|
|
12
|
+
filename : None|str
|
|
13
|
+
None if no config file was found, otherwise an absolute path to the
|
|
14
|
+
configuration file
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
# look at these file paths in increasing priority
|
|
18
|
+
locations = [
|
|
19
|
+
'/etc/crhydra/crhydra.cfg',
|
|
20
|
+
os.getenv('HOME') + os.sep + '.crhydra.cfg',
|
|
21
|
+
os.getcwd() + os.sep + 'crhydra.cfg',
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
config_file = None
|
|
25
|
+
for filename in locations:
|
|
26
|
+
if os.path.isfile(filename):
|
|
27
|
+
config_file = os.path.abspath(filename)
|
|
28
|
+
return config_file
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def get_config():
|
|
32
|
+
"""Return the configuration of crhydra, if a config file is available
|
|
33
|
+
"""
|
|
34
|
+
config_file = _find_best_config_file()
|
|
35
|
+
if config_file is None:
|
|
36
|
+
raise IOError(
|
|
37
|
+
'No config file was found! It is required to supply the database' +
|
|
38
|
+
' credentials!'
|
|
39
|
+
)
|
|
40
|
+
config = configparser.ConfigParser()
|
|
41
|
+
config.read(config_file)
|
|
42
|
+
return config
|
cr_hydra-0.2.0/setup.cfg
ADDED
cr_hydra-0.2.0/setup.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
import os
|
|
3
|
+
import glob
|
|
4
|
+
|
|
5
|
+
from setuptools import setup
|
|
6
|
+
|
|
7
|
+
version_long = '0.2.0'
|
|
8
|
+
|
|
9
|
+
# generate entry points
|
|
10
|
+
entry_points = {'console_scripts': []}
|
|
11
|
+
scripts = [os.path.basename(script)[0:-3] for script in glob.glob('src/*.py')]
|
|
12
|
+
for script in scripts:
|
|
13
|
+
print(script)
|
|
14
|
+
entry_points['console_scripts'].append(
|
|
15
|
+
'{0} = {0}:main'.format(script)
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
print(scripts, entry_points)
|
|
19
|
+
|
|
20
|
+
if __name__ == '__main__':
|
|
21
|
+
setup(
|
|
22
|
+
name='cr_hydra',
|
|
23
|
+
version=version_long,
|
|
24
|
+
description='CRHydra - distributed CRTomo computing',
|
|
25
|
+
author='Maximilian Weigand',
|
|
26
|
+
author_email='mweigand@geo.uni-bonn.de',
|
|
27
|
+
license='MIT',
|
|
28
|
+
# url='https://github.com/geophysics-ubonn/reda',
|
|
29
|
+
packages=['cr_hydra', ],
|
|
30
|
+
package_dir={
|
|
31
|
+
'': 'src',
|
|
32
|
+
'cr_hydra': 'lib/cr_hydra',
|
|
33
|
+
},
|
|
34
|
+
py_modules=scripts,
|
|
35
|
+
entry_points=entry_points,
|
|
36
|
+
install_requires=[
|
|
37
|
+
'sqlalchemy >= 2.0',
|
|
38
|
+
'psycopg2-binary',
|
|
39
|
+
'ipython',
|
|
40
|
+
'numpy',
|
|
41
|
+
'pandas',
|
|
42
|
+
],
|
|
43
|
+
)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
MANIFEST.in
|
|
2
|
+
setup.py
|
|
3
|
+
debian/changelog
|
|
4
|
+
debian/compat
|
|
5
|
+
debian/control
|
|
6
|
+
debian/rules
|
|
7
|
+
lib/cr_hydra/__init__.py
|
|
8
|
+
lib/cr_hydra/settings.py
|
|
9
|
+
src/crh_add.py
|
|
10
|
+
src/crh_get_file.py
|
|
11
|
+
src/crh_retrieve.py
|
|
12
|
+
src/crh_worker.py
|
|
13
|
+
src/cr_hydra.egg-info/PKG-INFO
|
|
14
|
+
src/cr_hydra.egg-info/SOURCES.txt
|
|
15
|
+
src/cr_hydra.egg-info/dependency_links.txt
|
|
16
|
+
src/cr_hydra.egg-info/entry_points.txt
|
|
17
|
+
src/cr_hydra.egg-info/requires.txt
|
|
18
|
+
src/cr_hydra.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# *-* coding: utf-8 *-*
|
|
3
|
+
"""Look for all unfinished tomodirs in the present directory (and
|
|
4
|
+
subdirectories), e called.
|
|
5
|
+
"""
|
|
6
|
+
import logging
|
|
7
|
+
import hashlib
|
|
8
|
+
import io
|
|
9
|
+
import shutil
|
|
10
|
+
import uuid
|
|
11
|
+
import os
|
|
12
|
+
import datetime
|
|
13
|
+
import json
|
|
14
|
+
import tarfile
|
|
15
|
+
import platform
|
|
16
|
+
|
|
17
|
+
from sqlalchemy import create_engine
|
|
18
|
+
from sqlalchemy import text
|
|
19
|
+
from optparse import OptionParser
|
|
20
|
+
import IPython
|
|
21
|
+
|
|
22
|
+
from cr_hydra.settings import get_config
|
|
23
|
+
IPython
|
|
24
|
+
|
|
25
|
+
logger = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def handle_cmd_options():
|
|
29
|
+
parser = OptionParser()
|
|
30
|
+
# parser.add_option(
|
|
31
|
+
# '-t', "--threads",
|
|
32
|
+
# dest="number_threads",
|
|
33
|
+
# type="int",
|
|
34
|
+
# help="number of threads EACH CRMod/CRTomo instance uses. If not " +
|
|
35
|
+
# "set, will be determined automatically",
|
|
36
|
+
# default=None,
|
|
37
|
+
# )
|
|
38
|
+
|
|
39
|
+
# parser.add_option(
|
|
40
|
+
# "-n", "--number",
|
|
41
|
+
# dest="number_processes",
|
|
42
|
+
# help="How many CRMod/CRTomo instances to start in parallel. " +
|
|
43
|
+
# "Default: number of detected CPUs/2",
|
|
44
|
+
# type='int',
|
|
45
|
+
# default=None,
|
|
46
|
+
# )
|
|
47
|
+
|
|
48
|
+
parser.add_option(
|
|
49
|
+
"-f", "--force",
|
|
50
|
+
dest="force_registration",
|
|
51
|
+
help="Assume that no other crh_add is running" +
|
|
52
|
+
" and add not fully initialized simulations",
|
|
53
|
+
action='store_true',
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
(options, args) = parser.parse_args()
|
|
57
|
+
return options
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def is_tomodir(subdirectories):
|
|
61
|
+
"""provided with the subdirectories of a given directory, check if this is
|
|
62
|
+
a tomodir
|
|
63
|
+
"""
|
|
64
|
+
required = (
|
|
65
|
+
'exe',
|
|
66
|
+
'config',
|
|
67
|
+
'rho',
|
|
68
|
+
'mod',
|
|
69
|
+
'inv'
|
|
70
|
+
)
|
|
71
|
+
is_tomodir = True
|
|
72
|
+
for subdir in required:
|
|
73
|
+
if subdir not in subdirectories:
|
|
74
|
+
is_tomodir = False
|
|
75
|
+
return is_tomodir
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def check_if_needs_modeling(tomodir):
|
|
79
|
+
"""check of we need to run CRMod in a given tomodir
|
|
80
|
+
"""
|
|
81
|
+
required_files = (
|
|
82
|
+
'config' + os.sep + 'config.dat',
|
|
83
|
+
'rho' + os.sep + 'rho.dat',
|
|
84
|
+
'grid' + os.sep + 'elem.dat',
|
|
85
|
+
'grid' + os.sep + 'elec.dat',
|
|
86
|
+
'exe' + os.sep + 'crmod.cfg',
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
not_allowed = (
|
|
90
|
+
'mod' + os.sep + 'volt.dat',
|
|
91
|
+
)
|
|
92
|
+
needs_modeling = True
|
|
93
|
+
for filename in not_allowed:
|
|
94
|
+
if os.path.isfile(tomodir + os.sep + filename):
|
|
95
|
+
needs_modeling = False
|
|
96
|
+
|
|
97
|
+
for filename in required_files:
|
|
98
|
+
full_file = tomodir + os.sep + filename
|
|
99
|
+
if not os.path.isfile(full_file):
|
|
100
|
+
needs_modeling = False
|
|
101
|
+
|
|
102
|
+
return needs_modeling
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def check_if_needs_inversion(tomodir):
|
|
106
|
+
"""check of we need to run CRTomo in a given tomodir
|
|
107
|
+
|
|
108
|
+
Parameters
|
|
109
|
+
----------
|
|
110
|
+
tomodir : str
|
|
111
|
+
Tomodir to check
|
|
112
|
+
|
|
113
|
+
Returns
|
|
114
|
+
-------
|
|
115
|
+
needs_inversion : bool
|
|
116
|
+
True if not finished yet
|
|
117
|
+
"""
|
|
118
|
+
required_files = (
|
|
119
|
+
'grid' + os.sep + 'elem.dat',
|
|
120
|
+
'grid' + os.sep + 'elec.dat',
|
|
121
|
+
'exe' + os.sep + 'crtomo.cfg',
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
needs_inversion = True
|
|
125
|
+
|
|
126
|
+
for filename in required_files:
|
|
127
|
+
if not os.path.isfile(tomodir + os.sep + filename):
|
|
128
|
+
needs_inversion = False
|
|
129
|
+
|
|
130
|
+
# check for crmod OR modeling capabilities
|
|
131
|
+
if not os.path.isfile(tomodir + os.sep + 'mod' + os.sep + 'volt.dat'):
|
|
132
|
+
if not check_if_needs_modeling(tomodir):
|
|
133
|
+
needs_inversion = False
|
|
134
|
+
|
|
135
|
+
# check if finished
|
|
136
|
+
inv_ctr_file = tomodir + os.sep + 'inv' + os.sep + 'inv.ctr'
|
|
137
|
+
if os.path.isfile(inv_ctr_file):
|
|
138
|
+
inv_lines = open(inv_ctr_file, 'r').readlines()
|
|
139
|
+
if inv_lines[-1].startswith('***finished***'):
|
|
140
|
+
needs_inversion = False
|
|
141
|
+
|
|
142
|
+
return needs_inversion
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def find_unfinished_tomodirs(directory):
|
|
146
|
+
needs_modeling = []
|
|
147
|
+
needs_inversion = []
|
|
148
|
+
for root, dirs, files in os.walk(directory):
|
|
149
|
+
dirs.sort()
|
|
150
|
+
if is_tomodir(dirs):
|
|
151
|
+
logging.info('found tomodir: {}'.format(root))
|
|
152
|
+
if check_if_needs_modeling(root):
|
|
153
|
+
needs_modeling.append(root)
|
|
154
|
+
if check_if_needs_inversion(root):
|
|
155
|
+
needs_inversion.append(root)
|
|
156
|
+
|
|
157
|
+
return sorted(needs_modeling), sorted(needs_inversion)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _register_tomodir_for_processing(
|
|
161
|
+
tomodir_raw, sim_type, global_settings, cmd_options):
|
|
162
|
+
"""
|
|
163
|
+
sim_type: inv|mod
|
|
164
|
+
"""
|
|
165
|
+
tomodir = os.path.abspath(tomodir_raw)
|
|
166
|
+
# should be read from the configuration file
|
|
167
|
+
username = 'mweigand'
|
|
168
|
+
|
|
169
|
+
crh_file = tomodir + '.crh'
|
|
170
|
+
if os.path.isfile(crh_file):
|
|
171
|
+
if cmd_options.force_registration:
|
|
172
|
+
logger.info('Checking existing .crh file: {}'.format(tomodir_raw))
|
|
173
|
+
# load crh file
|
|
174
|
+
with open(crh_file, 'r') as fid:
|
|
175
|
+
settings_tmp = json.load(fid)
|
|
176
|
+
if len(
|
|
177
|
+
settings_tmp.keys()
|
|
178
|
+
) == 1 and 'datetime_init' in settings_tmp:
|
|
179
|
+
logger.info('Simulation was not fully registered.')
|
|
180
|
+
logger.info('Deleting .crh file and registering again')
|
|
181
|
+
os.unlink(crh_file)
|
|
182
|
+
# now proceed
|
|
183
|
+
else:
|
|
184
|
+
return
|
|
185
|
+
else:
|
|
186
|
+
# do nothing - assume another process is working with this tomodir
|
|
187
|
+
return
|
|
188
|
+
|
|
189
|
+
tomodir_id = username + '_' + str(uuid.uuid4())
|
|
190
|
+
archive_file = os.path.abspath(tomodir_id + '.tar.xz')
|
|
191
|
+
|
|
192
|
+
crh_settings = {
|
|
193
|
+
'datetime_init': '{}'.format(
|
|
194
|
+
datetime.datetime.now(tz=datetime.timezone.utc)
|
|
195
|
+
),
|
|
196
|
+
}
|
|
197
|
+
# touch the crh file as a crude locking mechanism
|
|
198
|
+
with open(crh_file, 'w') as fid:
|
|
199
|
+
json.dump(crh_settings, fid)
|
|
200
|
+
|
|
201
|
+
# create archive
|
|
202
|
+
pwdx = os.getcwd()
|
|
203
|
+
os.chdir(os.path.dirname(tomodir))
|
|
204
|
+
|
|
205
|
+
data_archive = io.BytesIO()
|
|
206
|
+
with tarfile.open(fileobj=data_archive, mode='w:xz') as tar:
|
|
207
|
+
tar.add(os.path.basename(tomodir), recursive=True)
|
|
208
|
+
os.chdir(pwdx)
|
|
209
|
+
|
|
210
|
+
# prepare data for simulation registration
|
|
211
|
+
crh_settings['source_computer'] = platform.node()
|
|
212
|
+
crh_settings['sim_type'] = sim_type
|
|
213
|
+
crh_settings['crh_file'] = os.path.abspath(crh_file)
|
|
214
|
+
crh_settings['username'] = username
|
|
215
|
+
|
|
216
|
+
print(
|
|
217
|
+
'Connecting to: {}'.format(
|
|
218
|
+
global_settings['general']['db_credentials']
|
|
219
|
+
)
|
|
220
|
+
)
|
|
221
|
+
engine = create_engine(
|
|
222
|
+
global_settings['general']['db_credentials'],
|
|
223
|
+
echo=False,
|
|
224
|
+
pool_size=1,
|
|
225
|
+
pool_recycle=600,
|
|
226
|
+
)
|
|
227
|
+
connection = engine.connect()
|
|
228
|
+
# create hash of final data
|
|
229
|
+
m = hashlib.sha256()
|
|
230
|
+
data_archive.seek(0)
|
|
231
|
+
m.update(data_archive.read())
|
|
232
|
+
sha256 = m.hexdigest()
|
|
233
|
+
|
|
234
|
+
# upload archive to database
|
|
235
|
+
query = ' '.join((
|
|
236
|
+
'insert into binary_data (filename, hash, data) values',
|
|
237
|
+
'(:filename, :file_hash, :bin_data) returning index;'
|
|
238
|
+
))
|
|
239
|
+
|
|
240
|
+
data_archive.seek(0)
|
|
241
|
+
result = connection.execute(
|
|
242
|
+
text(query),
|
|
243
|
+
parameters={
|
|
244
|
+
'filename': os.path.basename(archive_file),
|
|
245
|
+
'file_hash': sha256,
|
|
246
|
+
'bin_data': data_archive.read(),
|
|
247
|
+
},
|
|
248
|
+
)
|
|
249
|
+
connection.commit()
|
|
250
|
+
assert result.rowcount == 1
|
|
251
|
+
file_id = result.fetchone()[0]
|
|
252
|
+
crh_settings['tomodir_unfinished_file'] = file_id
|
|
253
|
+
|
|
254
|
+
query = ' '.join((
|
|
255
|
+
'insert into inversions (',
|
|
256
|
+
'username,',
|
|
257
|
+
'datetime_init,',
|
|
258
|
+
'tomodir_unfinished_file,',
|
|
259
|
+
'source_computer,',
|
|
260
|
+
'sim_type,',
|
|
261
|
+
'crh_file',
|
|
262
|
+
') values (',
|
|
263
|
+
':username,',
|
|
264
|
+
':datetime_init,',
|
|
265
|
+
':tomodir_unfinished_file,',
|
|
266
|
+
':source_computer,',
|
|
267
|
+
':sim_type,',
|
|
268
|
+
':crh_file',
|
|
269
|
+
')',
|
|
270
|
+
'returning index;'
|
|
271
|
+
))
|
|
272
|
+
result = connection.execute(
|
|
273
|
+
text(query),
|
|
274
|
+
parameters=crh_settings
|
|
275
|
+
)
|
|
276
|
+
print('rowcount pre commit', result.rowcount)
|
|
277
|
+
connection.commit()
|
|
278
|
+
print('rowcount post commit', result.rowcount)
|
|
279
|
+
assert result.rowcount == 1
|
|
280
|
+
sim_id = result.fetchone()[0]
|
|
281
|
+
|
|
282
|
+
crh_settings['sim_id'] = sim_id
|
|
283
|
+
# update crh file
|
|
284
|
+
with open(crh_file, 'w') as fid:
|
|
285
|
+
json.dump(crh_settings, fid, sort_keys=True, indent=4)
|
|
286
|
+
|
|
287
|
+
# now we are ready for processing
|
|
288
|
+
connection.execute(
|
|
289
|
+
text(
|
|
290
|
+
'update inversions set '
|
|
291
|
+
'ready_for_processing=\'t\' where index=:id;'
|
|
292
|
+
),
|
|
293
|
+
parameters={'id': sim_id},
|
|
294
|
+
)
|
|
295
|
+
connection.commit()
|
|
296
|
+
# delete tomodir
|
|
297
|
+
shutil.rmtree(tomodir)
|
|
298
|
+
logger.info('Added {} to queue'.format(os.path.relpath(tomodir)))
|
|
299
|
+
connection.close()
|
|
300
|
+
engine.dispose()
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def main():
|
|
304
|
+
|
|
305
|
+
global_settings = get_config()
|
|
306
|
+
cmd_options = handle_cmd_options()
|
|
307
|
+
|
|
308
|
+
needs_modeling, needs_inversion = find_unfinished_tomodirs('.')
|
|
309
|
+
print('-' * 20)
|
|
310
|
+
print('modeling:', needs_modeling)
|
|
311
|
+
print('inversion:', needs_inversion)
|
|
312
|
+
print('-' * 20)
|
|
313
|
+
for directory in needs_modeling:
|
|
314
|
+
_register_tomodir_for_processing(
|
|
315
|
+
directory, 'mod', global_settings, cmd_options)
|
|
316
|
+
for directory in needs_inversion:
|
|
317
|
+
_register_tomodir_for_processing(
|
|
318
|
+
directory, 'inv', global_settings, cmd_options)
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
if __name__ == '__main__':
|
|
322
|
+
main()
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
import sys
|
|
3
|
+
import json
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
from sqlalchemy import create_engine
|
|
7
|
+
|
|
8
|
+
from cr_hydra.settings import get_config
|
|
9
|
+
|
|
10
|
+
logging.basicConfig(
|
|
11
|
+
level=logging.INFO,
|
|
12
|
+
format='{asctime} - {name} - %{levelname} - {message}',
|
|
13
|
+
style='{',
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def main():
|
|
18
|
+
# settings: should be read from config file/cmd
|
|
19
|
+
global_settings = get_config()
|
|
20
|
+
|
|
21
|
+
engine = create_engine(
|
|
22
|
+
global_settings['general']['db_credentials'],
|
|
23
|
+
echo=False, pool_size=1, pool_recycle=3600,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
crh_file = sys.argv[1]
|
|
27
|
+
|
|
28
|
+
with open(crh_file, 'r') as fid:
|
|
29
|
+
settings = json.load(fid)
|
|
30
|
+
print(crh_file)
|
|
31
|
+
print(settings)
|
|
32
|
+
|
|
33
|
+
file_id = settings['tomodir_unfinished_file']
|
|
34
|
+
|
|
35
|
+
result = engine.execute(
|
|
36
|
+
'select data from binary_data where index=%(file_id)s;',
|
|
37
|
+
file_id=file_id
|
|
38
|
+
)
|
|
39
|
+
assert result.rowcount == 1
|
|
40
|
+
data = result.fetchone()[0]
|
|
41
|
+
|
|
42
|
+
outfile = crh_file + '.tar.xz'
|
|
43
|
+
with open(outfile, 'wb') as fid:
|
|
44
|
+
fid.write(data)
|
|
45
|
+
|
|
46
|
+
# check if an inversion is present
|
|
47
|
+
sim_id = settings['sim_id']
|
|
48
|
+
result = engine.execute(
|
|
49
|
+
' '.join((
|
|
50
|
+
'select tomodir_finished_file from inversions',
|
|
51
|
+
'where index=%(sim_id)s and status=\'finished\'',
|
|
52
|
+
'and downloaded=\'f\'',
|
|
53
|
+
';'
|
|
54
|
+
)),
|
|
55
|
+
sim_id=sim_id
|
|
56
|
+
)
|
|
57
|
+
assert result.rowcount == 1
|
|
58
|
+
tomodir_finished_file = result.fetchone()[0]
|
|
59
|
+
|
|
60
|
+
result = engine.execute(
|
|
61
|
+
'select hash, data from binary_data where index=%(data_id)s;',
|
|
62
|
+
data_id=tomodir_finished_file
|
|
63
|
+
)
|
|
64
|
+
assert result.rowcount == 1
|
|
65
|
+
file_hash, binary_data = result.fetchone()
|
|
66
|
+
|
|
67
|
+
outfile = crh_file + '_finished.tar.xz'
|
|
68
|
+
with open(outfile, 'wb') as fid:
|
|
69
|
+
fid.write(binary_data)
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""
|
|
3
|
+
check the database for inversions
|
|
4
|
+
"""
|
|
5
|
+
import logging
|
|
6
|
+
import os
|
|
7
|
+
import hashlib
|
|
8
|
+
import io
|
|
9
|
+
import tarfile
|
|
10
|
+
import json
|
|
11
|
+
|
|
12
|
+
from sqlalchemy import create_engine, text
|
|
13
|
+
import IPython
|
|
14
|
+
|
|
15
|
+
from cr_hydra.settings import get_config
|
|
16
|
+
|
|
17
|
+
IPython
|
|
18
|
+
|
|
19
|
+
logging.basicConfig(
|
|
20
|
+
level=logging.INFO,
|
|
21
|
+
format='{asctime} - {name} - %{levelname} - {message}',
|
|
22
|
+
style='{',
|
|
23
|
+
)
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
global_settings = get_config()
|
|
27
|
+
|
|
28
|
+
engine = create_engine(
|
|
29
|
+
global_settings['general']['db_credentials'],
|
|
30
|
+
echo=False, pool_size=10, pool_recycle=60,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _is_finished(sim_id, conn):
|
|
35
|
+
"""Check if the simulation has been processed.
|
|
36
|
+
|
|
37
|
+
Ignore any rows already locked by other processes (i.e., concurrent runs
|
|
38
|
+
of crh_retrieve)
|
|
39
|
+
|
|
40
|
+
"""
|
|
41
|
+
result = conn.execute(
|
|
42
|
+
text(
|
|
43
|
+
' '.join((
|
|
44
|
+
'select tomodir_finished_file from inversions',
|
|
45
|
+
'where index=:sim_id and status=\'finished\'',
|
|
46
|
+
'and downloaded=\'f\'',
|
|
47
|
+
'for update',
|
|
48
|
+
'skip locked',
|
|
49
|
+
';'
|
|
50
|
+
))
|
|
51
|
+
),
|
|
52
|
+
parameters={
|
|
53
|
+
'sim_id': sim_id,
|
|
54
|
+
}
|
|
55
|
+
)
|
|
56
|
+
if result.rowcount == 1:
|
|
57
|
+
return result.fetchone()[0]
|
|
58
|
+
else:
|
|
59
|
+
result.close()
|
|
60
|
+
return None
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _check_and_retrieve(filename):
|
|
64
|
+
"""For a given .crh file, check if the inversion results are ready to be
|
|
65
|
+
downloaded and extract the results
|
|
66
|
+
"""
|
|
67
|
+
logger.info('Checking: {}'.format(filename))
|
|
68
|
+
sim_settings = json.load(open(filename, 'r'))
|
|
69
|
+
# ignore any simulation not successfully uploade
|
|
70
|
+
if 'sim_id' not in sim_settings:
|
|
71
|
+
return False
|
|
72
|
+
|
|
73
|
+
conn = engine.connect()
|
|
74
|
+
# transaction = conn.begin_nested()
|
|
75
|
+
|
|
76
|
+
final_data_id = _is_finished(sim_settings['sim_id'], conn)
|
|
77
|
+
|
|
78
|
+
tomodir_name = os.path.basename(filename)[:-4]
|
|
79
|
+
basedir = os.path.abspath(os.path.dirname(filename))
|
|
80
|
+
|
|
81
|
+
pwd = os.getcwd()
|
|
82
|
+
|
|
83
|
+
if final_data_id is not None:
|
|
84
|
+
# we got data
|
|
85
|
+
result = conn.execute(
|
|
86
|
+
text(
|
|
87
|
+
'select hash, data from binary_data where index=:data_id;'
|
|
88
|
+
),
|
|
89
|
+
parameters={
|
|
90
|
+
'data_id': final_data_id,
|
|
91
|
+
},
|
|
92
|
+
)
|
|
93
|
+
assert result.rowcount == 1
|
|
94
|
+
file_hash, binary_data = result.fetchone()
|
|
95
|
+
|
|
96
|
+
# check hash
|
|
97
|
+
m = hashlib.sha256()
|
|
98
|
+
m.update(binary_data)
|
|
99
|
+
assert file_hash == m.hexdigest()
|
|
100
|
+
|
|
101
|
+
logger.info('retrieving and unpacking')
|
|
102
|
+
os.chdir(basedir)
|
|
103
|
+
|
|
104
|
+
# unpack
|
|
105
|
+
fid = io.BytesIO(bytes(binary_data))
|
|
106
|
+
with tarfile.open(fileobj=fid, mode='r') as tar:
|
|
107
|
+
assert os.path.abspath(os.getcwd()) == os.path.abspath(basedir)
|
|
108
|
+
|
|
109
|
+
# make sure there are only files in the archive that go into the
|
|
110
|
+
# tomodir
|
|
111
|
+
for entry in tar.getnames():
|
|
112
|
+
if entry == '.':
|
|
113
|
+
continue
|
|
114
|
+
# strip leading './'
|
|
115
|
+
if entry.startswith('./'):
|
|
116
|
+
entry = entry[2:]
|
|
117
|
+
if not entry.startswith(tomodir_name):
|
|
118
|
+
raise Exception('Content should go into tomodir')
|
|
119
|
+
# now extract
|
|
120
|
+
tar.extractall('.')
|
|
121
|
+
os.chdir(pwd)
|
|
122
|
+
mark_sim_as_downloaded(sim_settings['sim_id'], conn)
|
|
123
|
+
os.unlink(filename)
|
|
124
|
+
# IPython.embed()
|
|
125
|
+
# transaction.commit()
|
|
126
|
+
conn.close()
|
|
127
|
+
engine.dispose()
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def mark_sim_as_downloaded(sim_id, conn):
|
|
131
|
+
# mark the simulation as downloaded and delete the files
|
|
132
|
+
result = conn.execute(
|
|
133
|
+
text(
|
|
134
|
+
'select tomodir_unfinished_file, tomodir_finished_file from ' +
|
|
135
|
+
'inversions where index=:sim_id;'
|
|
136
|
+
),
|
|
137
|
+
parameters={
|
|
138
|
+
'sim_id': sim_id,
|
|
139
|
+
}
|
|
140
|
+
)
|
|
141
|
+
assert result.rowcount == 1
|
|
142
|
+
file_ids = list(result.fetchone())
|
|
143
|
+
result = conn.execute(
|
|
144
|
+
text(
|
|
145
|
+
'update inversions set ' +
|
|
146
|
+
'tomodir_unfinished_file=NULL, ' +
|
|
147
|
+
'tomodir_finished_file=NULL, ' +
|
|
148
|
+
'downloaded=\'t\' where index=:sim_id;'
|
|
149
|
+
),
|
|
150
|
+
parameters={
|
|
151
|
+
'sim_id': sim_id,
|
|
152
|
+
},
|
|
153
|
+
)
|
|
154
|
+
conn.commit()
|
|
155
|
+
assert result.rowcount == 1
|
|
156
|
+
result.close()
|
|
157
|
+
# delete
|
|
158
|
+
result = conn.execute(
|
|
159
|
+
text('delete from binary_data where index in (:id1, :id2);'),
|
|
160
|
+
parameters={
|
|
161
|
+
'id1': file_ids[0],
|
|
162
|
+
'id2': file_ids[1],
|
|
163
|
+
},
|
|
164
|
+
)
|
|
165
|
+
conn.commit()
|
|
166
|
+
result.close()
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def main():
|
|
170
|
+
for root, dirs, files in os.walk('.'):
|
|
171
|
+
dirs.sort()
|
|
172
|
+
files.sort()
|
|
173
|
+
for filename in files:
|
|
174
|
+
if filename.endswith('.crh'):
|
|
175
|
+
_check_and_retrieve(root + os.sep + filename)
|
|
176
|
+
print(engine.pool.status())
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
if __name__ == '__main__':
|
|
180
|
+
main()
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""This cr_hydra worker program - it queries the database and does the actual
|
|
3
|
+
processing of simulations (modelings/inversions).
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
import shutil
|
|
7
|
+
import glob
|
|
8
|
+
import time
|
|
9
|
+
import datetime
|
|
10
|
+
import hashlib
|
|
11
|
+
import io
|
|
12
|
+
from multiprocessing import Process
|
|
13
|
+
import logging
|
|
14
|
+
import tarfile
|
|
15
|
+
import platform
|
|
16
|
+
import os
|
|
17
|
+
import tempfile
|
|
18
|
+
import subprocess
|
|
19
|
+
from optparse import OptionParser
|
|
20
|
+
|
|
21
|
+
import pandas as pd
|
|
22
|
+
from sqlalchemy import create_engine
|
|
23
|
+
import IPython
|
|
24
|
+
|
|
25
|
+
from cr_hydra.settings import get_config
|
|
26
|
+
|
|
27
|
+
IPython
|
|
28
|
+
|
|
29
|
+
logging.basicConfig(
|
|
30
|
+
level=logging.INFO,
|
|
31
|
+
format='{asctime} - {name} - %{levelname} - {message}',
|
|
32
|
+
style='{',
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
# settings: should be read from config file/cmd
|
|
36
|
+
global_settings = get_config()
|
|
37
|
+
# [s]
|
|
38
|
+
query_interval = 15
|
|
39
|
+
# settings end
|
|
40
|
+
|
|
41
|
+
engine = create_engine(
|
|
42
|
+
global_settings['general']['db_credentials'],
|
|
43
|
+
echo=False, pool_size=1, pool_recycle=3600,
|
|
44
|
+
)
|
|
45
|
+
node_name = platform.node()
|
|
46
|
+
logging.info('Identifying as node: {}'.format(node_name))
|
|
47
|
+
results = engine.execute(
|
|
48
|
+
' '.join((
|
|
49
|
+
'select nice_level, nr_cpus, nr_threads from node_settings',
|
|
50
|
+
'where node_name = %(node_name)s;',
|
|
51
|
+
)),
|
|
52
|
+
node_name=node_name
|
|
53
|
+
)
|
|
54
|
+
if results.rowcount == 1:
|
|
55
|
+
logging.info('Found node settings in database for node: {}'.format(
|
|
56
|
+
node_name
|
|
57
|
+
))
|
|
58
|
+
(nice_level, nr_cpus, number_of_worker_threads) = results.fetchone()
|
|
59
|
+
number_of_workers = int(nr_cpus / number_of_worker_threads)
|
|
60
|
+
else:
|
|
61
|
+
# hopefully sane defaults
|
|
62
|
+
number_of_workers = 2
|
|
63
|
+
number_of_worker_threads = 2
|
|
64
|
+
nice_level = 20
|
|
65
|
+
results.close()
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class hydra_worker(Process):
|
|
69
|
+
|
|
70
|
+
def __init__(self, name, cmd_opts, **kwargs):
|
|
71
|
+
Process.__init__(self)
|
|
72
|
+
self.name = name
|
|
73
|
+
self.cmd_opts = cmd_opts
|
|
74
|
+
self.logger = logging.getLogger('crh_worker-' + name)
|
|
75
|
+
self.engine = create_engine(
|
|
76
|
+
global_settings['general']['db_credentials'],
|
|
77
|
+
echo=False, pool_size=10, pool_recycle=3600,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
def run(self):
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
"""
|
|
84
|
+
while(True):
|
|
85
|
+
if self._check_node_active():
|
|
86
|
+
did_sim = self._query_db_and_run_sim()
|
|
87
|
+
if did_sim:
|
|
88
|
+
# try to get the next inversion immediately
|
|
89
|
+
interval = 0
|
|
90
|
+
else:
|
|
91
|
+
if self.cmd_opts.quit_after_empty:
|
|
92
|
+
self.logger.info(
|
|
93
|
+
'thread quitting due to empty db queue ' +
|
|
94
|
+
'(--quit option)'
|
|
95
|
+
)
|
|
96
|
+
return
|
|
97
|
+
interval = query_interval
|
|
98
|
+
else:
|
|
99
|
+
# sleep 30 seconds
|
|
100
|
+
self.logger.info(
|
|
101
|
+
'Node was disabled by db, sleeping 30 seconds')
|
|
102
|
+
interval = 30
|
|
103
|
+
time.sleep(interval)
|
|
104
|
+
|
|
105
|
+
def _check_node_active(self):
|
|
106
|
+
"""Check if this node should be active
|
|
107
|
+
"""
|
|
108
|
+
result = self.engine.execute(
|
|
109
|
+
'select active from node_settings where node_name=%(node_name)s;',
|
|
110
|
+
node_name=node_name
|
|
111
|
+
)
|
|
112
|
+
if result.rowcount == 1:
|
|
113
|
+
is_active = result.fetchone()[0]
|
|
114
|
+
return is_active
|
|
115
|
+
else:
|
|
116
|
+
result.close()
|
|
117
|
+
# by default we assume that this node is active
|
|
118
|
+
return True
|
|
119
|
+
|
|
120
|
+
def _query_db_and_run_sim(self):
|
|
121
|
+
"""
|
|
122
|
+
Returns
|
|
123
|
+
-------
|
|
124
|
+
did_sim: bool
|
|
125
|
+
We want to know if there were simulations to run, e.g., to adapt
|
|
126
|
+
the sleeping interval between db queries
|
|
127
|
+
"""
|
|
128
|
+
self.logger.info('Querying DB for new simulations')
|
|
129
|
+
# query database for open inversions
|
|
130
|
+
self.conn = self.engine.connect()
|
|
131
|
+
transaction = self.conn.begin_nested()
|
|
132
|
+
# get the next free, unfinished simulation
|
|
133
|
+
r = self.conn.execute(
|
|
134
|
+
' '.join((
|
|
135
|
+
'select index from inversions where ',
|
|
136
|
+
'ready_for_processing=\'t\' and status <> \'finished\'',
|
|
137
|
+
'and error=0',
|
|
138
|
+
'order by ',
|
|
139
|
+
'index asc for update skip locked limit 1;'
|
|
140
|
+
))
|
|
141
|
+
)
|
|
142
|
+
if r.rowcount == 0:
|
|
143
|
+
r.close()
|
|
144
|
+
transaction.commit()
|
|
145
|
+
self.conn.close()
|
|
146
|
+
return False
|
|
147
|
+
# now the row is locked for us
|
|
148
|
+
job_id = r.fetchone()[0]
|
|
149
|
+
self.logger.info('job id: {}'.format(job_id))
|
|
150
|
+
query = ' '.join((
|
|
151
|
+
'select',
|
|
152
|
+
'tomodir_unfinished_file, sim_type,',
|
|
153
|
+
'ready_for_processing, status',
|
|
154
|
+
'from inversions',
|
|
155
|
+
'where index=%(index)s;'
|
|
156
|
+
))
|
|
157
|
+
job_data = pd.read_sql_query(
|
|
158
|
+
query, self.conn, params={'index': job_id})
|
|
159
|
+
|
|
160
|
+
self.logger.info('Processing job id: {}'.format(job_id))
|
|
161
|
+
|
|
162
|
+
self._run_sim(
|
|
163
|
+
job_id,
|
|
164
|
+
int(job_data['tomodir_unfinished_file'].values.take(0)),
|
|
165
|
+
# job_data['hydra_location'].values.take(0),
|
|
166
|
+
# job_data['archive_hash'].values.take(0),
|
|
167
|
+
job_data['sim_type'].values.take(0)
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
# this actually updates the database
|
|
171
|
+
transaction.commit()
|
|
172
|
+
self.conn.close()
|
|
173
|
+
return True
|
|
174
|
+
|
|
175
|
+
def _run_sim(self, job_id, file_id, sim_type):
|
|
176
|
+
tempdir = tempfile.mkdtemp('_crhydra')
|
|
177
|
+
self.logger.info('tempdir: {}'.format(tempdir))
|
|
178
|
+
|
|
179
|
+
# get unfinished data and unpack
|
|
180
|
+
result = self.conn.execute(
|
|
181
|
+
'select hash, data from binary_data where index=%(file_id)s;',
|
|
182
|
+
file_id=file_id
|
|
183
|
+
)
|
|
184
|
+
file_hash, binary_data = result.fetchone()
|
|
185
|
+
|
|
186
|
+
# check hash
|
|
187
|
+
m = hashlib.sha256()
|
|
188
|
+
m.update(binary_data)
|
|
189
|
+
assert file_hash == m.hexdigest()
|
|
190
|
+
|
|
191
|
+
# unpack to tempir
|
|
192
|
+
fid = io.BytesIO(bytes(binary_data))
|
|
193
|
+
with tarfile.open(fileobj=fid, mode='r') as tar:
|
|
194
|
+
tar.extractall(path=tempdir)
|
|
195
|
+
|
|
196
|
+
# call td run
|
|
197
|
+
old_pwd = os.getcwd()
|
|
198
|
+
os.chdir(tempdir)
|
|
199
|
+
self.logger.info('Running inversion')
|
|
200
|
+
dt_inv_started = datetime.datetime.now(tz=datetime.timezone.utc)
|
|
201
|
+
|
|
202
|
+
inv_cpu = subprocess.check_output(
|
|
203
|
+
'cat /proc/cpuinfo | grep "model name" | head -1',
|
|
204
|
+
shell=True
|
|
205
|
+
)[13:].decode('utf-8').strip()
|
|
206
|
+
# 0: everything ok
|
|
207
|
+
error_code = 0
|
|
208
|
+
try:
|
|
209
|
+
subprocess.check_output(
|
|
210
|
+
'nice -n {} td_run_all_local -n 1 -t {}'.format(
|
|
211
|
+
nice_level,
|
|
212
|
+
number_of_worker_threads
|
|
213
|
+
),
|
|
214
|
+
stderr=subprocess.STDOUT,
|
|
215
|
+
shell=True
|
|
216
|
+
)
|
|
217
|
+
except subprocess.CalledProcessError as e:
|
|
218
|
+
self.logger.error('There was an error executing CRTomo')
|
|
219
|
+
self.logger.error(e)
|
|
220
|
+
# it seems something went wrong while running CRTomo...
|
|
221
|
+
query = ' '.join((
|
|
222
|
+
'update inversions set',
|
|
223
|
+
'error = %(error_code)s,',
|
|
224
|
+
'error_msg=%(error_msg)s,',
|
|
225
|
+
'datetime_inversion_started=%(dt_started)s,',
|
|
226
|
+
'datetime_finished=%(dt_finished)s, ',
|
|
227
|
+
'inv_computer=%(node_name)s,',
|
|
228
|
+
'inv_cpu=%(inv_cpu)s',
|
|
229
|
+
'where index=%(job_id)s;',
|
|
230
|
+
))
|
|
231
|
+
r = self.conn.execute(
|
|
232
|
+
query,
|
|
233
|
+
{
|
|
234
|
+
'job_id': job_id,
|
|
235
|
+
'error_code': 2,
|
|
236
|
+
'node_name': node_name,
|
|
237
|
+
'inv_cpu': inv_cpu,
|
|
238
|
+
'dt_started': dt_inv_started,
|
|
239
|
+
'dt_finished': datetime.datetime.now(
|
|
240
|
+
tz=datetime.timezone.utc),
|
|
241
|
+
'error_msg': e.cmd + '_' + e.output.decode('utf-8'),
|
|
242
|
+
}
|
|
243
|
+
)
|
|
244
|
+
return
|
|
245
|
+
|
|
246
|
+
# special case: an error.dat file appeared, indicating that the
|
|
247
|
+
# inversion itself broke. This is seen as a successful simulation
|
|
248
|
+
# from the view of cr_hydra
|
|
249
|
+
tomodir_name = glob.glob(tempdir + '/*')[0]
|
|
250
|
+
error_file = os.sep.join((
|
|
251
|
+
tomodir_name, 'exe', 'error.dat'
|
|
252
|
+
))
|
|
253
|
+
if os.path.isfile(error_file):
|
|
254
|
+
self.logger.info('found error.dat file - setting error code to 1')
|
|
255
|
+
error_code = 1
|
|
256
|
+
|
|
257
|
+
dt_inv_ended = datetime.datetime.now(tz=datetime.timezone.utc)
|
|
258
|
+
|
|
259
|
+
self.logger.info('finished')
|
|
260
|
+
# create in-memory archive
|
|
261
|
+
finished_data = io.BytesIO()
|
|
262
|
+
with tarfile.open(fileobj=finished_data, mode='w:xz') as tar_out:
|
|
263
|
+
tar_out.add('.')
|
|
264
|
+
|
|
265
|
+
# create hash of final data
|
|
266
|
+
m = hashlib.sha256()
|
|
267
|
+
finished_data.seek(0)
|
|
268
|
+
m.update(finished_data.read())
|
|
269
|
+
hash_final = m.hexdigest()
|
|
270
|
+
|
|
271
|
+
# upload
|
|
272
|
+
finished_data.seek(0)
|
|
273
|
+
result = self.conn.execute(
|
|
274
|
+
'insert into binary_data (hash, data) values' +
|
|
275
|
+
'(%(data_hash)s, %(bin_data)s) returning index;',
|
|
276
|
+
data_hash=hash_final,
|
|
277
|
+
bin_data=finished_data.read()
|
|
278
|
+
)
|
|
279
|
+
finished_data_index = result.fetchone()[0]
|
|
280
|
+
|
|
281
|
+
os.chdir(old_pwd)
|
|
282
|
+
self.logger.info('updating to finished')
|
|
283
|
+
# mark as finished
|
|
284
|
+
query = ' '.join((
|
|
285
|
+
'update inversions set',
|
|
286
|
+
'status=\'finished\',',
|
|
287
|
+
'error=%(error_code)s,',
|
|
288
|
+
'tomodir_finished_file=%(finished_data)s,',
|
|
289
|
+
'datetime_inversion_started=%(dt_started)s,',
|
|
290
|
+
'datetime_finished=%(dt_finished)s, ',
|
|
291
|
+
'inv_computer=%(node_name)s,',
|
|
292
|
+
'inv_cpu=%(inv_cpu)s',
|
|
293
|
+
'where index=%(job_id)s;',
|
|
294
|
+
))
|
|
295
|
+
r = self.conn.execute(
|
|
296
|
+
query,
|
|
297
|
+
{
|
|
298
|
+
'job_id': job_id,
|
|
299
|
+
'finished_data': finished_data_index,
|
|
300
|
+
'node_name': node_name,
|
|
301
|
+
'inv_cpu': inv_cpu,
|
|
302
|
+
'error_code': error_code,
|
|
303
|
+
'dt_started': dt_inv_started,
|
|
304
|
+
'dt_finished': dt_inv_ended,
|
|
305
|
+
}
|
|
306
|
+
)
|
|
307
|
+
assert r.rowcount == 1
|
|
308
|
+
|
|
309
|
+
# delete temporary directory
|
|
310
|
+
shutil.rmtree(tempdir)
|
|
311
|
+
|
|
312
|
+
def _get_hash_sha256(self, filename):
|
|
313
|
+
sha256 = subprocess.check_output(
|
|
314
|
+
'sha256sum "{}"'.format(filename), shell=True).decode('utf-8')
|
|
315
|
+
sha256 = sha256[0:sha256.find(' ')]
|
|
316
|
+
return sha256
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def handle_cmd_options():
|
|
320
|
+
parser = OptionParser()
|
|
321
|
+
# parser.add_option(
|
|
322
|
+
# "-n", "--number",
|
|
323
|
+
# dest="number_processes",
|
|
324
|
+
# help="How many CRMod/CRTomo instances to start in parallel. " +
|
|
325
|
+
# "Default: number of detected CPUs/2",
|
|
326
|
+
# type='int',
|
|
327
|
+
# default=None,
|
|
328
|
+
# )
|
|
329
|
+
|
|
330
|
+
parser.add_option(
|
|
331
|
+
"-q", "--quit",
|
|
332
|
+
dest="quit_after_empty",
|
|
333
|
+
help="Exit the program when no more simulations are queries in the DB",
|
|
334
|
+
action='store_true',
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
(options, args) = parser.parse_args()
|
|
338
|
+
return options
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def main():
|
|
342
|
+
cmd_opts = handle_cmd_options()
|
|
343
|
+
|
|
344
|
+
workers = []
|
|
345
|
+
for i in range(number_of_workers):
|
|
346
|
+
worker = hydra_worker('thread_{:02}'.format(i), cmd_opts)
|
|
347
|
+
workers.append(worker)
|
|
348
|
+
worker.start()
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
if __name__ == '__main__':
|
|
352
|
+
main()
|