Source code for pint.models.parameter

# parameter.py
# Defines Parameter class for timing model parameters
from ..utils import fortran_float, time_from_mjd_string, time_to_mjd_string,\
    time_to_longdouble, is_number, time_from_longdouble, str2longdouble, \
    longdouble2string, data2longdouble, split_prefixed_name
import numpy
import astropy.time as time
from astropy import log
from pint import pint_units
import astropy.units as u
import astropy.constants as const
from astropy.coordinates.angles import Angle
import re
import numbers
from . import priors
from ..toa_select import TOASelect


[docs]class Parameter(object): """A base PINT class describing a single timing model parameter. PINT Parameter class will have A `Parameter` object can be created with one of the subclasses provided by `PINT` depending on the parameter usage. Current Parameter type: [`floatParameter`, `strParameter`, `boolParameter`, `MDJParameter`, `AngleParameter`, `prefixParameter`, `maskParameter`] Parameter Mechanism Parameter current value information will be stored at `.quantity` property which can be a flexible format, for example astropy.quantity in floatParameter and string in strParameter, (For more detail see Parameter subclasses docstrings). If applicable, Parameter default unit is stored at`.units` property which is an `astropy.unit` object. Property `.value` always returns a pure value associate with `.units` from `.quantity`. `.uncertainty` provides the storage for parameter uncertainty and `.uncertainty_value` for pure uncertainty value. Like `.value`, `.uncertainty_value` always associate with default unit. Parameters ---------- name : str, optional The name of the parameter. value : number, str, `Astropy.units.Quantity` object, or other data type or object The input parameter value. units : str or Astropy.units, optional Parameter default unit. Parameter .value and .uncertainty_value attribute will associate with the default units. description : str, optional A short description of what this parameter means. uncertainty : number Current uncertainty of the value. frozen : bool, optional A flag specifying whether "fitters" should adjust the value of this parameter or leave it fixed. aliases : list, optional An optional list of strings specifying alternate names that can also be accepted for this parameter. continuous : bool, optional A flag specifying whether phase derivatives with respect to this parameter exist. print_quantity : method, optional A function that converts the internal value to a string for output. set_quantity : method, optional A function that sets the quantity property get_value: A function that get purely value from quantity attribute Attributes ---------- quantity: Type depends on the parameter subclass, it can be anything An internal storage for parameter value and units """ def __init__(self, name=None, value=None, units=None, description=None, uncertainty=None, frozen=True, aliases=None, continuous=True, print_quantity=str, set_quantity=lambda x: x, get_value=lambda x: x, prior=priors.Prior(priors.UniformUnboundedRV()), set_uncertainty=fortran_float): self.name = name # name of the parameter self.units = units # Default unit self.set_quantity = set_quantity # Method to get value self.get_value = get_value # method to convert quantity to a string. self.print_quantity = print_quantity # Method to get uncertainty from input self.set_uncertainty = set_uncertainty self.from_parfile_line = self.from_parfile_line_regular self.quantity = value # The value of parameter, internal storage self.prior = prior self.description = description self.uncertainty = uncertainty self.frozen = frozen self.continuous = continuous self.aliases = [] if aliases is None else aliases self.is_prefix = False self.paramType = 'Not specified' # Type of parameter. Here is general type self.valueType = None self.special_arg = [] @property def prior(self): return self._prior @prior.setter def prior(self,p): if not isinstance(p,priors.Prior): log.error("prior must be an instance of Prior()") self._prior = p # Setup units property @property def units(self): return self._units @units.setter def units(self, unt): # Check if this is the first time set units and check compatibility if hasattr(self, 'quantity'): if self.units is not None: if unt != self.units: wmsg = 'Parameter '+self.name+' default units has been ' wmsg += ' reset to ' + str(unt) + ' from '+ str(self._units) log.warning(wmsg) try: if hasattr(self.quantity, 'unit'): _ = self.quantity.to(unt) except: log.warning('The value unit is not compatible with' ' parameter units right now.') if unt is None: self._units = None # Always compare a string to pint_units.keys() # If search an astropy unit object with a sting list # If the string does not match astropy unit, astropy will guess what # does the string mean. It will take a lot of time. elif isinstance(unt, str) and unt in pint_units.keys(): # These are special-case unit strings in in PINT self._units = pint_units[unt] else: # Try to use it as an astopy unit. If this fails, # ValueError will be raised. self._units = u.Unit(unt) if hasattr(self, 'quantity') and hasattr(self.quantity, 'unit'): # Change quantity unit to new unit self.quantity = self.quantity.to(self._units) if hasattr(self, 'uncertainty') and hasattr(self.uncertainty, 'unit'): # Change uncertainty unit to new unit self.uncertainty = self.uncertainty.to(self._units) # Setup quantity property @property def quantity(self): """Return the internal stored parameter value and units. """ return self._quantity @quantity.setter def quantity(self, val): """General wrapper method to set .quantity. For different type of parameters, the setter method is stored at .set_quantity attribute. """ if val is None: if hasattr(self, 'quantity') and self.quantity is not None: raise ValueError('Setting an exist value to None is not' ' allowed.') else: self._quantity = val return self._quantity = self.set_quantity(val)
[docs] def prior_pdf(self,value=None, logpdf=False): """Return the prior probability, evaluated at the current value of the parameter, or at a proposed value. Parameters ---------- value : array_like or float_like Probabilities are evaluated using the value attribute """ if value is None: return self.prior.pdf(self.value) if not logpdf else self.prior.logpdf(self.value) else: return self.prior.pdf(value) if not logpdf else self.prior.logpdf(value)
# Setup .value property # .value will get pure number from ._quantity. # Setting .value property will change ._quantity. @property def value(self): """Return the pure value of a parameter. This value will associate with parameter default value, which is .units attribute. """ if self._quantity is None: return None else: return self.get_value(self._quantity) @value.setter def value(self, val): """Method to set .value. Setting .value attribute will change the .quantity attribute other than .value attribute. """ if val is None: if not isinstance(self.quantity, (str, bool)) and \ self._quantity is not None: raise ValueError('This parameter value is number convertible. ' 'Setting .value to None will lost the ' 'parameter value.') else: self.value = val self._quantity = self.set_quantity(val) @property def uncertainty(self): """Return the internal stored parameter uncertainty value and units. """ return self._uncertainty @uncertainty.setter def uncertainty(self, val): """General wrapper setter for uncertainty. The setting method is stored at .set_uncertainty attribute """ if val is None: if hasattr(self, 'uncertainty') and self.uncertainty is not None: raise ValueError('Setting an exist uncertainty to None is not' ' allowed.') else: self._uncertainty = val self._uncertainty_value = self._uncertainty return self._uncertainty = self.set_uncertainty(val) # This is avoiding negtive unvertainty input. if self._uncertainty is not None and self.uncertainty_value < 0: self.uncertainty_value = numpy.abs(self.uncertainty_value) @property def uncertainty_value(self): """Return a pure value from .uncertainty. The unit will associate with .units """ if self._uncertainty is None: return None else: return self.get_value(self._uncertainty) @uncertainty_value.setter def uncertainty_value(self, val): """Setter for uncertainty_value. Setting .uncertainty_value will only change the .uncertainty attribute. """ if val is None: if not isinstance(self.uncertainty, (str, bool)) and \ self._uncertainty_value is not None: log.warning('This parameter has uncertainty value. ' 'Change it to None will lost information.') else: self.uncertainty_value = val self._uncertainty = self.set_uncertainty(val)
[docs] def print_uncertainty(self, uncertainty): return str(uncertainty.to(self.units).value)
def __str__(self): out = self.name if self.units is not None: out += " (" + str(self.units) + ")" if self.quantity is not None: out += " " + self.print_quantity(self.quantity) else: out += " " + "UNSET" return out if self.uncertainty is not None and isinstance(self.value, numbers.Number): out += " +/- " + str(self.uncertainty.to(self.units)) return out
[docs] def set(self, value): """Parses a string 'value' into the appropriate internal representation of the parameter. """ self.value = value
[docs] def add_alias(self, alias): """Add a name to the list of aliases for this parameter.""" self.aliases.append(alias)
[docs] def help_line(self): """Return a help line containing parameter name, description and units.""" out = "%-12s %s" % (self.name, self.description) if self.units is not None: out += ' (' + str(self.units) + ')' return out
[docs] def as_parfile_line(self): """Return a parfile line giving the current state of the parameter.""" # Don't print unset parameters if self.quantity is None: return "" line = "%-15s %25s" % (self.name, self.print_quantity(self.quantity)) if self.uncertainty is not None: line += " %d %s" % (0 if self.frozen else 1, str(self.uncertainty_value)) elif not self.frozen: line += " 1" return line + "\n"
[docs] def from_parfile_line_regular(self, line): """ Parse a parfile line into the current state of the parameter. Returns True if line was successfully parsed, False otherwise. Notes ----- The accepted format: NAME value NAME value fit_flag NAME value fit_flag uncertainty NAME value uncertainty """ try: k = line.split() name = k[0].upper() except IndexError: return False # Test that name matches if not self.name_matches(name): return False if len(k) < 2: return False if len(k) >= 2: self.set(k[1]) if len(k) >= 3: try: fit_flag = int(k[2]) if fit_flag == 0: self.frozen = True ucty = 0.0 elif fit_flag == 1: self.frozen = False ucty = 0.0 else: ucty = fit_flag except: if is_number(k[2]): ucty = k[2] else: errmsg = 'Unidentified string ' + k[2] + ' in' errmsg += ' parfile line ' + k raise ValueError(errmsg) if len(k) >= 4: ucty = k[3] self.uncertainty = self.set_uncertainty(ucty) return True
[docs] def name_matches(self, name): """Whether or not the parameter name matches the provided name """ return (name == self.name) or (name in self.aliases)
[docs]class floatParameter(Parameter): """This is a Parameter type that is specific to the parameters has a float/ float128 quantity as its value. `.quantity` stores current parameter value and its unit in an `astropy.units.quantity` class. The unit of `.quantity` can be any unit that convertible to default unit. Parameters ---------- name : str The name of the parameter. value : number, str, `Astropy.units.Quantity` object, The input parameter float value. units : str or Astropy.units Parameter default unit. Parameter .value and .uncertainty_value attribute will associate with the default units. If unit is dimensionless, use "''" as its unit. description : str, optional A short description of what this parameter means. uncertainty : number Current uncertainty of the value. frozen : bool, optional A flag specifying whether "fitters" should adjust the value of this parameter or leave it fixed. aliases : list, optional An optional list of strings specifying alternate names that can also be accepted for this parameter. continuous : bool, optional, default True A flag specifying whether phase derivatives with respect to this parameter exist. long_double : bool, optional, default False A flag specifying whether value is float or float128/longdouble. Example:: >>> from parameter import floatParameter >>> test = floatParameter(name='test1', value=100.0, units='second') >>> print test test1 (s) 100.0 """ def __init__(self, name=None, value=None, units=None, description=None, uncertainty=None, frozen=True, aliases=[], continuous=True, long_double=False, **kwargs): self.long_double = long_double set_quantity = self.set_quantity_float print_quantity = self.print_quantity_float get_value = self.get_value_float set_uncertainty = self.set_quantity_float super(floatParameter, self).__init__(name=name, value=value, units=units, frozen=True, aliases=aliases, continuous=continuous, description=description, uncertainty=uncertainty, print_quantity=print_quantity, set_quantity=set_quantity, get_value=get_value, set_uncertainty=set_uncertainty) self.paramType = 'floatParameter' self.special_arg += ['long_double',] @property def long_double(self): return self._long_double @long_double.setter def long_double(self, val): """long double setter, if a floatParameter's longdouble flag has been changed, `.quantity` will get reset in order to get to the right data type. """ if not isinstance(val, bool): raise ValueError("long_double property can only be set as boolean" " type") if hasattr(self, 'long_double'): if self._long_double != val and hasattr(self, 'quantity'): if not val: log.warning("Setting floatParameter from long double to float," " precision will be lost.") # Reset quantity to its asked type self._long_double = val self.quantity = self.quantity else: self._long_double = val
[docs] def set_quantity_float(self, val): """Set value method specific for float parameter accept format 1. Astropy quantity 2. float 3. string """ # Check long_double if not self._long_double: setfunc_with_unit = lambda x: x setfunc_no_unit = lambda x: fortran_float(x) * self.units else: setfunc_with_unit = lambda x: data2longdouble(x.value)*x.unit setfunc_no_unit = lambda x: data2longdouble(x) * self.units # First try to use astropy unit conversion try: # If this fails, it will raise UnitConversionError _ = val.to(self.units) result = setfunc_with_unit(val) except AttributeError: # This will happen if the input value did not have units result = setfunc_no_unit(val) return result
[docs] def print_quantity_float(self, quan): """A function gives print quantity string. """ if not self._long_double: result = str(quan.to(self.units).value) else: result = longdouble2string(quan.to(self.units).value) return result
[docs] def get_value_float(self, quan): if quan is None: return None else: return quan.to(self.units).value
[docs]class strParameter(Parameter): """This is a Parameter type that is specific to string values. `.quantity` stores current parameter information in a string. `.value` returns the same with `.quantity`. `.units` is not applicable. `strParameter` is not fitable. Parameter --------- name : str The name of the parameter. value : str The input parameter string value. description : str, optional A short description of what this parameter means. aliases : list, optional An optional list of strings specifying alternate names that can also be accepted for this parameter. Example:: >>> from parameter import strParameter >>> test = strParameter(name='test1', value='This is a test',) >>> print test test1 This is a test """ def __init__(self, name=None, value=None, description=None, aliases=[], **kwargs): print_quantity = str get_value = lambda x: x set_quantity = lambda x: str(x) set_uncertainty = lambda x: None super(strParameter, self).__init__(name=name, value=value, description=None, frozen=True, aliases=aliases, print_quantity=print_quantity, set_quantity=set_quantity, get_value=get_value, set_uncertainty=set_uncertainty) self.paramType = 'strParameter' self.value_type = str
[docs]class boolParameter(Parameter): """This is a Parameter type that is specific to boolean values. `.quantity` stores current parameter information in boolean type. `.value` returns the same with `.quantity`. `.units` is not applicable. `boolParameter` is not fitable. Parameter --------- name : str The name of the parameter. value : str, bool, [0,1] The input parameter boolean value. description : str, optional A short description of what this parameter means. aliases : list, optional An optional list of strings specifying alternate names that can also be accepted for this parameter. Example:: >>> from parameter import boolParameter >>> test = boolParameter(name='test1', value='N') >>> print test test1 N """ def __init__(self, name=None, value=None, description=None, frozen=True, aliases=[], **kwargs): print_quantity = lambda x: 'Y' if x else 'N' set_quantity = self.set_quantity_bool get_value = lambda x: x set_uncertainty = lambda x: None super(boolParameter, self).__init__(name=name, value=value, description=None, frozen=True, aliases=aliases, print_quantity=print_quantity, set_quantity=set_quantity, get_value=get_value, set_uncertainty=set_uncertainty) self.value_type = bool self.paramType = 'boolParameter'
[docs] def set_quantity_bool(self, val): """ This function is to get boolean value for boolParameter class """ # First try strings try: if val.upper() in ['Y','YES','T','TRUE','1']: return True else: return False except AttributeError: # Will get here on non-string types return bool(val)
[docs]class MJDParameter(Parameter): """This is a Parameter type that is specific to MJD values. `.quantity` stores current parameter information in an `astropy.Time` type in the format of MJD. `.value` returns the pure MJD value. `.units` is in day as default unit. Parameter --------- name : str The name of the parameter. value : astropy Time, str, float in mjd, str in mjd. The input parameter MJD value. description : str, optional A short description of what this parameter means. uncertainty : number Current uncertainty of the value. frozen : bool, optional A flag specifying whether "fitters" should adjust the value of this parameter or leave it fixed. continuous : bool, optional, default True A flag specifying whether phase derivatives with respect to this parameter exist. aliases : list, optional An optional list of strings specifying alternate names that can also be accepted for this parameter. time_scale : str, optional, default 'utc' MJD parameter time scale. Example:: >>> from parameter import MJDParameter >>> test = MJDParameter(name='test1', value='54000', time_scale='utc') >>> print test test1 (d) 54000.000000000000000 """ def __init__(self, name=None, value=None, description=None, uncertainty=None, frozen=True, continuous=True, aliases=[], time_scale='utc', **kwargs): self.time_scale = time_scale set_quantity = self.set_quantity_mjd print_quantity = time_to_mjd_string get_value = time_to_longdouble set_uncertainty = self.set_uncertainty_mjd super(MJDParameter, self).__init__(name=name, value=value, units="MJD", description=description, uncertainty=uncertainty, frozen=frozen, continuous=continuous, aliases=aliases, print_quantity=print_quantity, set_quantity=set_quantity, get_value=get_value, set_uncertainty=set_uncertainty) self.value_type = time.Time self.paramType = 'MJDParameter' self.special_arg += ['time_scale',] @property def uncertainty_value(self): """Return a pure value from .uncertainty. The unit will associate with .units """ if self._uncertainty is None: return None else: return self._uncertainty.value @uncertainty_value.setter def uncertainty_value(self, val): """Setter for uncertainty_value. Setting .uncertainty_value will only change the .uncertainty attribute. """ if val is None: if not isinstance(self.uncertainty, (str, bool)) and \ self._uncertainty_value is not None: log.warning('This parameter has uncertainty value. ' 'Change it to None will lost information.') else: self.uncertainty_value = val self._uncertainty = self.set_uncertainty(val)
[docs] def set_quantity_mjd(self, val): """Value setter for MJD parameter, Accepted format: Astropy time object mjd float mjd string """ if isinstance(val, numbers.Number): val = numpy.longdouble(val) result = time_from_longdouble(val, self.time_scale) elif isinstance(val, str): try: result = time_from_mjd_string(val, self.time_scale) except: raise ValueError('String ' + val + 'can not be converted to' 'a time object.' ) elif isinstance(val,time.Time): result = val else: raise ValueError('MJD parameter can not accept ' + type(val).__name__ + 'format.') return result
[docs] def set_uncertainty_mjd(self, val): # First try to use astropy unit conversion try: # If this fails, it will raise UnitConversionError _ = val.to(self.units) result = data2longdouble(val.value) * self.units except AttributeError: # This will happen if the input value did not have units result = data2longdouble(val) * self.units return result
[docs] def print_uncertainty(self, uncertainty): return longdouble2string(self.uncertainty_value)
[docs]class AngleParameter(Parameter): """This is a Parameter type that is specific to Angle values. `.quantity` stores current parameter information in an `astropy Angle` type. `.value` returns the pure angle value associate with default unit. `.units` currently can accept angle format {'h:m:s': u.hourangle, 'd:m:s': u.deg, 'rad': u.rad, 'deg': u.deg} Parameter --------- name : str The name of the parameter. value : angle string, float, astropy angle object The input parameter angle value. description : str, optional A short description of what this parameter means. uncertainty : number Current uncertainty of the value. frozen : bool, optional A flag specifying whether "fitters" should adjust the value of this parameter or leave it fixed. continuous : bool, optional, default True A flag specifying whether phase derivatives with respect to this parameter exist. aliases : list, optional An optional list of strings specifying alternate names that can also be accepted for this parameter. Example:: >>> from parameter import AngleParameter >>> test = AngleParameter(name='test1', value='12:20:10', units='H:M:S') >>> print test test1 (hourangle) 12:20:10.00000000 """ def __init__(self, name=None, value=None, description=None, units='rad', uncertainty=None, frozen=True, continuous=True, aliases=[],**kwargs): self._str_unit = units self.unit_identifier = { 'h:m:s': (u.hourangle, 'h', '0:0:%.15fh'), 'd:m:s': (u.deg, 'd', '0:0:%.15fd'), 'rad': (u.rad, 'rad', '%.15frad'), 'deg': (u.deg, 'deg', '%.15fdeg'), } # Check unit format if units.lower() not in self.unit_identifier.keys(): raise ValueError('Unidentified unit ' + units) self.unitsuffix = self.unit_identifier[units.lower()][1] set_quantity = self.set_quantity_angle print_quantity = lambda x: x.to_string(sep=':', precision=8) \ if x.unit != u.rad else x.to_string(decimal = True, precision=8) #get_value = lambda x: Angle(x * self.unit_identifier[units.lower()][0]) get_value = lambda x: x.value set_uncertainty = self.set_uncertainty_angle self.value_type = Angle self.paramType = 'AngleParameter' super(AngleParameter, self).__init__(name=name, value=value, units=units, description=description, uncertainty=uncertainty, frozen=frozen, continuous=continuous, aliases=aliases, print_quantity=print_quantity, set_quantity=set_quantity, get_value=get_value, set_uncertainty=set_uncertainty)
[docs] def set_quantity_angle(self, val): """ This function is to set value to angle parameters. Accepted format: 1. Astropy angle object 2. float 3. number string """ if isinstance(val, numbers.Number): result = Angle(val * self.units) elif isinstance(val, str): result = Angle(val + self.unitsuffix) elif hasattr(val, 'unit'): result = Angle(val.to(self.units)) else: raise ValueError('Angle parameter can not accept ' + type(val).__name__ + 'format.') return result
[docs] def set_uncertainty_angle(self, val): """This function is to set the uncertainty for an angle parameter. """ if isinstance(val, numbers.Number): result =Angle(self.unit_identifier[self._str_unit.lower()][2] % val) elif isinstance(val, str): result =Angle(self.unit_identifier[self._str_unit.lower()][2] \ % fortran_float(val)) #except: # raise ValueError('Srting ' + val + ' can not be converted to' # ' astropy angle.') elif hasattr(val, 'unit'): result = Angle(val.to(self.units)) else: raise ValueError('Angle parameter can not accept ' + type(val).__name__ + 'format.') return result
[docs]class prefixParameter(object): """ This is a Parameter type for prefix parameters, for example DMX_ Create a prefix parameter, is like create a normal parameter. But the name should be in the format of prefix and index. For example DMX_0001 or F22. To create a prefix parameter with the same prefix but different index, just use the `.new_param` method. It will return a new prefix parameter with the same setup but the index. Some parameters' unit and description will be changed once the index has been changed. In order to get the right units and description, `.unitTplt` and `.descriptionTplt` should be provided. If not the new prefix parameter will use the same units and description with the old one. A typical description and units template is like: >>> descritionTplt = lambda x: 'This is the description of parameter %d'%x >>> unitTplt = lambda x: 'second^%d'%x Parameter --------- name : str optional The name of the parameter. It has to be in the format of prefix + index. units : str optional The unit of parameter unitTplt : lambda method The unit template for prefixed parameter description : str optional Description for the parameter descriptionTplt : lambda method optional Description template for prefixed parameters prefix_aliases : list of str optional Alias for the prefix frozen : bool, optional A flag specifying whether "fitters" should adjust the value of this parameter or leave it fixed. continuous : bool parameter_type : str, optional, default 'float' Example parameter class template for quantity and value setter long_double : bool, optional default 'double' Set float type quantity and value in numpy float128 time_scale : str, optional default 'utc' Time scale for MJDParameter class. """ def __init__(self, parameter_type='float',name=None, value=None, units=None, unitTplt=None, description=None, descriptionTplt=None, uncertainty=None, frozen=True, continuous=True, prefix_aliases=[],long_double=False, time_scale='utc', **kwargs): # Split prefixed name, if the name is not in the prefixed format, error # will be raised self.name = name self.prefix, self.idxfmt, self.index = split_prefixed_name(name) # Type identifier self.type_mapping = {'float': floatParameter, 'str': strParameter, 'bool': boolParameter, 'mjd': MJDParameter, 'angle': AngleParameter} self.parameter_type = parameter_type try: self.param_class = self.type_mapping[self.parameter_type.lower()] except KeyError: raise ValueError("Unknow parameter type '"+ parameter_type + "' ") # Set up other attributes in the wrapper class self.unit_template = unitTplt self.description_template = descriptionTplt input_units = units input_description = description self.prefix_aliases = prefix_aliases # set templates, the templates should be a lambda function and input is # the index of prefix parameter. if self.unit_template is None: self.unit_template = lambda x: input_units if self.description_template is None: self.description_template = lambda x: input_description # Set the description and units for the parameter compostion. real_units = self.unit_template(self.index) real_description = self.description_template(self.index) aliases = [] for pa in prefix_aliases: aliases.append(pa + self.idxfmt) # initiate parameter class self.param_comp = self.param_class(name=self.name, value=value, units=real_units, description=real_description, uncertainty=uncertainty, frozen=frozen, continuous=continuous, aliases=aliases, long_double=long_double, time_scale=time_scale) self.is_prefix = True # Define prpoerties for access the parameter composition @property def units(self): return self.param_comp.units @units.setter def units(self, unt): self.param_comp.units = unt @property def quantity(self): return self.param_comp.quantity @quantity.setter def quantity(self, qnt): self.param_comp.quantity = qnt @property def value(self): return self.param_comp.value @value.setter def value(self, val): self.param_comp.value = val @property def uncertainty(self): return self.param_comp.uncertainty @uncertainty.setter def uncertainty(self, ucty): self.param_comp.uncertainty = ucty @property def uncertainty_value(self): return self.param_comp.uncertainty_value @uncertainty_value.setter def uncertainty_value(self, val): self.param_comp.uncertainty_value = val @property def prior(self): return self.param_comp.prior @prior.setter def prior(self,p): self.param_comp.prior = p @property def aliases(self): return self.param_comp.aliases @aliases.setter def aliases(self,a): self.param_comp.aliases = a @property def continuous(self): return self.param_comp.continuous @continuous.setter def continuous(self, val): self.param_comp.continuous = val @property def frozen(self): return self.param_comp.frozen @frozen.setter def frozen(self, val): self.param_comp.frozen = val @property def description(self): return self.param_comp.description @description.setter def description(self, val): self.param_comp.description = val @property def special_arg(self): return self.param_comp.special_arg def __str__(self): out = self.name if self.units is not None: out += " (" + str(self.units) + ")" out += " " + self.print_quantity(self.quantity) if self.uncertainty is not None: out += " +/- " + str(self.uncertainty.to(self.units)) return out # Define the function to call functions inside of parameter composition. def __str__(self): return self.param_comp.__str__()
[docs] def from_parfile_line(self, line): return self.param_comp.from_parfile_line(line)
[docs] def prior_pdf(self,value=None, logpdf=False): return self.param_comp.prior_pdf(value, logpdf)
[docs] def print_quantity(self, quantity): return self.param_comp.print_quantity(quantity)
[docs] def name_matches(self, name): return self.param_comp.name_matches(name)
[docs] def as_parfile_line(self): return self.param_comp.as_parfile_line()
[docs] def help_line(self): return self.param_comp.help_line()
[docs] def prefix_matches(self, prefix): return (prefix == self.perfix) or (prefix in self.prefix_aliases)
[docs] def new_param(self, index): """Get one prefix parameter with the same type. Parameter ---------- index : int index of prefixed parameter. Return ---------- A prefixed parameter with the same type of instance. """ new_name = self.prefix + format(index, '0'+ str(len(self.idxfmt))) kws = dict() for key in ['units', 'unitTplt', 'description','descriptionTplt', 'frozen', 'continuous', 'prefix_aliases', 'long_double', 'time_scale', 'parameter_type']: if hasattr(self, key): kws[key] = getattr(self, key) newpfx = prefixParameter(name=new_name, **kws) return newpfx
[docs]class maskParameter(floatParameter): """ This is a Parameter type for mask parameters which is to select a certain subset of TOAs and apply changes on the subset of TOAs, for example JUMP. This type of parameter does not require index input. But eventrully an index part will be added, for the purpose of parsing the right value from the parfile. For example, >>> p = maskParameter(name='JUMP', index=2) >>> p.name 'JUMP2' Parameter --------- name : str optional The name of the parameter. index : int optional [default 1] The index number for the prefixed parameter. key : str optional The key words/flag for the selecting TOAs key_value : list/single value optional The value for key words/flags. Value can take one value as a flag value. or two value as a range. e.g. JUMP freq 430.0 1440.0. or JUMP -fe G430 value : float or long_double optinal Toas/phase adjust value long_double : bool, optional default 'double' Set float type quantity and value in numpy float128 units : str optional Unit for the offset value description : str optional Description for the parameter uncertainty: float/longdouble uncertainty of the parameter. frozen : bool, optional A flag specifying whether "fitters" should adjust the value of this parameter or leave it fixed. continuous : bool optional aliases : list optional List of aliases for parameter name. TODO: Is mask parameter provide some other type of parameters other then floatParameter? """ def __init__(self, name=None, index=1, key=None, key_value=None, value=None, long_double=False, units= None, description=None, uncertainty=None, frozen=True, continuous=False, aliases=[]): self.is_mask = True self.key_identifier = {'mjd': (lambda x: time.Time(x, format='mjd'), 2), 'freq': (float, 2), 'name': (str, 1), 'tel': (str, 1)} if key_value is None: key_value = [] elif not isinstance(key_value, list): key_value = [key_value] # Check key and key value if key is not None \ and key.lower() in self.key_identifier.keys(): key_info = self.key_identifier[key.lower()] if len(key_value) != key_info[1]: errmsg = "key " + key + " takes " + key_info[1] + \ " element." raise ValueError(errmsg) self.key = key self.key_value = key_value self.index = index name_param = name + str(index) self.origin_name = name super(maskParameter, self).__init__(name=name_param, value=value, units=units, description=description, uncertainty=uncertainty, frozen=frozen, continuous=continuous, aliases=aliases, long_double=long_double) # For the first mask parameter, add name to aliases for the reading # first mask parameter from parfile. if index == 1: self.aliases.append(name) self.from_parfile_line = self.from_parfile_line_mask self.as_parfile_line = self.as_parfile_line_mask def __str__(self): out = self.name if self.units is not None: out += " (" + str(self.units) + ")" out += " " + self.key for kv in self.key_value: out += " " + str(kv) if self.quantity is not None: out += " " + self.print_quantity(self.quantity) else: out += " " + "UNSET" return out if self.uncertainty is not None and isinstance(self.value, numbers.Number): out += " +/- " + str(self.uncertainty.to(self.units)) return out
[docs] def from_parfile_line_mask(self, line): """ This is a method to read mask parameter line (e.g. JUMP) Notes ----- The accepted format: NAME key key_value parameter_value NAME key key_value parameter_value fit_flag NAME key key_value parameter_value fit_flag uncertainty NAME key key_value parameter_value uncertainty NAME key key_value1 key_value2 parameter_value NAME key key_value1 key_value2 parameter_value fit_flag NAME key key_value1 key_value2 parameter_value fit_flag uncertainty NAME key key_value1 key_value2 parameter_value uncertainty """ try: k = line.split() name = k[0].upper() except IndexError: return False # Test that name matches if not self.name_matches(name): return False try: self.key = k[1] except IndexError: return False key_value_info = self.key_identifier.get(self.key.lower(), (str, 1)) len_key_v = key_value_info[1] if len(k) < 3 + len_key_v: return False for ii in range(len_key_v): if key_value_info[0] != str: try: kval = float(k[2 + ii]) except: kval = k[2 + ii] else: kval = k[2 + ii] if ii > len(self.key_value)-1: self.key_value.append(key_value_info[0](kval)) else: self.key_value[ii] = key_value_info[0](kval) if len(k) >= 3 + len_key_v: self.set(k[2 + len_key_v]) if len(k) >= 4 + len_key_v: try: fit_flag = int(k[3 + len_key_v]) if fit_flag == 0: self.frozen = True ucty = 0.0 elif fit_flag == 1: self.frozen = False ucty = 0.0 else: ucty = fit_flag except: if is_number(k[3 + len_key_v]): ucty = k[3 + len_key_v] else: errmsg = 'Unidentified string ' + k[3 + len_key_v] + ' in' errmsg += ' parfile line ' + k raise ValueError(errmsg) if len(k) >= 5 + len_key_v: ucty = k[4 + len_key_v] self.uncertainty = self.set_uncertainty(ucty) return True
[docs] def as_parfile_line_mask(self): if self.quantity is None: return "" line = "%-15s %s " % (self.origin_name, self.key) for kv in self.key_value: if not isinstance(kv, time.Time): line += "%s " % kv else: line += "%s " % time_to_mjd_string(kv) line += "%25s" % self.print_quantity(self.quantity) if self.uncertainty is not None: line += " %d %s" % (0 if self.frozen else 1, str(self.uncertainty_value)) elif not self.frozen: line += " 1" return line + "\n"
[docs] def new_param(self, index): """Create a new but same style mask parameter """ new_mask_param = maskParameter(name=self.origin_name, index=index, long_double=self.long_double, units= self.units, aliases=[]) return new_mask_param
[docs] def select_toa_mask(self, toas): """Select the toas. Parameter ---------- toas : toas table Return ---------- A mask array. the select toas are masked as True. """ self.toa_select = TOASelect(self.key, self.key_value) return self.toa_select.get_toa_key_mask(toas)