goPEST 0.0.11__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.
- goPEST-0.0.11.dist-info/LICENSE +11 -0
- goPEST-0.0.11.dist-info/METADATA +95 -0
- goPEST-0.0.11.dist-info/RECORD +30 -0
- goPEST-0.0.11.dist-info/WHEEL +5 -0
- goPEST-0.0.11.dist-info/entry_points.txt +2 -0
- goPEST-0.0.11.dist-info/top_level.txt +1 -0
- gopest/__init__.py +11 -0
- gopest/_version.py +16 -0
- gopest/check_slaves.py +402 -0
- gopest/commands.py +80 -0
- gopest/common.py +194 -0
- gopest/data/case.pst +67 -0
- gopest/data/goPESTconfig.aut2.toml +95 -0
- gopest/data/goPESTconfig.toml +94 -0
- gopest/data/goPESTobs.list +793 -0
- gopest/data/goPESTpar.list +95 -0
- gopest/make_case_pst.py +229 -0
- gopest/obs.py +297 -0
- gopest/obs_def.py +2086 -0
- gopest/par.py +332 -0
- gopest/par_def.py +313 -0
- gopest/pest_model.py +245 -0
- gopest/rename_latest_files.py +35 -0
- gopest/run_beopest.py +205 -0
- gopest/run_ns_pr.py +617 -0
- gopest/submit_beopest.py +931 -0
- gopest/utils/__init__.py +0 -0
- gopest/utils/gener_groups.py +192 -0
- gopest/utils/t2listingh5.py +376 -0
- gopest/utils/waiwera_listing.py +587 -0
gopest/pest_model.py
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
""" This is the actual code that wraps TOUGH2 model run with pre- and post-
|
|
2
|
+
processing, so all PEST's model/batch file call this.
|
|
3
|
+
|
|
4
|
+
Use with optiona flags:
|
|
5
|
+
python pest_model.py [--svda] [--obsreref]
|
|
6
|
+
|
|
7
|
+
Run with "--svda" flag with additional command of parcalc for SVDassist runs.
|
|
8
|
+
Flag "--obsreref" is for observation re-referencing which reset save to incon
|
|
9
|
+
and overwrites the master's incon, which will be used by all subsequent model
|
|
10
|
+
runs. Flag "--test-update" will cause each slave to save a unique
|
|
11
|
+
real_model.save file back to master directory. This allows "--obsreref" to
|
|
12
|
+
later select the best save/incon to start with.
|
|
13
|
+
|
|
14
|
+
To generate/overwrite/fix the model/batch files, use:
|
|
15
|
+
python make_batch_files.py
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import time
|
|
19
|
+
import sys
|
|
20
|
+
import glob
|
|
21
|
+
import os
|
|
22
|
+
from os import devnull, system, remove, sep, path
|
|
23
|
+
from shutil import copy2
|
|
24
|
+
from shutil import Error
|
|
25
|
+
from time import sleep
|
|
26
|
+
|
|
27
|
+
from numpy.testing import assert_approx_equal
|
|
28
|
+
|
|
29
|
+
from gopest.run_ns_pr import run_ns_pr
|
|
30
|
+
from gopest.par import generate_real_model
|
|
31
|
+
from gopest.obs import read_from_real_model
|
|
32
|
+
|
|
33
|
+
from gopest.common import config
|
|
34
|
+
from gopest.common import runtime
|
|
35
|
+
|
|
36
|
+
def get_master_dir():
|
|
37
|
+
# if config['mode'] != 'local':
|
|
38
|
+
with open('_master_dir', 'r') as f:
|
|
39
|
+
line = f.readlines()[0].strip()
|
|
40
|
+
return line
|
|
41
|
+
|
|
42
|
+
def get_pest_dir():
|
|
43
|
+
with open('_pest_dir', 'r') as f:
|
|
44
|
+
line = f.readlines()[0].strip()
|
|
45
|
+
return line
|
|
46
|
+
|
|
47
|
+
def get_t2():
|
|
48
|
+
with open('_tough2', 'r') as f:
|
|
49
|
+
line = f.readlines()[0].strip()
|
|
50
|
+
return line
|
|
51
|
+
|
|
52
|
+
def get_slave_id():
|
|
53
|
+
try:
|
|
54
|
+
with open('_procid', 'r') as f:
|
|
55
|
+
line = f.readlines()[0].strip()
|
|
56
|
+
return line
|
|
57
|
+
except:
|
|
58
|
+
return '0'
|
|
59
|
+
|
|
60
|
+
def par_match(pf1, pf2):
|
|
61
|
+
matched = False
|
|
62
|
+
with open(pf1,'r') as a:
|
|
63
|
+
with open(pf2,'r') as b:
|
|
64
|
+
# once open successfully, assume equal, until something fails
|
|
65
|
+
matched = True
|
|
66
|
+
try:
|
|
67
|
+
for aa,bb in zip(a,b):
|
|
68
|
+
ax, bx = float(aa.split(',')[0]), float(bb.split(',')[0])
|
|
69
|
+
assert_approx_equal(ax, bx, significant=7)
|
|
70
|
+
except AssertionError:
|
|
71
|
+
matched = False
|
|
72
|
+
return matched
|
|
73
|
+
|
|
74
|
+
def main(obsreref, svda, testup, local, skiprun, useobf, sendbad, skippr, hdf5, waiwera):
|
|
75
|
+
fgeo = runtime['filename']['geom']
|
|
76
|
+
fsave = runtime['filename']['save']
|
|
77
|
+
fincon = runtime['filename']['incon']
|
|
78
|
+
fdato = runtime['filename']['dat_orig']
|
|
79
|
+
fdats = runtime['filename']['dat_seq']
|
|
80
|
+
flsts = runtime['filename']['lst_seq']
|
|
81
|
+
|
|
82
|
+
print("----- Running " + " ".join(sys.argv[1:]))
|
|
83
|
+
if local:
|
|
84
|
+
master_dir = '.'
|
|
85
|
+
else:
|
|
86
|
+
master_dir = get_master_dir()
|
|
87
|
+
print(" --- Clean up pest_model.obf")
|
|
88
|
+
if path.isfile('pest_model.obf'):
|
|
89
|
+
remove('pest_model.obf')
|
|
90
|
+
|
|
91
|
+
### skips everything if use obf directly
|
|
92
|
+
if useobf:
|
|
93
|
+
raise Exception('Should use pest_hp file distribution 17/12/2022')
|
|
94
|
+
if path.isfile('pest_model.obf.use'):
|
|
95
|
+
copy2(master_dir + sep + 'pest_model.obf.use', 'pest_model.obf')
|
|
96
|
+
print("Found pest_model.obf.use , skips everything.")
|
|
97
|
+
return
|
|
98
|
+
else:
|
|
99
|
+
print("Error, cannot find existing pest_model.obf.use file")
|
|
100
|
+
|
|
101
|
+
### SVD-assist
|
|
102
|
+
if svda:
|
|
103
|
+
print(" --- PARCALC")
|
|
104
|
+
try:
|
|
105
|
+
remove('pest_model.dat')
|
|
106
|
+
except:
|
|
107
|
+
print("pest_model.dat probably does not exist")
|
|
108
|
+
PARCALC = path.join(get_pest_dir(), 'parcalc')
|
|
109
|
+
system(PARCALC + ' > ' + devnull)
|
|
110
|
+
|
|
111
|
+
### goPESTpar
|
|
112
|
+
if not skiprun:
|
|
113
|
+
print(" --- goPESTpar")
|
|
114
|
+
generate_real_model(fdato, 'pest_model.dat', fdats[0])
|
|
115
|
+
# sleep(30) # just in case shared file system slow
|
|
116
|
+
|
|
117
|
+
if obsreref:
|
|
118
|
+
if path.isfile('pest_model.obf'):
|
|
119
|
+
remove('pest_model.obf')
|
|
120
|
+
# get matching incon, if exist
|
|
121
|
+
for parf in glob.glob(master_dir + sep + 'pest_model.dat.*'):
|
|
122
|
+
if par_match(parf, 'pest_model.dat'):
|
|
123
|
+
matchname = path.splitext(parf)[1]
|
|
124
|
+
print(" --- found matched incon/pars %s from master dir, overwrite Master INCON" % matchname)
|
|
125
|
+
copy2(master_dir + sep + fincon + matchname, master_dir + sep + fincon)
|
|
126
|
+
copy2(master_dir + sep + 'pest_model.obf' + matchname, 'pest_model.obf')
|
|
127
|
+
break
|
|
128
|
+
# print(" --- remove all pairs from labmda tests after searching")
|
|
129
|
+
# for f in glob.glob(master_dir + sep + 'pest_model.obf.*'):
|
|
130
|
+
# remove(f)
|
|
131
|
+
# for f in glob.glob(master_dir + sep + 'pest_model.dat.*'):
|
|
132
|
+
# remove(f)
|
|
133
|
+
# for f in glob.glob(master_dir + sep + 'real_model.incon.*'):
|
|
134
|
+
# remove(f)
|
|
135
|
+
if path.isfile('pest_model.obf'):
|
|
136
|
+
print(" --- use obf, skip actual model run")
|
|
137
|
+
return
|
|
138
|
+
else:
|
|
139
|
+
print(" --- could not find matching pars, obsreref continue with normal run")
|
|
140
|
+
|
|
141
|
+
### RUN TOUGH2 model
|
|
142
|
+
if skiprun:
|
|
143
|
+
print(" --- skip actual TOUGH2 run")
|
|
144
|
+
else:
|
|
145
|
+
if not local:
|
|
146
|
+
print(" --- use master INCON")
|
|
147
|
+
try:
|
|
148
|
+
copy2(master_dir + sep + fincon, fincon)
|
|
149
|
+
except Error as e:
|
|
150
|
+
# OK if src and dst are the same file, simply skip.
|
|
151
|
+
print(e)
|
|
152
|
+
|
|
153
|
+
START_TIME = time.time()
|
|
154
|
+
print(" --- run_ns_pr()")
|
|
155
|
+
runok = run_ns_pr()
|
|
156
|
+
if obsreref:
|
|
157
|
+
if not local:
|
|
158
|
+
print(" --- reset Master INCON")
|
|
159
|
+
copy2('real_model.incon', master_dir + sep + 'real_model.incon')
|
|
160
|
+
else:
|
|
161
|
+
print(" --- .save file written as .incon")
|
|
162
|
+
if sendbad:
|
|
163
|
+
for f in glob.glob('bad_model_*'):
|
|
164
|
+
copy2(f, master_dir + sep + 'bad_model_slave' + get_slave_id() + '_' + f)
|
|
165
|
+
if not runok:
|
|
166
|
+
print(" --- run_ns_pr failed, skip goPESTobs, no obf, make sure lamforgive/derforgive is used.")
|
|
167
|
+
return
|
|
168
|
+
print(' --- run_ns_pr() complete after', (time.time() - START_TIME), 'seconds')
|
|
169
|
+
|
|
170
|
+
### goPESTobs
|
|
171
|
+
# sleep(30) # just in case shared file system slow
|
|
172
|
+
print(" --- goPESTobs")
|
|
173
|
+
read_from_real_model(fgeo, fdats[-1], flsts[-1], 'pest_model.obf', waiwera=waiwera)
|
|
174
|
+
|
|
175
|
+
if testup:
|
|
176
|
+
print(" --- store lambda test (save,obf,pars) pair:" + get_slave_id())
|
|
177
|
+
copy2(fsave, master_dir + sep + fincon + '.' + get_slave_id())
|
|
178
|
+
copy2('pest_model.dat', master_dir + sep + 'pest_model.dat.' + get_slave_id())
|
|
179
|
+
copy2('pest_model.obf', master_dir + sep + 'pest_model.obf.' + get_slave_id())
|
|
180
|
+
|
|
181
|
+
def main_cli(argv=[]):
|
|
182
|
+
""" the main purpose of run-pest-model is to be called by PEST.
|
|
183
|
+
|
|
184
|
+
PEST needs:
|
|
185
|
+
- pest_model.tpl
|
|
186
|
+
- pest_model.ins
|
|
187
|
+
|
|
188
|
+
When PEST wants a forward run, PEST
|
|
189
|
+
1. uses pest_model.tpl to generate: pest_model.dat
|
|
190
|
+
2. runs the command: gopest run-pest-model
|
|
191
|
+
3. uses pest_model.ins to read data from: pest_model.obf
|
|
192
|
+
|
|
193
|
+
under the hood, run-pest-model's job is to:
|
|
194
|
+
a. get parameters from pest_model.dat to construct real_model.dat
|
|
195
|
+
b. run actual simulation, which generates real_model.h5
|
|
196
|
+
c. converts real_model.h5 into pest_model.obf
|
|
197
|
+
|
|
198
|
+
a. is done by goPESTpar
|
|
199
|
+
b. is done by run-forward
|
|
200
|
+
c. is done by goPESTobs
|
|
201
|
+
"""
|
|
202
|
+
obsreref = False
|
|
203
|
+
svda = False
|
|
204
|
+
testup = False
|
|
205
|
+
skiprun = False
|
|
206
|
+
useobf = False
|
|
207
|
+
sendbad = True
|
|
208
|
+
skippr = False
|
|
209
|
+
waiwera = False
|
|
210
|
+
hdf5 = False
|
|
211
|
+
|
|
212
|
+
if config['simulator']['output-type'] == 'h5':
|
|
213
|
+
hdf5 = True
|
|
214
|
+
else:
|
|
215
|
+
hdf5 = False
|
|
216
|
+
|
|
217
|
+
if config['simulator']['input-type'] == 'waiwera':
|
|
218
|
+
waiwera = True
|
|
219
|
+
hdf5 = True
|
|
220
|
+
else:
|
|
221
|
+
waiwera = False
|
|
222
|
+
|
|
223
|
+
# probably should be invoked from within gopest?
|
|
224
|
+
# local = config['pest']['mode'] == 'local'
|
|
225
|
+
local = False
|
|
226
|
+
|
|
227
|
+
skiprun = config['model']['skip']
|
|
228
|
+
skippr = config['model']['skip-pr']
|
|
229
|
+
|
|
230
|
+
print('pest_model.py running at ', os.getcwd())
|
|
231
|
+
|
|
232
|
+
if len(argv) > 1:
|
|
233
|
+
if '--test-update' in argv[1:]:
|
|
234
|
+
testup = True
|
|
235
|
+
if '--obsreref' in argv[1:]:
|
|
236
|
+
obsreref = True
|
|
237
|
+
if '--svda' in argv[1:]:
|
|
238
|
+
svda = True
|
|
239
|
+
if '--use-obf' in argv[1:]:
|
|
240
|
+
# requires existing pest_model.obf.use (PEST will remove
|
|
241
|
+
# pest_model.obf, so use different name)
|
|
242
|
+
useobf = True
|
|
243
|
+
if '--local' in argv[1:]:
|
|
244
|
+
local = True
|
|
245
|
+
main(obsreref, svda, testup, local, skiprun, useobf, sendbad, skippr, hdf5, waiwera)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import glob
|
|
2
|
+
import os
|
|
3
|
+
from shutil import copy2
|
|
4
|
+
|
|
5
|
+
from gopest.common import config
|
|
6
|
+
|
|
7
|
+
def current_iteration():
|
|
8
|
+
its = [int(f.split('.')[-1]) for f in glob.glob('%s.jco.*' % config['pest']['case-name'])]
|
|
9
|
+
its.append(0)
|
|
10
|
+
return max(its)
|
|
11
|
+
|
|
12
|
+
def rename_latest_files(argv=[]):
|
|
13
|
+
""" PEST_HP's file distribution will copy files from the best update slave
|
|
14
|
+
to here. They will be named as '*.999'. This script renames these to the
|
|
15
|
+
latest iteration number.
|
|
16
|
+
"""
|
|
17
|
+
print('gopest save-iter-model (rename_latest_files.py):')
|
|
18
|
+
|
|
19
|
+
# try:
|
|
20
|
+
# copy2('real_model.incon.999', 'real_model.incon')
|
|
21
|
+
# print(" real_model.incon.999 -> real_model.incon (copy)")
|
|
22
|
+
# except Exception as e:
|
|
23
|
+
# print(" ", e)
|
|
24
|
+
# print(" failed to copy incon file for next iteration!")
|
|
25
|
+
|
|
26
|
+
ii = current_iteration()
|
|
27
|
+
print(' Current iteration is %i' % ii)
|
|
28
|
+
|
|
29
|
+
for f in glob.glob('*.999'):
|
|
30
|
+
newname = f.replace('.999', '.%i' % ii)
|
|
31
|
+
print(' %s -> %s' % (f, newname))
|
|
32
|
+
try:
|
|
33
|
+
os.rename(f, newname)
|
|
34
|
+
except Exception as e:
|
|
35
|
+
print(e)
|
gopest/run_beopest.py
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import shutil
|
|
4
|
+
import time
|
|
5
|
+
from subprocess import Popen, DEVNULL
|
|
6
|
+
from multiprocessing import cpu_count
|
|
7
|
+
|
|
8
|
+
from gopest.common import config as cfg
|
|
9
|
+
from gopest.common import runtime
|
|
10
|
+
|
|
11
|
+
NUM_SLAVES = cfg['pest']['num_slaves']
|
|
12
|
+
SILENT_SLAVES = cfg['pest']['silent_slaves']
|
|
13
|
+
PST_NAME = cfg['pest']['case-name']
|
|
14
|
+
PORT = cfg['pest']['port']
|
|
15
|
+
SWITCHES = " ".join(cfg['pest']['switches'])
|
|
16
|
+
|
|
17
|
+
TOUGH2 = cfg['simulator']['executable']
|
|
18
|
+
BEOPEST = cfg['pest']['executable']
|
|
19
|
+
PESTDIR = cfg['pest']['dir']
|
|
20
|
+
|
|
21
|
+
"""
|
|
22
|
+
This script helps to launch BeoPEST on the case in current directory (either
|
|
23
|
+
'case.pst' or 'case_svda.pst'). Use:
|
|
24
|
+
|
|
25
|
+
python run_beopest.py
|
|
26
|
+
|
|
27
|
+
Requires:
|
|
28
|
+
- Python 2.6 or 2.7, NumPy
|
|
29
|
+
- PEST and TOUGH2 executables, see settings above.
|
|
30
|
+
|
|
31
|
+
Most of the important settings are listed above. Instead of letting PEST runs
|
|
32
|
+
TOUGH2 models directly, actual model runs were controlled by the pest_model.py
|
|
33
|
+
script, which uses the goPEST interface and run_ns_pr.py. (goPEST is a little
|
|
34
|
+
like PLPROC)
|
|
35
|
+
|
|
36
|
+
In the actual workflow, there are only a couple files that need attention:
|
|
37
|
+
|
|
38
|
+
- goPESTpar.list a list of parameters (goPESTpar.py translates this into
|
|
39
|
+
actual PEST parameter data)
|
|
40
|
+
- goPESTobs.list a list of observations (goPESTobs.py translates this
|
|
41
|
+
into actual PEST observation data)
|
|
42
|
+
|
|
43
|
+
Brief workflow:
|
|
44
|
+
|
|
45
|
+
1. edit goPESTpar.list and goPESTobs.list
|
|
46
|
+
|
|
47
|
+
2. run 'python make_case_pst.py', which processed two lists above and
|
|
48
|
+
updates case.pst. This also initialise the parameter values from the values
|
|
49
|
+
in the real_model_original.dat (.pdat).
|
|
50
|
+
|
|
51
|
+
3. inspect/modify/check case.pst
|
|
52
|
+
|
|
53
|
+
4. run 'python run_beopest.py'
|
|
54
|
+
|
|
55
|
+
5. to obtain the model with optimised parameters run 'python
|
|
56
|
+
make_real_model.py' or 'python make_real_model_svda.py', now
|
|
57
|
+
real_model.dat is the updated model, run_ns_pr.py can be used to run this
|
|
58
|
+
model.
|
|
59
|
+
|
|
60
|
+
6. occasionally run 'make_batch_files.py', eg. after svdaprep, so that model
|
|
61
|
+
batch files are correct. This should be run after moving the folder between
|
|
62
|
+
platform as well.
|
|
63
|
+
|
|
64
|
+
To update/change a TOUGH2 model, the following files needs to be updated:
|
|
65
|
+
|
|
66
|
+
- g_real_model.dat (model's mulgrid/geometry file)
|
|
67
|
+
- real_model_original.dat (and .pdat)
|
|
68
|
+
- real_model_original_pr.dat (and .pdat, rocktypes, geners not as important,
|
|
69
|
+
they are processed from the natural state real_model_ogirinal.dat)
|
|
70
|
+
- real_model.incon
|
|
71
|
+
- and of course goPESTpar.list and goPESTobs.list
|
|
72
|
+
- make sure to run 'python make_case_pst.py'
|
|
73
|
+
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
if NUM_SLAVES is None:
|
|
77
|
+
NUM_SLAVES = max(cpu_count() - 2, 1)
|
|
78
|
+
if PESTDIR:
|
|
79
|
+
BEOPEST = os.path.join(PESTDIR,BEOPEST)
|
|
80
|
+
|
|
81
|
+
def ignore_dirs(folder, files):
|
|
82
|
+
""" for shutil.copytree, ignores all subdirectories, ie. copy only files
|
|
83
|
+
"""
|
|
84
|
+
ignore_list = []
|
|
85
|
+
for f in files:
|
|
86
|
+
full_path = os.path.join(folder, f)
|
|
87
|
+
if os.path.isdir(full_path):
|
|
88
|
+
ignore_list.append(f)
|
|
89
|
+
return ignore_list
|
|
90
|
+
|
|
91
|
+
def run_cli(argv=[]):
|
|
92
|
+
""" generate master and slave commands, copy files into slave directories,
|
|
93
|
+
and launch them as process, wait until all finished.
|
|
94
|
+
"""
|
|
95
|
+
if "pest_hp" in BEOPEST.lower():
|
|
96
|
+
MASTER = BEOPEST
|
|
97
|
+
if PESTDIR:
|
|
98
|
+
AGENT = os.path.join(PESTDIR, cfg['pest']['executable_agent'])
|
|
99
|
+
else:
|
|
100
|
+
AGENT = cfg['pest']['executable_agent']
|
|
101
|
+
else:
|
|
102
|
+
MASTER = BEOPEST
|
|
103
|
+
AGENT = BEOPEST
|
|
104
|
+
|
|
105
|
+
generate_comm_files()
|
|
106
|
+
print('Running BeoPEST with silent=', SILENT_SLAVES)
|
|
107
|
+
|
|
108
|
+
# each command is a tuple (args, other options), see subproces.Popen()
|
|
109
|
+
master = ([MASTER, PST_NAME, SWITCHES, '/h :%s' % PORT], {} )
|
|
110
|
+
slaves, slaves_outs = [], []
|
|
111
|
+
for i in range(NUM_SLAVES):
|
|
112
|
+
s_dir = 'slave%i' % (i+1)
|
|
113
|
+
if os.path.exists(s_dir):
|
|
114
|
+
shutil.rmtree(s_dir)
|
|
115
|
+
shutil.copytree('.', s_dir, ignore=ignore_dirs)
|
|
116
|
+
if SILENT_SLAVES:
|
|
117
|
+
# stdout_file = DEVNULL
|
|
118
|
+
stdout_file = open(s_dir + '/stdout.txt', 'w')
|
|
119
|
+
slaves_outs.append(stdout_file)
|
|
120
|
+
else:
|
|
121
|
+
stdout_file = None
|
|
122
|
+
slaves.append((
|
|
123
|
+
[AGENT, PST_NAME, '/h localhost:%s' % PORT],
|
|
124
|
+
{
|
|
125
|
+
'cwd': s_dir,
|
|
126
|
+
'stdout': stdout_file,
|
|
127
|
+
'stderr': stdout_file,
|
|
128
|
+
}))
|
|
129
|
+
|
|
130
|
+
# start master, sleep a bit, then start all slaves
|
|
131
|
+
ps = []
|
|
132
|
+
ps.append(Popen(master[0], **master[1]))
|
|
133
|
+
time.sleep(0.1)
|
|
134
|
+
for s in slaves:
|
|
135
|
+
ps.append(Popen(s[0], **s[1]))
|
|
136
|
+
for p in ps:
|
|
137
|
+
p.wait()
|
|
138
|
+
for f in slaves_outs:
|
|
139
|
+
f.close()
|
|
140
|
+
|
|
141
|
+
def gen_run_management_file(nslave, slaves, wait=0.2, parlam=1, runtime=3600):
|
|
142
|
+
""" generate run management file required by ppest/jactest. I am
|
|
143
|
+
implementing the simplified version here, single template and instruction
|
|
144
|
+
file only. TODO extend to be more flexible, which may require more info
|
|
145
|
+
from pest control file. Each slaves is a list of tuple (SLAVNAME,SLAVDIR),
|
|
146
|
+
should have the same length as nslaves.
|
|
147
|
+
"""
|
|
148
|
+
ifletyp = 0 # for now
|
|
149
|
+
lines = []
|
|
150
|
+
lines += [
|
|
151
|
+
'prf',
|
|
152
|
+
'%i %i %f %i' % (nslave, ifletyp, wait, parlam),
|
|
153
|
+
]
|
|
154
|
+
for i in range(nslave):
|
|
155
|
+
slavname, slavdir = slaves[i]
|
|
156
|
+
if " " in slavname or "'" in slavname:
|
|
157
|
+
slavname = slavname.replace("'", "''")
|
|
158
|
+
slavname = "'" + slavname + "'"
|
|
159
|
+
lines.append(slavname + ' ' + slavdir)
|
|
160
|
+
lines.append(' '.join([str(runtime)] * nslave))
|
|
161
|
+
# TODO: support non-zero IFLETYP
|
|
162
|
+
return '\n'.join(lines)
|
|
163
|
+
|
|
164
|
+
def run_pslaves(master_command):
|
|
165
|
+
""" generate master and slave commands, copy files into slave directories,
|
|
166
|
+
and launch them as process, wait until all finished. master_command should
|
|
167
|
+
be a list of strings [cmd, arg1, arg2, ...].
|
|
168
|
+
"""
|
|
169
|
+
# each command is a tuple (args, other options), see subproces.Popen()
|
|
170
|
+
master = (master_command, {} )
|
|
171
|
+
slaves = []
|
|
172
|
+
for i in range(NUM_SLAVES):
|
|
173
|
+
s_dir = 'slave%i' % (i+1)
|
|
174
|
+
if os.path.exists(s_dir):
|
|
175
|
+
shutil.rmtree(s_dir)
|
|
176
|
+
shutil.copytree('.', s_dir, ignore=ignore_dirs)
|
|
177
|
+
slaves.append((
|
|
178
|
+
[BEOPEST, PST_NAME, '/h localhost:%s' % PORT],
|
|
179
|
+
{
|
|
180
|
+
'cwd': s_dir,
|
|
181
|
+
}))
|
|
182
|
+
|
|
183
|
+
# start master, sleep a bit, then start all slaves
|
|
184
|
+
ps = []
|
|
185
|
+
ps.append(Popen(master[0], **master[1]))
|
|
186
|
+
time.sleep(0.1)
|
|
187
|
+
for s in slaves:
|
|
188
|
+
ps.append(Popen(s[0], **s[1]))
|
|
189
|
+
for p in ps:
|
|
190
|
+
p.wait()
|
|
191
|
+
|
|
192
|
+
def generate_comm_files():
|
|
193
|
+
""" generate some files contain information so slaves know where things are.
|
|
194
|
+
TODO: to improve, there should be better ways, partially to fit with my
|
|
195
|
+
original NeSI system.
|
|
196
|
+
"""
|
|
197
|
+
def write_to(filename, line):
|
|
198
|
+
with open(filename, 'w') as f:
|
|
199
|
+
f.write(line)
|
|
200
|
+
write_to('_master_dir', os.getcwd())
|
|
201
|
+
write_to('_pest_dir', PESTDIR)
|
|
202
|
+
write_to('_tough2', TOUGH2)
|
|
203
|
+
|
|
204
|
+
if __name__ == '__main__':
|
|
205
|
+
run_cli()
|