"""basic syntax of the parameter file is::
# simple parameter file
[driver]
nsteps = 100 ; comment
max_time = 0.25
[riemann]
tol = 1.e-10
max_iter = 10
[io]
basename = myfile_
The recommended way to use this is for the code to have a master list
of parameters and their defaults (e.g. _defaults), and then the user
can override these defaults at runtime through an inputs file. These
two files have the same format.
The calling sequence would then be::
rp = RuntimeParameters()
rp.load_params("_defaults")
rp.load_params("inputs")
The parser will determine what datatype the parameter is (string,
integer, float), and store it in a RuntimeParameters object. If a
parameter that already exists is encountered a second time (e.g.,
there is a default value in _defaults and the user specifies a new
value in inputs), then the second instance replaces the first.
Runtime parameters can then be accessed via any module through the
get_param method::
tol = rp.get_param('riemann.tol')
If the optional flag no_new=1 is set, then the load_params function
will not define any new parameters, but only overwrite existing ones.
This is useful for reading in an inputs file that overrides previously
read default values.
"""
from __future__ import print_function
import os
import re
from util import msg
# some utility functions to automagically determine what the data
# types are
[docs]def is_int(string):
""" is the given string an interger? """
try:
int(string)
except ValueError:
return False
else:
return True
[docs]def is_float(string):
""" is the given string a float? """
try:
float(string)
except ValueError:
return False
else:
return True
def _get_val(value):
if is_int(value):
return int(value)
elif is_float(value):
return float(value)
else:
return value.strip()
[docs]class RuntimeParameters(object):
def __init__(self):
"""
Initialize a collection of runtime parameters. This class
holds a dictionary of the parameters, their comments, and keeps
track of which parameters were actually used.
"""
# keep track of the parameters and their comments
self.params = {}
self.param_comments = {}
# for debugging -- keep track of which parameters were
# actually looked- up
self.used_params = []
[docs] def load_params(self, pfile, no_new=0):
"""
Reads line from file and makes dictionary pairs from the data
to store.
Parameters
----------
file : str
The name of the file to parse
no_new : int, optional
If no_new = 1, then we don't add any new paramters to the
dictionary of runtime parameters, but instead just override
the values of existing ones.
"""
# check to see whether the file exists
if not os.path.isfile(pfile):
pfile = "{}/{}".format(os.environ["PYRO_HOME"], pfile)
try:
f = open(pfile, 'r')
except IOError:
msg.fail("ERROR: parameter file does not exist: {}".format(pfile))
# we could use the ConfigParser, but we actually want to
# have our configuration files be self-documenting, of the
# format key = value ; comment
sec = re.compile(r'^\[(.*)\]')
eq = re.compile(r'^([^=#]+)=([^;]+);{0,1}(.*)')
for line in f.readlines():
if sec.search(line):
_, section, _ = sec.split(line)
section = section.strip().lower()
elif eq.search(line):
_, item, value, comment, _ = eq.split(line)
item = item.strip().lower()
# define the key
key = section + "." + item
# if we have no_new = 1, then we only want to override existing
# key/values
if no_new:
if key not in self.params.keys():
msg.warning("warning, key: %s not defined" % (key))
continue
self.params[key] = _get_val(value)
# if the comment already exists (i.e. from reading in
# _defaults) and we are just resetting the value of
# the parameter (i.e. from reading in inputs), then
# we don't want to destroy the comment
if comment.strip() == "":
try:
comment = self.param_comments[key]
except KeyError:
comment = ""
self.param_comments[key] = comment.strip()
[docs] def command_line_params(self, cmd_strings):
"""
finds dictionary pairs from a string that came from the
commandline. Stores the parameters in only if they
already exist.
we expect things in the string in the form:
["sec.opt=value", "sec.opt=value"]
with each opt an element in the list
Parameters
----------
cmd_strings : list
The list of strings containing runtime parameter pairs
"""
for item in cmd_strings:
# break it apart
key, value = item.split("=")
# we only want to override existing keys/values
if key not in self.params.keys():
msg.warning("warning, key: %s not defined" % (key))
continue
# check in turn whether this is an interger, float, or string
self.params[key] = _get_val(value)
[docs] def get_param(self, key):
"""
returns the value of the runtime parameter corresponding to the
input key
"""
if self.params == {}:
msg.warning("WARNING: runtime parameters not yet initialized")
self.load_params("_defaults")
# debugging
if key not in self.used_params:
self.used_params.append(key)
if key in self.params.keys():
return self.params[key]
else:
raise KeyError("ERROR: runtime parameter {} not found".format(key))
[docs] def print_unused_params(self):
"""
Print out the list of parameters that were defined by never used
"""
for key in self.params:
if key not in self.used_params:
msg.warning("parameter %s never used" % (key))
[docs] def print_all_params(self):
"""
Print out all runtime parameters and their values
"""
for key in sorted(self.params.keys()):
print(key, "=", self.params[key])
print(" ")
[docs] def write_params(self, f):
"""
Write the runtime parameters to an HDF5 file. Here, f is the
h5py file object
"""
grp = f.create_group("runtime parameters")
keys = self.params.keys()
for key in sorted(keys):
grp.attrs[key] = self.params[key]
def __str__(self):
ostr = ""
for key in sorted(self.params.keys()):
ostr += "{} = {}\n".format(key, self.params[key])
return ostr
[docs] def print_paramfile(self):
"""
Create a file, inputs.auto, that has the structure of a pyro
inputs file, with all known parameters and values
"""
all_keys = list(self.params.keys())
try:
f = open('inputs.auto', 'w')
except IOError:
msg.fail("ERROR: unable to open inputs.auto")
f.write('# automagically generated parameter file\n')
# find all the sections
secs = set([q for (q, _) in [k.split(".") for k in all_keys]])
for sec in sorted(secs):
keys = [q for q in all_keys if q.startswith("{}.".format(sec))]
f.write("\n[{}]\n".format(sec))
for key in keys:
_, option = key.split('.')
value = self.params[key]
if self.param_comments[key] != '':
f.write("{} = {} ; {}\n".format(option, value, self.param_comments[key]))
else:
f.write("{} = {}\n".format(option, value))
f.close()
[docs] def print_sphinx_tables(self, outfile="params-sphinx.inc"):
"""Output Sphinx-formatted tables for inclusion in the documentation.
The table columns will be: param, default, description.
"""
all_keys = list(self.params.keys())
try:
f = open(outfile, 'w')
except IOError:
msg.fail("ERROR: unable to open inputs.auto")
# find all the sections
secs = set([q for (q, _) in [k.split(".") for k in all_keys]])
heading = " +=" + 32*"=" + "=+=" + 14*"=" + "=+=" + 50*"=" + "=+" + "\n"
separator = " +-" + 32*"-" + "-+-" + 14*"-" + "-+-" + 50*"-" + "-+" + "\n"
entry = " | {:32} | {:14} | {:50} |\n"
for sec in sorted(secs):
keys = [q for q in all_keys if q.startswith("{}.".format(sec))]
head = "* section: [{}]".format(sec.strip())
f.write("{}\n\n".format(head))
#f.write(len(head)*"^"+"\n\n")
f.write(separator)
f.write(entry.format("option", "value", "description"))
f.write(heading)
for key in keys:
_, option = key.split('.')
f.write(entry.format(option, "``{}``".format(str(self.params[key]).strip()),
self.param_comments[key].strip()))
f.write(separator)
f.write("\n")
f.close()
if __name__ == "__main__":
rp = RuntimeParameters()
rp.load_params("inputs.test")
rp.print_all_params()