Source code for util.runparams

"""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()