def build_model(self, parfile=None, name=''): """Read parfile using the model_instance attribute. Parameters --------- name: str, optional The name for the timing model parfile : str optional The parfile name """ if parfile is not None: self.get_comp_from_parfile(parfile) sorted_comps = self.sort_components() self.timing_model = TimingModel(name, sorted_comps) param_inModel = self.timing_model.get_params_mapping() # Find unrecognised parameters in par file. if self.param_inparF is not None: parName = [] # add aliases for p in list(param_inModel.keys()): parName+= getattr(self.timing_model, p).aliases parName += param_inModel.keys() for pp in self.param_inparF.keys(): if pp not in parName: self.param_unrecognized[pp] = self.param_inparF[pp] for ptype in ['prefixParameter', 'maskParameter']: prefix_param = \ self.search_prefix_param( \ list(self.param_unrecognized.keys()),self.timing_model, ptype) prefix_in_model = self.timing_model.get_params_of_type(ptype) for key in prefix_param.keys(): ppnames = [x for x in prefix_in_model if x.startswith(key)] for ppn in ppnames: pfx, idxs, idxv = split_prefixed_name(ppn) if pfx == key: exm_par = getattr(self.timing_model, ppn) else: continue exm_par_comp = param_inModel[exm_par.name] for parname in prefix_param[key]: pre,idstr,idx = split_prefixed_name(parname) if idx == exm_par.index: continue if hasattr(exm_par, 'new_param'): new_par = exm_par.new_param(idx) self.timing_model.add_param_from_top(new_par, exm_par_comp) if parfile is not None: self.timing_model.read_parfile(parfile)
def build_model(self, parfile=None, name=''): """Read parfile using the model_instance attribute. Parameters --------- name: str, optional The name for the timing model parfile : str optional The parfile name """ if parfile is not None: self.get_comp_from_parfile(parfile) sorted_comps = self.sort_components() self.timing_model = TimingModel(name, sorted_comps) param_inModel = self.timing_model.get_params_mapping() # Find unrecognised parameters in par file. if self.param_inparF is not None: parName = [] # add aliases for p in list(param_inModel.keys()): parName += getattr(self.timing_model, p).aliases parName += param_inModel.keys() for pp in self.param_inparF.keys(): if pp not in parName: self.param_unrecognized[pp] = self.param_inparF[pp] for ptype in ['prefixParameter', 'maskParameter']: prefix_param = \ self.search_prefix_param( \ list(self.param_unrecognized.keys()),self.timing_model, ptype) prefix_in_model = self.timing_model.get_params_of_type(ptype) for key in prefix_param.keys(): ppnames = [x for x in prefix_in_model if x.startswith(key)] for ppn in ppnames: pfx, idxs, idxv = split_prefixed_name(ppn) if pfx == key: exm_par = getattr(self.timing_model, ppn) else: continue exm_par_comp = param_inModel[exm_par.name] for parname in prefix_param[key]: pre, idstr, idx = split_prefixed_name(parname) if idx == exm_par.index: continue if hasattr(exm_par, 'new_param'): new_par = exm_par.new_param(idx) self.timing_model.add_param_from_top( new_par, exm_par_comp) if parfile is not None: self.timing_model.read_parfile(parfile)
def d_dm_d_DMs( self, toas, param_name, acc_delay=None): # NOTE we should have a better name for this.) """Derivatives of DM wrt the DM taylor expansion parameters.""" par = getattr(self, param_name) if param_name == "DM": order = 0 else: pn, idxf, idxv = split_prefixed_name(param_name) order = idxv dms = self.get_DM_terms() dm_terms = np.longdouble(np.zeros(len(dms))) dm_terms[order] = np.longdouble(1.0) if self.DMEPOCH.value is None: if any(t.value != 0 for t in dms[1:]): # Should be ruled out by validate() raise ValueError( f"DMEPOCH is not set but {param_name} is not zero") DMEPOCH = 0 else: DMEPOCH = self.DMEPOCH.value dt = (toas["tdbld"] - DMEPOCH) * u.day dt_value = (dt.to(u.yr)).value d_dm_d_dm_param = taylor_horner(dt_value, dm_terms) * (self.DM.units / par.units) return d_dm_d_dm_param
def read_parfile(self, filename): """Read values from the specified parfile into the model parameters.""" pfile = open(filename, 'r') for l in [pl.strip() for pl in pfile.readlines()]: # Skip blank lines if not l: continue # Skip commented lines if l.startswith('#') or l[:2]=="C ": continue parsed = False for par in self.params: if getattr(self, par).from_parfile_line(l): parsed = True if not parsed: try: prefix,f,v = utils.split_prefixed_name(l.split()[0]) if prefix not in ignore_prefix: log.warn("Unrecognized parfile line '%s'" % l) except: if l.split()[0] not in ignore_params: log.warn("Unrecognized parfile line '%s'" % l) # The "setup" functions contain tests for required parameters or # combinations of parameters, etc, that can only be done # after the entire parfile is read self.setup()
def d_phase_d_GLTD(self, toas, param, delay): """Calculate the derivative wrt GLF0D """ tbl = toas.table p, ids, idv = split_prefixed_name(param) if p != "GLTD_": raise ValueError( "Can not calculate d_phase_d_GLF0D with respect to %s." % param ) eph = np.longdouble(getattr(self, "GLEP_" + ids).value) par_GLTD = getattr(self, param) if par_GLTD.value == 0.0: return np.zeros(len(tbl), dtype=np.longdouble) * u.cycle / par_GLTD.units glf0d = getattr(self, "GLF0D_" + ids).quantity tau = par_GLTD.quantity dt = (tbl["tdbld"] - eph) * u.day - delay dt = dt.to(u.second) affected = np.where(dt > 0.0)[0] dpdGLTD = np.zeros(len(tbl), dtype=np.longdouble) * u.cycle / par_GLTD.units with u.set_enabled_equivalencies(dimensionless_cycles): dpdGLTD[affected] += glf0d * ( np.longdouble(1.0) - np.exp(-dt[affected] / tau) ) + glf0d * tau * (-np.exp(-dt[affected] / tau)) * dt[affected] / ( tau * tau ) return dpdGLTD
def d_dm_d_DMs( self, toas, param_name, acc_delay=None): # NOTE we should have a better name for this.) """ Derivatives of DM wrt the DM taylor expansion parameters. """ tbl = toas.table try: bfreq = self.barycentric_radio_freq(toas) except AttributeError: warn("Using topocentric frequency for dedispersion!") bfreq = tbl["freq"] par = getattr(self, param_name) unit = par.units if param_name == "DM": order = 0 else: pn, idxf, idxv = split_prefixed_name(param_name) order = idxv dms = self.get_DM_terms() dm_terms = np.longdouble(np.zeros(len(dms))) dm_terms[order] = np.longdouble(1.0) if self.DMEPOCH.value is None: DMEPOCH = tbl["tdbld"][0] else: DMEPOCH = self.DMEPOCH.value dt = (tbl["tdbld"] - DMEPOCH) * u.day dt_value = (dt.to(u.yr)).value d_dm_d_dm_param = taylor_horner(dt_value, dm_terms) * (self.DM.units / par.units) return d_dm_d_dm_param
def deriv_prep(self, toas, param, delay): """Get the things we need for any of the derivative calcs""" tbl = toas.table p, ids, idv = split_prefixed_name(param) eph = getattr(self, "GLEP_" + ids).value dt = (tbl["tdbld"] - eph) * u.day - delay dt = dt.to(u.second) affected = np.where(dt > 0.0)[0] return tbl, p, ids, idv, dt, affected
def test_aliases_mapping(): """Test if aliases gets mapped correclty """ mb = AllComponents() # all alases should be mapped to the components assert set(mb._param_alias_map.keys()) == set( mb.param_component_map.keys()) # Test if the param_alias_map is passed by pointer # Testing the private function for building the aliases map mb._check_alias_conflict("TESTAX", "TESTAXX", mb._param_alias_map) # assert "TESTAX" in mb._param_alias_map # Test existing entry # When adding an existing alias to the map. The mapped value should be the # same, otherwrise it will fail. mb._check_alias_conflict("F0", "F0", mb._param_alias_map) # assert mb._param_alias_map["F0"] == "F0" # Test repeatable_params with differnt indices. for rp in mb.repeatable_param: pint_par, first_init_par = mb.alias_to_pint_param(rp) cp = mb.param_component_map[pint_par][0] pint_par_obj = getattr(mb.components[cp], pint_par) try: prefix, id, ids = split_prefixed_name(rp) except PrefixError: prefix = rp new_idx_par = prefix + "2" assert mb.alias_to_pint_param( new_idx_par)[0] == pint_par_obj.prefix + "2" new_idx_par = prefix + "55" assert mb.alias_to_pint_param( new_idx_par)[0] == pint_par_obj.prefix + "55" # Test all aliases for als in pint_par_obj.aliases: assert mb.alias_to_pint_param(als)[0] == pint_par_obj.name try: als_prefix, id, ids = split_prefixed_name(als) except PrefixError: als_prefix = als assert mb.alias_to_pint_param(als_prefix + "2")[0] == pint_par_obj.prefix + "2" assert (mb.alias_to_pint_param(als_prefix + "55")[0] == pint_par_obj.prefix + "55")
def search_prefix_param(self, paramList, prefix_inModel): """ Check if the Unrecognized parameter has prefix parameter """ prefixs = {} for pn in prefix_inModel: try: pre,idxstr,idxV = split_prefixed_name(pn) prefixs[pre] = [] except: continue for p in paramList: try: pre,idxstr,idxV = split_prefixed_name(p) if pre in prefixs.keys(): prefixs[pre].append(p) except: continue return prefixs
def read_parfile(self, filename): """Read values from the specified parfile into the model parameters.""" checked_param = [] repeat_param = {} param_map = self.get_params_mapping() comps = self.components pfile = open(filename, 'r') for l in [pl.strip() for pl in pfile.readlines()]: # Skip blank lines if not l: continue # Skip commented lines if l.startswith('#') or l[:2]=="C ": continue k = l.split() name = k[0].upper() if name == 'UNITS' and len(k) > 1 and k[1] != 'TDB': log.error("UNITS %s not yet supported by PINT" % k[1]) raise Exception("UNITS %s not yet supported by PINT" % k[1]) if name in checked_param: if name in repeat_param.keys(): repeat_param[name] += 1 else: repeat_param[name] = 2 k[0] = k[0] + str(repeat_param[name]) l = ' '.join(k) parsed = False for par in param_map.keys(): host_comp = param_map[par] if host_comp != 'timing_model': cmp = comps[host_comp] else: cmp = self if cmp.__getattr__(par).from_parfile_line(l): parsed = True if not parsed: try: prefix,f,v = utils.split_prefixed_name(l.split()[0]) if prefix not in ignore_prefix: log.warn("Unrecognized parfile line '%s'" % l) except: if l.split()[0] not in ignore_params: log.warn("Unrecognized parfile line '%s'" % l) checked_param.append(name) # The "setup" functions contain tests for required parameters or # combinations of parameters, etc, that can only be done # after the entire parfile is read self.setup()
def d_phase_d_F(self, toas, param, delay): """Calculate the derivative wrt to an spin term.""" par = getattr(self, param) unit = par.units pn, idxf, idxv = split_prefixed_name(param) if param.startswith("PWF"): order = split_prefixed_name(param[:4])[2] + 1 else: order = 0 # order = idxv + 1 fterms = self.get_spin_terms(idxv) # make the choosen fterms 1 others 0 fterms = [ft * np.longdouble(0.0) / unit for ft in fterms] fterms[order] += np.longdouble(1.0) glepnm = f"PWEP_{idxf}" res = u.Quantity(np.zeros(toas.ntoas, dtype=np.longdouble)) * (1 / unit) dt, affected = self.get_dt_and_affected(toas, delay, glepnm) d_pphs_d_f = taylor_horner(dt.to(u.second), fterms) res[affected] = d_pphs_d_f.to(1 / unit) return res
def search_prefix_param(self,paramList,prefixList): """ Check if the Unrecognized parameter has prefix parameter """ for pn in prefixList: self.param_prefix[pn] = [] pnlen = len(pn) for p in paramList: try: pre,idxstr,idxV = split_prefixed_name(p) except: continue if pre == pn: self.param_prefix[pn].append(p)
def d_phase_d_F(self, toas, param, delay): """Calculate the derivative wrt to an spin term.""" par = getattr(self, param) unit = par.units pn, idxf, idxv = split_prefixed_name(param) order = idxv + 1 fterms = [0.0 * u.Unit("")] + self.get_spin_terms() # make the choosen fterms 1 others 0 fterms = [ft * numpy.longdouble(0.0) / unit for ft in fterms] fterms[order] += numpy.longdouble(1.0) dt = self.get_dt(toas, delay) d_pphs_d_f = taylor_horner(dt.to(u.second), fterms) return d_pphs_d_f.to(1 / unit)
def search_prefix_param(self, paramList, model, prefix_type): """Check if the Unrecognized parameter has prefix parameter""" prefixs = {} prefix_inModel = model.get_params_of_type_top(prefix_type) for pn in prefix_inModel: par = getattr(model, pn) prefixs[par.prefix] = [] for p in paramList: try: pre, idxstr, idxV = split_prefixed_name(p) if pre in [par.prefix] + par.prefix_aliases: prefixs[par.prefix].append(p) except ValueError: # FIXME: is this meant to catch KeyErrors? continue return prefixs
def d_phase_d_GLPH(self, toas, param, delay): """Calculate the derivative wrt GLPH""" tbl = toas.table p, ids, idv = split_prefixed_name(param) if p != "GLPH_": raise ValueError( "Can not calculate d_phase_d_GLPH with respect to %s." % param ) eph = np.longdouble(getattr(self, "GLEP_" + ids).value) par_GLPH = getattr(self, param) dt = (tbl["tdbld"] - eph) * u.day - delay dt = dt.to(u.second) affected = np.where(dt > 0.0)[0] dpdGLPH = np.zeros(len(tbl), dtype=np.longdouble) * u.cycle / par_GLPH.units dpdGLPH[affected] += 1.0 * u.cycle / par_GLPH.units return dpdGLPH
def read_parfile(self, filename): """Read values from the specified parfile into the model parameters.""" checked_param = [] repeat_param = {} param_map = self.get_params_mapping() comps = self.components pfile = open(filename, 'r') for l in [pl.strip() for pl in pfile.readlines()]: # Skip blank lines if not l: continue # Skip commented lines if l.startswith('#') or l[:2]=="C ": continue k = l.split() name = k[0].upper() if name in checked_param: if name in repeat_param.keys(): repeat_param[name] += 1 else: repeat_param[name] = 2 k[0] = k[0] + str(repeat_param[name]) l = ' '.join(k) parsed = False for par in param_map.keys(): host_comp = param_map[par] if host_comp != 'timing_model': cmp = comps[host_comp] else: cmp = self if cmp.__getattr__(par).from_parfile_line(l): parsed = True if not parsed: try: prefix,f,v = utils.split_prefixed_name(l.split()[0]) if prefix not in ignore_prefix: log.warn("Unrecognized parfile line '%s'" % l) except: if l.split()[0] not in ignore_params: log.warn("Unrecognized parfile line '%s'" % l) checked_param.append(name) # The "setup" functions contain tests for required parameters or # combinations of parameters, etc, that can only be done # after the entire parfile is read self.setup()
def d_phase_d_GLF1(self, toas, param, delay): """Calculate the derivative wrt GLF1""" tbl = toas.table p, ids, idv = split_prefixed_name(param) if p != "GLF1_": raise ValueError( "Can not calculate d_phase_d_GLF1 with respect to %s." % param ) eph = np.longdouble(getattr(self, "GLEP_" + ids).value) par_GLF1 = getattr(self, param) dt = (tbl["tdbld"] - eph) * u.day - delay dt = dt.to(u.second) affected = np.where(dt > 0.0)[0] dpdGLF1 = np.zeros(len(tbl), dtype=np.longdouble) * u.cycle / par_GLF1.units with u.set_enabled_equivalencies(dimensionless_cycles): dpdGLF1[affected] += np.longdouble(0.5) * dt[affected] * dt[affected] return dpdGLF1
def search_prefix_param(self, paramList, model, prefix_type): """ Check if the Unrecognized parameter has prefix parameter """ prefixs = {} prefix_inModel = model.get_params_of_type(prefix_type) for pn in prefix_inModel: par = getattr(model, pn) prefixs[par.prefix] = [] for p in paramList: try: pre,idxstr,idxV = split_prefixed_name(p) if pre in [par.prefix,] + par.prefix_aliases: prefixs[par.prefix].append(p) except: continue return prefixs
def get_model_instance(self,parfile=None): """Read parfile using the model_instance attribute. Parameters --------- parfile : str optional The parfile name """ if self.model_instance is None: model = self.build_model() self.model_instance = model() self.param_inModel = self.model_instance.params self.prefix_names = self.model_instance.prefix_params # Find unrecognised parameters in par file. if self.param_inparF is not None: parName = [] for p in self.param_inModel: parName+= getattr(self.model_instance,p).aliases parName += self.param_inModel for pp in self.param_inparF.keys(): if pp not in parName: self.param_unrecognized[pp] = self.param_inparF[pp] self.search_prefix_param(self.param_unrecognized.keys(),self.prefix_names) if self.param_prefix != {}: for p in self.param_prefix.keys(): ppnames = [x for x in self.model_instance.params if x.startswith(p)] for ppn in ppnames: pfxp = getattr(self.model_instance,ppn) if pfxp.is_prefix is True: for pp in self.param_prefix[p]: pre,idstr,idx = split_prefixed_name(pp) if idx == pfxp.index: continue newPfxp = pfxp.new_index_prefix_param(idx) self.model_instance.add_param(newPfxp) if parfile is not None: self.model_instance.read_parfile(parfile) return self.model_instance
def d_phase_d_GLF0D(self, toas, param, delay): """Calculate the derivative wrt GLF0D """ tbl = toas.table p, ids, idv = split_prefixed_name(param) if p != "GLF0D_": raise ValueError( "Can not calculate d_phase_d_GLF0D with respect to %s." % param ) eph = np.longdouble(getattr(self, "GLEP_" + ids).value) par_GLF0D = getattr(self, param) tau = getattr(self, "GLTD_%d" % idv).quantity dt = (tbl["tdbld"] - eph) * u.day - delay dt = dt.to(u.second) affected = np.where(dt > 0.0)[0] dpdGLF0D = np.zeros(len(tbl), dtype=np.longdouble) / par_GLF0D.units dpdGLF0D[affected] += tau * (np.longdouble(1.0) - np.exp(-dt[affected] / tau)) return dpdGLF0D
def get_model_instance(self,parfile=None): """Read parfile using the model_instance attribute. Parameters --------- parfile : str optional The parfile name """ if self.model_instance is None: model = self.build_model() self.model_instance = model() self.param_inModel = self.model_instance.params self.prefix_names = self.model_instance.prefix_params # Find unrecognised parameters in par file. if self.param_inparF is not None: parName = [] for p in self.param_inModel: parName+= getattr(self.model_instance,p).aliases parName += self.param_inModel for pp in self.param_inparF.keys(): if pp not in parName: self.param_unrecognized[pp] = self.param_inparF[pp] for ptype in ['prefixParameter', 'maskParameter']: prefix_in_model = self.model_instance.get_params_of_type(ptype) prefix_param = self.search_prefix_param(self.param_unrecognized.keys(), prefix_in_model) for key in prefix_param.keys(): ppnames = [x for x in prefix_in_model if x.startswith(key)] exm_par = getattr(self.model_instance,ppnames[0]) for parname in prefix_param[key]: pre,idstr,idx = split_prefixed_name(parname) if idx == exm_par.index: continue if hasattr(exm_par, 'new_param'): new_par = exm_par.new_param(idx) self.model_instance.add_param(new_par) if parfile is not None: self.model_instance.read_parfile(parfile)
def add_prefixed_param(modelIns,names): """Add a prefixed parameter into the timing model. Parameter ---------- modelIns : PINT Timing_model class The timing model of parameters adding to names : string or list of strings The name list of prefixed parameters.s """ if type(names) is str: names = [names,] for n in names: prefix,indexformat,indexV = split_prefixed_name(n) if prefix in modelIns.prefix_params: units = modelIns.prefix_params_units[prefix] des = modelIns.prefix_params_description[prefix] modelIns.add_param(prefixParameter(name = n,units = units,value = 0.0, description = des))
def read_parfile(self, filename): """Read values from the specified parfile into the model parameters.""" checked_param = [] repeat_param = {} pfile = open(filename, 'r') for l in [pl.strip() for pl in pfile.readlines()]: # Skip blank lines if not l: continue # Skip commented lines if l.startswith('#') or l[:2]=="C ": continue k = l.split() name = k[0].upper() if name in checked_param: if name in repeat_param.keys(): repeat_param[name] += 1 else: repeat_param[name] = 2 k[0] = k[0] + str(repeat_param[name]) l = ' '.join(k) parsed = False for par in self.params: if getattr(self, par).from_parfile_line(l): parsed = True if not parsed: try: prefix,f,v = utils.split_prefixed_name(l.split()[0]) if prefix not in ignore_prefix: log.warn("Unrecognized parfile line '%s'" % l) except: if l.split()[0] not in ignore_params: log.warn("Unrecognized parfile line '%s'" % l) checked_param.append(name) # The "setup" functions contain tests for required parameters or # combinations of parameters, etc, that can only be done # after the entire parfile is read self.setup()
def choose_model(parfile, category_order=None, name=None, check_for_missing_parameters=False): """Determine which model components are appropriate for parfile.""" if name is None: if isinstance(parfile, str): name = os.path.basename(parfile) else: name = "" if category_order is None: category_order = DEFAULT_ORDER models_by_category = defaultdict(list) for k, c_type in Component.component_types.items(): models_by_category[c_type.category].append(c_type) par_dict = {} par_lines = [] multi_tags = set([ "JUMP", "ECORR", "T2EFAC", "T2EQUAD", "EQUAD", "EFAC", "DMJUMP", "DMEFAC", "DMEQUAD", ]) multi_line = Counter() for l in interesting_lines(lines_of(parfile), comments=("#", "C ")): ll = l.split() k = ll[0] if k in multi_tags: multi_line[k] += 1 k = k + str(multi_line[k]) if k in par_dict: # FIXME: what happens with JUMPs? log.info("Lines with duplicate keys in par file: {} and {}".format( [k] + par_dict[k], ll)) par_dict[k] = ll[1:] par_lines.append(l) models_to_use = {} for category, models in models_by_category.items(): acceptable = [] for m_type in models: m = m_type() if m.is_in_parfile(par_dict): acceptable.append(m) if len(acceptable) > 1: raise ValueError( "Multiple models are compatible with this par file: {}".format( acceptable)) if acceptable: models_to_use[category] = acceptable[0] if "BINARY" in par_dict: vals = par_dict["BINARY"] if len(vals) != 1: raise ValueError("Mal-formed binary model selection: {}".format( repr(" ".join(["BINARY"] + vals)))) (bm, ) = vals if "pulsar_system" not in models_to_use: # Either we're missing parameters or the model is bogus # FIXME: distinguish raise UnknownBinaryModel( "Unknown binary model requested in par file: {}".format(bm)) # FIXME: consistency check - the componens actually chosen should know the name bm models_in_order = [] for category in category_order: try: models_in_order.append(models_to_use.pop(category)) except KeyError: pass models_in_order.extend(v for k, v in sorted(models_to_use.items())) tm = TimingModel(name, models_in_order) # FIXME: this should go in TimingModel for when you try to # add conflicting components alias_map = {} for prefix_type in ["prefixParameter", "maskParameter"]: for pn in tm.get_params_of_type_top(prefix_type): par = getattr(tm, pn) for a in [par.prefix] + par.prefix_aliases: if a in alias_map: raise ValueError( "Two prefix/mask parameters have the same " "alias {}: {} and {}".format(a, alias_map[a], par)) alias_map[a] = par leftover_params = par_dict.copy() for k in tm.get_params_mapping(): leftover_params.pop(k, None) for a in getattr(tm, k).aliases: leftover_params.pop(a, None) for p in leftover_params: try: pre, idxstr, idxV = split_prefixed_name(p) try: par = alias_map[pre] except KeyError: if pre in ignore_prefix: # log.warning("Ignoring unhandled prefix {}".format(pre)) continue else: raise ValueError( "Mystery parameter {}, prefix {} with number {}". format(p, pre, idxV)) component = tm.get_params_mapping()[par.name] new_parameter = par.new_param(idxV) if hasattr(tm, new_parameter.name): raise ValueError("Received duplicate parameter {}".format( new_parameter.name)) tm.add_param_from_top(new_parameter, component) # print("added", new_parameter) except PrefixError: pass return tm
def build_model(self, parfile=None, name=""): """Read parfile using the model_instance attribute. Throws error if mismatched coordinate systems detected. Parameters --------- name: str, optional The name for the timing model parfile : str optional The parfile name """ if parfile is not None: self.get_comp_from_parfile(parfile) # ensure coordinate systems match for POS and PM if "RAJ" in self.preprocess_parfile(parfile).keys(): if "PMELONG" in self.preprocess_parfile(parfile): raise AttributeError( "Cannot have Ecliptic proper motion parameters (PMELONG/PMELAT) with Equatorial position parameters (RAJ/DECJ) in par file." ) elif "PMELAT" in self.preprocess_parfile(parfile): raise AttributeError( "Cannot have Ecliptic proper motion parameters (PMELONG/PMELAT) with Equatorial position parameters (RAJ/DECJ) in par file." ) elif "ELONG" in self.preprocess_parfile(parfile).keys(): if "PMRA" in self.preprocess_parfile(parfile): raise AttributeError( "Cannot have Equatorial proper motion parameters (PMRA/PMDEC) with Ecliptic position parameters (ELONG/ELAT) in par file." ) elif "PMDEC" in self.preprocess_parfile(parfile): raise AttributeError( "Cannot have Equatorial proper motion parameters (PMRA/PMDEC) with Ecliptic position parameters (ELONG/ELAT) in par file." ) sorted_comps = self.sort_components() self.timing_model = TimingModel(name, sorted_comps) param_inModel = self.timing_model.get_params_mapping() # Find unrecognised parameters in par file. if self.param_inparF is not None: parName = [] # add aliases for p in list(param_inModel.keys()): parName += getattr(self.timing_model, p).aliases parName += param_inModel.keys() for pp in self.param_inparF.keys(): if pp not in parName: self.param_unrecognized[pp] = self.param_inparF[pp] for ptype in ["prefixParameter", "maskParameter"]: prefix_param = self.search_prefix_param( self.param_unrecognized, self.timing_model, ptype) prefix_in_model = self.timing_model.get_params_of_type_top( ptype) for key in prefix_param: ppnames = [x for x in prefix_in_model if x.startswith(key)] for ppn in ppnames: pfx, idxs, idxv = split_prefixed_name(ppn) if pfx == key: exm_par = getattr(self.timing_model, ppn) else: continue exm_par_comp = param_inModel[exm_par.name] for parname in prefix_param[key]: pre, idstr, idx = split_prefixed_name(parname) if idx == exm_par.index: continue if hasattr(exm_par, "new_param"): new_par = exm_par.new_param(idx) self.timing_model.add_param_from_top( new_par, exm_par_comp) if "BINARY" in self.param_inparF: vals = self.param_inparF["BINARY"] if len(vals) != 1: raise ValueError( "Mal-formed binary model selection: {}".format( repr(" ".join(["BINARY"] + vals)))) (bm, ) = vals cats = self.timing_model.get_components_by_category() if "pulsar_system" not in cats: raise UnknownBinaryModel( "Unknown binary model requested in par file: {}". format(bm)) # FIXME: consistency check - the componens actually chosen should know the name bm for p in self.timing_model.params: if isinstance(self.timing_model[p], maskParameter): # maskParameters need a bogus alias for parfile parsing # remove this bogus alias try: ix = self.timing_model[p].aliases.index( self.timing_model[p].prefix) except ValueError: pass else: del self.timing_model[p].aliases[ix] if parfile is not None: self.timing_model.read_parfile(parfile)
def read_parfile(self, file): """Read values from the specified parfile into the model parameters. Parameters ---------- file : str or list or file-like The parfile to read from. May be specified as a filename, a list of lines, or a readable file-like object. """ repeat_param = defaultdict(int) param_map = self.get_params_mapping() comps = self.components.copy() comps['timing_model'] = self wants_tcb = None stray_lines = [] for li in interesting_lines(lines_of(file), comments=("#", "C ")): k = li.split() name = k[0].upper() if name == 'UNITS': if name in repeat_param: raise ValueError("UNITS is repeated in par file") else: repeat_param[name] += 1 if len(k) > 1 and k[1] == 'TDB': wants_tcb = False else: wants_tcb = li continue if name == 'EPHVER': if len(k) > 1 and k[1] != '2' and wants_tcb is None: wants_tcb = li log.warning("EPHVER %s does nothing in PINT" % k[1]) #actually people expect EPHVER 5 to work #even though it's supposed to imply TCB which doesn't continue repeat_param[name] += 1 if repeat_param[name] > 1: k[0] = k[0] + str(repeat_param[name]) li = ' '.join(k) used = [] for p, c in param_map.items(): if getattr(comps[c], p).from_parfile_line(li): used.append((c, p)) if len(used) > 1: log.warning("More than one component made use of par file " "line {!r}: {}".format(li, used)) if used: continue if name in ignore_params: log.debug("Ignoring parfile line '%s'" % (li, )) continue try: prefix, f, v = utils.split_prefixed_name(name) if prefix in ignore_prefix: log.debug("Ignoring prefix parfile line '%s'" % (li, )) continue except utils.PrefixError: pass stray_lines.append(li) if wants_tcb: raise ValueError( "Only UNITS TDB supported by PINT but parfile has {}".format( wants_tcb)) if stray_lines: for l in stray_lines: log.warning("Unrecognized parfile line {!r}".format(l)) for name, param in getattr(self, "discarded_components", []): log.warning("Model component {} was rejected because we " "didn't find parameter {}".format(name, param)) log.warning("Final object: {}".format(self)) # The "setup" functions contain tests for required parameters or # combinations of parameters, etc, that can only be done # after the entire parfile is read self.setup()
def _pintify_parfile(self, parfile, allow_name_mixing=False): """Translate parfile parameter name to PINT style name. This function converts the parfile information to PINT understandable parameter name. It also returns the PINT unrecognized parameters and check if the parfile has illegal repeating lines. Parameters ---------- parfile : str, file-like object, or parfile dictionary Parfile name, parfile StringIO, or the parfile dictionary returned by :func:`parse_parfile`. allow_name_mixing : bool, optional Flag for allowing the input to have mixing aliases names for the same parameter. For example, if this flag is true, one can have T2EFAC and EFAC, both of them maps to PINT parameter EFAC, present in the parfile at the same time. Returns ------- pint_param_dict : dict Pintified parameter dictionary with the PINT name as key and list of parameter value-uncertainty lines as value. For the repeating parameters in the parfile, the value will contain mulitple lines. original_name_map : dict PINT name maps to the original .par file input names. PINT name is the key and the original name is in the value. unknown_param : dict The PINT unrecognized parameters in the format of a dictionary. The key is the unknown parameter name and the value is the parfile value lines. Raises ------ TimingModelError If the parfile has mulitple line with non-repeating parameters. """ pint_param_dict = defaultdict(list) original_name_map = defaultdict(list) unknown_param = defaultdict(list) repeating = Counter() if isinstance(parfile, (str, StringIO)): parfile_dict = parse_parfile(parfile) else: parfile_dict = parfile for k, v in parfile_dict.items(): try: pint_name, init0 = self.all_components.alias_to_pint_param(k) except UnknownParameter: if k in ignore_params: # Parameter is known but in the ingore list continue else: # Check ignored prefix try: pfx, idxs, idx = split_prefixed_name(k) if pfx in ignore_prefix: # It is an ignored prefix. continue else: unknown_param[k] += v except PrefixError: unknown_param[k] += v continue pint_param_dict[pint_name] += v original_name_map[pint_name].append(k) repeating[pint_name] += len(v) # Check if this parameter is allowed to be repeated by PINT if len(pint_param_dict[pint_name]) > 1: if pint_name not in self.all_components.repeatable_param: raise TimingModelError( f"Parameter {pint_name} is not a repeatable parameter. " f"However, mulitple line use it.") # Check if the name is mixed for p_n, o_n in original_name_map.items(): if len(o_n) > 1: if not allow_name_mixing: raise TimingModelError( f"Parameter {p_n} have mixed input names/alias " f"{o_n}. If you want to have mixing names, please use" f" 'allow_name_mixing=True', and the output .par file " f"will use '{original_name_map[pint_name][0]}'.") original_name_map[p_n] = o_n[0] return pint_param_dict, original_name_map, unknown_param
def build_model(self, parfile=None, name=""): """Read parfile using the model_instance attribute. Parameters --------- name: str, optional The name for the timing model parfile : str optional The parfile name """ if parfile is not None: self.get_comp_from_parfile(parfile) sorted_comps = self.sort_components() self.timing_model = TimingModel(name, sorted_comps) param_inModel = self.timing_model.get_params_mapping() # Find unrecognised parameters in par file. if self.param_inparF is not None: parName = [] # add aliases for p in list(param_inModel.keys()): parName += getattr(self.timing_model, p).aliases parName += param_inModel.keys() for pp in self.param_inparF.keys(): if pp not in parName: self.param_unrecognized[pp] = self.param_inparF[pp] for ptype in ["prefixParameter", "maskParameter"]: prefix_param = self.search_prefix_param( self.param_unrecognized, self.timing_model, ptype) prefix_in_model = self.timing_model.get_params_of_type(ptype) for key in prefix_param: ppnames = [x for x in prefix_in_model if x.startswith(key)] for ppn in ppnames: pfx, idxs, idxv = split_prefixed_name(ppn) if pfx == key: exm_par = getattr(self.timing_model, ppn) else: continue exm_par_comp = param_inModel[exm_par.name] for parname in prefix_param[key]: pre, idstr, idx = split_prefixed_name(parname) if idx == exm_par.index: continue if hasattr(exm_par, "new_param"): new_par = exm_par.new_param(idx) self.timing_model.add_param_from_top( new_par, exm_par_comp) if "BINARY" in self.param_inparF: vals = self.param_inparF["BINARY"] if len(vals) != 1: raise ValueError( "Mal-formed binary model selection: {}".format( repr(" ".join(["BINARY"] + vals)))) (bm, ) = vals cats = self.timing_model.get_component_of_category() if "pulsar_system" not in cats: raise UnknownBinaryModel( "Unknown binary model requested in par file: {}". format(bm)) # FIXME: consistency check - the componens actually chosen should know the name bm if parfile is not None: self.timing_model.read_parfile(parfile)
def read_parfile(self, filename): """Read values from the specified parfile into the model parameters.""" checked_param = [] repeat_param = {} param_map = self.get_params_mapping() comps = self.components pfile = open(filename, 'r') wants_tcb = None for l in [pl.strip() for pl in pfile.readlines()]: # Skip blank lines if not l: continue # Skip commented lines if l.startswith('#') or l[:2] == "C ": continue k = l.split() name = k[0].upper() if name == 'UNITS': if len(k) > 1 and k[1] == 'TDB': wants_tcb = False else: wants_tcb = k[1] if name == 'EPHVER': if len(k) > 1 and k[1] != '2' and wants_tcb is None: wants_tcb = l log.warning("EPHVER %s does nothing in PINT" % k[1]) #actually people expect EPHVER 5 to work #even though it's supposed to imply TCB which doesn't if name in checked_param: if name in repeat_param.keys(): repeat_param[name] += 1 else: repeat_param[name] = 2 k[0] = k[0] + str(repeat_param[name]) l = ' '.join(k) parsed = False for par in param_map.keys(): host_comp = param_map[par] if host_comp != 'timing_model': cmp = comps[host_comp] else: cmp = self if cmp.__getattr__(par).from_parfile_line(l): parsed = True if not parsed: p = l.split()[0] if p in ignore_params: log.debug("Ignoring parfile line '%s'" % (l, )) parsed = True if not parsed: p = l.split()[0] try: prefix, f, v = utils.split_prefixed_name(l.split()[0]) if prefix in ignore_prefix: log.debug("Ignoring prefix parfile line '%s'" % (l, )) parsed = True except utils.PrefixError: pass if not parsed: log.warning("Unrecognized parfile line '%s'" % (l, )) checked_param.append(name) if wants_tcb: raise ValueError("UNITS %s not yet supported by PINT" % k[1]) # The "setup" functions contain tests for required parameters or # combinations of parameters, etc, that can only be done # after the entire parfile is read self.setup()
def _setup_model( self, timing_model, pint_param_dict, original_name=None, setup=True, validate=True, ): """Fill up a timing model with parameter values and then setup the model. This function fills up the timing model parameter values from the input pintified parameter dictionary. If the parameter has not initialized yet, it will add the parameter to the timing model. For the repeatable parameters, it will search matching key value pair first. If the input parameter line's key-value matches the existing parameter, the parameter value and uncertainty will copy to the existing parameter. If there is no match, it will find an empty existing parameter, whose `key` is `None`, and fill it up. If no empyt parameter left, it will add a new parameter to it. Parameters ---------- timing_model : pint.models.TimingModel Timing model to get setup. pint_param_dict: dict Pintified parfile dictionary which can be aquired by :meth:`ModelBuilder._pintify_parfile` origin_name : dict, optional A map from PINT name to the original input name. setup : bool, optional Whether to run the setup function in the timing model. validate : bool, optional Whether to run the validate funciotn in the timing model. """ if original_name is not None: use_alias = True else: use_alias = False for pp, v in pint_param_dict.items(): try: par = getattr(timing_model, pp) except AttributeError: # since the input is pintfied, it should be an uninitized indexed parameter # double check if the missing parameter an indexed parameter. pint_par, first_init = self.all_components.alias_to_pint_param( pp) try: prefix, _, index = split_prefixed_name(pint_par) except PrefixError: par_hosts = self.all_components.param_component_map[ pint_par] currnt_cp = timing_model.components.keys() raise TimingModelError( f"Parameter {pint_par} is recognized" f" by PINT, but not used in the current" f" timing model. It is used in {par_hosts}," f" but the current timing model uses {currnt_cp}.") # TODO need to create a beeter API for _loacte_param_host host_component = timing_model._locate_param_host(first_init) timing_model.add_param_from_top( getattr(timing_model, first_init).new_param(index), host_component[0][0], ) par = getattr(timing_model, pint_par) # Fill up the values param_line = len(v) if param_line < 2: if use_alias: # Use the input alias as input name = original_name[pp] else: name = pp par.from_parfile_line(" ".join([name] + v)) else: # For the repeatable parameters lines = copy.deepcopy(v) # Line queue. # Check how many repeatable parameters in the model. example_par = getattr(timing_model, pp) prefix, _, index = split_prefixed_name(pp) for li in lines: # Creat a temp parameter with the idx bigger than all the existing indices repeatable_map = timing_model.get_prefix_mapping(prefix) new_max_idx = max(repeatable_map.keys()) + 1 temp_par = example_par.new_param(new_max_idx) temp_par.from_parfile_line(" ".join( [prefix + str(new_max_idx), li])) if use_alias: # Use the input alias as input temp_par.use_alias = original_name[pp] # Check current repeatable's key and value # TODO need to change here when maskParameter name changes to name_key_value empty_repeat_param = [] for idx, rp in repeatable_map.items(): rp_par = getattr(timing_model, rp) if rp_par.compare_key_value(temp_par): # Key and key value match, copy the new line to it # and exit rp_par.from_parfile_line(" ".join([rp, li])) if use_alias: # Use the input alias as input rp_par.use_alias = original_name[pp] break if rp_par.key is None: # Empty space for new repeatable parameter empty_repeat_param.append(rp_par) # There is no current repeatable parameter matching the new line # First try to fill up an empty space. if empty_repeat_param != []: emt_par = empty_repeat_param.pop(0) emt_par.from_parfile_line(" ".join([emt_par.name, li])) if use_alias: # Use the input alias as input emt_par.use_alias = original_name[pp] else: # No empty space, add a new parameter to the timing model. host_component = timing_model._locate_param_host(pp) timing_model.add_param_from_top( temp_par, host_component[0][0]) if setup: timing_model.setup() if validate: timing_model.validate() return timing_model