-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathreadinput.py
More file actions
197 lines (148 loc) · 5.36 KB
/
readinput.py
File metadata and controls
197 lines (148 loc) · 5.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
""" This is an auxiliary module for reading input .ini files.
It is based on Python's stdlib
`ConfigParser <http://docs.python.org/2/library/configparser.html/>`_
This module also provides functionality for setting run sets where
one or more of the parameters run over a list of values.
"""
import os, os.path, socket
from ConfigParser import SafeConfigParser, RawConfigParser, NoOptionError
from warnings import warn
from itertools import product
import re
import numpy
def guess_type(s):
""" Converts s into int or float if possible. If not, leave it as a
string. This will give us problems if the user wants to e.g. use
filenames or run names that can be parser as a number. So have to
implement a better approach whereby parameter names are associated with
types.
"""
try:
return int(s)
except ValueError:
pass
try:
return float(s)
except ValueError:
pass
return s
# These are decorators to check allowed values.
def positive(func):
""" A decorator to constraint the parameter to positive values. """
def f(s):
r = func(s)
if not r >= 0:
raise ValueError("%s must be positive" % func.func_name)
return r
f.__doc__ = func.__doc__
f.func_name = func.func_name
return f
# These are decorators to check allowed values.
def nonnegative(func):
""" A decorator to constraint the parameter to nonnegative values. """
def f(s):
r = func(s)
if r < 0:
raise ValueError("%s must be positive" % func.func_name)
return r
f.__doc__ = func.__doc__
f.func_name = func.func_name
return f
# A decorator to set a default value, stored in the dictionary PARAM_DEFAULTS
PARAM_DEFAULTS = {}
def default(value):
def deco(func):
global PARAM_DEFAULTS
PARAM_DEFAULTS[func.func_name] = value
return func
return deco
def load_input(fname, parameters, d=None, upper=False, raw=False):
""" Loads an input file and stores its values in the dictionary
d. If upper is true, transforms the parameter names to upper case. """
if d is None:
d = dict()
config = SafeConfigParser()
defaults = dict(home=os.environ['HOME'],
user=os.environ['LOGNAME'],
cwd=os.getcwd(),
input=os.path.splitext(os.path.basename(fname))[0],
input_dir=os.path.split(os.path.realpath(fname))[0],
hostname=socket.gethostname())
config.read(fname)
for section in ['global', 'parameters']:
for name, value in config.items(section, vars=defaults, raw=raw):
print name, repr(value)
try:
func = getattr(parameters, name)
value = expand(value, func)
print "%-30s =\t%-10s [%s]" % (name, repr(value), func.__doc__)
except AttributeError:
warn("'%s' is not defined as a parameter." % name)
value = guess_type(value)
if upper:
name = name.upper()
d[name] = value
r = PARAM_DEFAULTS.copy()
r.update(d)
return r
RE_LIST = re.compile(r'@\((.+)\)')
RE_LOOP = re.compile(r'@@\(([\d.-]+):([\d.-]+)(:([\d.-]+))?\)')
def expand(s, parser):
""" Expands special characters to produce e.g. lists of many parameters.
"""
# All expansions start with the symbol '@':
if not '@' in s:
return parser(s)
m = RE_LIST.match(s)
if m:
return [parser(x) for x in m.group(1).split()]
m = RE_LOOP.match(s)
if m:
if m.group(4) is not None:
a, b, c = parser(m.group(1)), parser(m.group(2)), parser(m.group(4))
else:
a, b, c = parser(m.group(1)), parser(m.group(2)), None
r = numpy.arange(a, b, c)
print r
print [parser(x) for x in r]
return [parser(str(x)) for x in r]
def expand_dict(d):
""" Takes a dictionary that may contain a few lists as values and returns
an iterator over dictionaries where each of these elements is iterated. """
keys, lists = zip(*[(k, v) for k, v in d.iteritems()
if isinstance(v, list)])
for tpl in product(*lists):
d0 = d.copy()
for k, v in zip(keys, tpl):
d0[k] = v
yield d0
def expand_input(ifile, parameters):
""" Expands an input file by expanding some of its arguments.
"""
d = load_input(ifile, parameters, d={}, upper=False, raw=True)
base = os.path.splitext(ifile)[0]
config = RawConfigParser()
config.read(ifile)
l = []
for i, d0 in enumerate(expand_dict(d)):
fname = '%s_%.4d.ini' % (base, i)
l.append(fname)
with open(fname, 'w') as fp:
for k, v in d0.iteritems():
for sect in config.sections():
try:
old = config.get(sect, k)
config.set(sect, k, v)
except NoOptionError:
pass
config.write(fp)
return l
def main():
import sys
import parameters
expand_input(sys.argv[1], parameters)
# d = load_input(sys.argv[1], parameters, d={}, upper=True)
# for d0 in expand_dict(d):
# print d0
if __name__ == '__main__':
main()