Source code for eureka.lib.readEPF

import numpy as np
import os
import inspect


[docs]class Parameter: """A generic parameter class""" def __init__(self, name, value, ptype, priorpar1=None, priorpar2=None, prior=None): """Instantiate a Parameter with a name and value at least. Parameters ---------- name : str The name of the parameter. value : float, int, str, list, tuple The value of the parameter. ptype : str The parameter type from ['free','fixed','independent','shared', 'white_free', 'white_fixed']. priorpar1 : float, int, str, list, tuple; optional The first prior input value: lower-bound for uniform/log uniform priors, or mean for normal priors. Defaults to None. priorpar2 : float, int, str, list, tuple; optional The second prior input value: upper-bound for uniform/log uniform priors, or std. dev. for normal priors. Defaults to None. prior : str; optional Type of prior, ['U','LU','N']. Defaults to None. """ # If value is a list, distribute the elements if isinstance(value, list): value, *other = value if len(other) > 1: ptype, *other = other if len(other) > 0: priorpar1, priorpar2 = other # Set the attributes self.name = name self.value = value self.priorpar1 = priorpar1 self.priorpar2 = priorpar2 self.ptype = ptype self.prior = prior def __str__(self): '''A function to nicely format some outputs when a Parameter object is converted to a string. This function gets used if one does str(param) or print(param). Returns ------- str A string representation of what is contained in the Parameter object. Notes ----- History: - Mar 2022 Taylor J Bell Initial version. ''' # Return a string representation of the self.values list return str(self.values) def __repr__(self): '''A function to nicely format some outputs when asked for a printable representation of the Parameter object. This function gets used if one does repr(param) or does just param in an interactive shell. Returns ------- str A string representation of what is contained in the Parameter object in a manner that could reproduce a similar Parameter object. Notes ----- History: - Mar 2022 Taylor J Bell Initial version. ''' # Get the fully qualified name of the class output = type(self).__module__+'.'+type(self).__qualname__+'(' # Get the list of Parameter.__init__ arguments (excluding self) keys = inspect.getfullargspec(Parameter.__init__).args[1:] # Show how the Parameter could be initialized # (e.g. name='rp', val=0.01, ptype='free') for name in keys: val = getattr(self, name) if isinstance(val, str): val = "'"+val+"'" else: val = str(val) output += name+'='+val+', ' # Remove the extra ', ' and close with a parenthesis output = output[:-2]+')' return output @property def ptype(self): """Getter for the ptype""" return self._ptype @ptype.setter def ptype(self, param_type): """Setter for ptype Parameters ---------- param_type : str Parameter type, ['free','fixed','independent','shared', 'white_free', 'white_fixed'] """ if param_type in [True, False]: raise ValueError("Boolean ptype values are deprecated. ptype must " "now be 'free', 'fixed', 'independent', " "'shared', 'white_free', oe 'white_fixed'") elif param_type not in ['free', 'fixed', 'independent', 'shared', 'white_free', 'white_fixed']: raise ValueError("ptype must be 'free', 'fixed', 'independent', " "'shared', 'white_free', or 'white_fixed'") self._ptype = param_type @property def values(self): """Return all values for this parameter Returns ------- list [self.name, self.value, self.ptype, self.priorpar1, self.priorpar2, self.prior] excluding any values which are None. """ vals = (self.name, self.value, self.ptype, self.priorpar1, self.priorpar2, self.prior) return list(filter(lambda x: x is not None, vals))
[docs]class Parameters: """A class to hold the Parameter instances This class loads a Eureka! Parameter File (epf) and lets you query the parameters and values. Notes ----- History: - 2022-03-24 Taylor J Bell Based on readECF with significant edits for Eureka """ def __init__(self, param_path=None, param_file=None, **kwargs): """Initialize the parameter object Parameters ---------- param_path : str; optional The path to the parameters. Defaults to None. param_file : str; optional A text file of the parameters to parse. Defaults to None. **kwargs : dict Any additional settings to set in the Parameters object. """ if param_path is None: param_path = '.'+os.sep # Make an empty params dict self.params = {} self.dict = {} # If a param_file is given, make sure it exists param_file_okay = (param_file is not None and param_path is not None) if param_file_okay: if not os.path.exists(os.path.join(param_path, param_file)): raise FileNotFoundError( f"The Eureka! Parameter File:\n" f"{os.path.join(param_path,param_file)}\n" f"does not exist. Make sure to update the fit_par setting" f" in your Stage 5 ECF to point to the EPF file you've " f"made.") elif param_file.endswith('.txt') or param_file.endswith('.json'): raise AssertionError( 'ERROR: S5 parameter files in txt or json file formats ' 'have been deprecated.\n' 'Please change to using EPF (Eureka! Parameter File) ' 'file formats.') elif param_file.endswith('.ecf'): print('WARNING, using ECF file formats for S5 parameter files ' 'has been deprecated.') print('Please update the file format to an EPF (Eureka! ' 'Parameter File; .epf).') self.read(param_path, param_file) # Add any kwargs to the parameter dict self.params.update(kwargs) # Try to store each as an attribute for param, value in self.params.items(): setattr(self, param, value) def __str__(self): '''A function to nicely format some outputs when a Parameters object is converted to a string. This function gets used if one does str(params) or print(params). Returns ------- str A string representation of what is contained in the Parameters object. Notes ----- History: - Mar 2022 Taylor J Bell Initial version. ''' output = '' for key in self.params: # For each parameter, format a line as "Name: Value" output += key+': '+str(getattr(self, key))+'\n' return output[:-1] def __repr__(self): '''A function to nicely format some outputs when asked for a printable representation of the Parameters object. This function gets used if one does repr(params) or does just params in an interactive shell. Returns ------- str A string representation of what is contained in the Parameters object in a manner that could reproduce a similar Parameters object. Notes ----- History: - Mar 2022 Taylor J Bell Initial version. ''' # Get the fully qualified name of the class output = type(self).__module__+'.'+type(self).__qualname__+'(' # Show what folder and file were used to read in an EPF output += f"param_path='{self.folder}', param_file='{self.filename}', " # Show what values have been loaded into the params dictionary output += "**"+str(self.params) output = output+')' return output def __setattr__(self, item, value): """Maps attributes to values Parameters ---------- item : str The name for the attribute value : any The attribute value """ if item in ['epf', 'params', 'dict', 'filename', 'folder', 'lines', 'cleanlines']: self.__dict__[item] = value return if isinstance(value, (str, float, int, bool)): # Convert single items to list value = [value, ] elif isinstance(value, tuple): # Convert tuple to list value = list(value) elif not isinstance(value, list): raise TypeError("Cannot set {}={}.".format(item, value)) # Set the attribute self.__dict__[item] = Parameter(item, *value) # Add it to the list of parameters self.__dict__['dict'][item] = self.__dict__[item].values[1:] def __add__(self, other): """Add parameters to make a combined model Parameters ---------- other : Parameters The parameters object to combine Returns ------- newParams : Parameters The combined model """ # Make sure it is the right type if not type(self) == type(other): raise TypeError('Only another Parameters instance may be added.') # Combine the model parameters too kwargs = self.dict kwargs.update(other.dict) newParams = Parameters(**kwargs) return newParams
[docs] def read(self, folder, file): """A function to read EPF files Parameters ---------- folder : str The folder containing an EPF file to be read in. file : str The EPF filename to be read in. Notes ----- History: - Mar 2022 Taylor J Bell Initial Version based on old readECF code. """ self.filename = file self.folder = folder # Read the file with open(os.path.join(folder, file), 'r') as file: self.lines = file.readlines() cleanlines = [] # list with only the important lines # Clean the lines: for line in self.lines: # Strip off comments: if "#" in line: line = line[0:line.index('#')] line = line.strip() line = ' '.join(line.split()) # Keep only useful lines: if len(line) > 0: cleanlines.append(line) self.params = {} for line in cleanlines: par = np.array(line.split()) name = par[0] vals = [] for i in range(len(par[1:])): try: vals.append(eval(par[i+1])) except: # FINDME: Need to catch only the expected exception vals.append(par[i+1]) self.params[name] = vals
[docs] def write(self, folder): """A function to write an EPF file based on the current Parameters settings. NOTE: For now this only rewrites the input EPF file to a new EPF file in the requested folder. In the future this function should make a full EPF file based on any adjusted parameters. Parameters ---------- folder : str The folder where the EPF file should be written. Notes ----- History: - Mar 2022 Taylor J Bell Initial Version. """ with open(os.path.join(folder, self.filename), 'w') as file: file.writelines(self.lines)