# model_builder.py
# Defines the automatic timing model generator interface
import os
# The timing models that we will be using
from .timing_model import generate_timing_model, TimingModel
from pint.utils import split_prefixed_name
from .parameter import prefixParameter
import os, inspect, fnmatch
import glob
import sys
[docs]def get_componets():
timing_comps = {}
path = os.path.dirname(os.path.abspath(__file__))
for root, dirnames, filenames in os.walk(path):
for filename in fnmatch.filter(filenames, '*.py'):
if filename == '__init__.py':
continue
mod_root_start = root.find('pint/models')
if mod_root_start + len('pint/models') > len(root):
mod_root = ''
else:
mod_root = root[mod_root_start + len('pint/models/'):]
mod = os.path.join(mod_root, filename).replace("/", ".")[:-3]
exec('import %s as tmp' % mod)
s = set()
for k, v in tmp.__dict__.items():
if inspect.isclass(v) and issubclass(v, TimingModel):
if k == 'TimingModel':
continue
s.add(v)
if s != set():
timing_comps[tmp.__name__] = s
return timing_comps
ComponentsList = get_componets()
default_models = ["StandardTimingModel",]
[docs]class model_builder(object):
"""A class for model construction interface.
Parameters
---------
name : str
Name for the model.
parfile : str optional
The .par file input for select components. If the parfile is
provided the self.model_instance will be put model instance
with .par file read in. If it is not provided, self.model_instance
will return as None.
Return
---------
A class contains the result model instance if parfile is provided and
method to build the model.
Examples:
---------
Read model from parfile :
[1] mb = model_builder("PulsarJ1955", parfile ="J1955.par" )
[2] psrJ1955 = mb.model_instance
Build model from sketch and read parfile:
[1] from .bt import BT
[2] mb = model_builder("BT_model")
[3] mb.add_components(BT)
[4] psrJ1955 = mb.get_model_instance('J1955.par')
Build model instance without reading parfile:
[1] mb = model_builder("BT_model")
[2] mb.add_components(BT)
[3] myModel = mb.get_model_instance()
"""
def __init__(self, name, parfile = None):
self.name = name
self.model_instance = None
self.param_inparF = None
self.param_unrecognized = {}
self.param_inModel = []
self.comps = ComponentsList
self.prefix_names = None
self.param_prefix = {}
self.select_comp = []
if parfile is not None:
self.parfile = parfile
self.get_comp_from_parfile(self.parfile)
self.get_model_instance(self.parfile)
def __str__(self):
result = 'Model name : ' + self.name + '\n'
result += 'Components in the model : \n'
for c in self.select_comp:
result += ' '+str(c)+'\n'
if self.model_instance is not None:
result += 'Read parameters from : '+ self.parfile +'\n'
result += 'The model instance is :\n'+str(self.model_instance)
return result
[docs] def preprocess_parfile(self,parfile):
"""Preprocess the par file.
Return
---------
A dictionary with all the parfile parameters with values in string
"""
param = {}
repeat_par = {}
pfile = open(parfile, '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()
if k[0] in param.keys(): # repeat parameter TODO: add JUMP1 even there is only one
if k[0] in repeat_par.keys():
repeat_par[k[0]] += 1
else:
repeat_par[k[0]] = 2
param[k[0] + str(repeat_par[k[0]])] = k[1:]
else:
param[k[0]] = k[1:]
self.param_inparF = param
for key in repeat_par.keys():
self.param_inparF[key + str(1)] = self.param_inparF.pop(key)
pfile.close()
return self.param_inparF
[docs] def get_comp_from_parfile(self,parfile):
"""Check all the components if it is needed from parfile
Put the needed on in the selected components list
"""
params_inpar = self.preprocess_parfile(parfile)
for module in self.comps.keys():
selected_c = None
num_comp = len(self.comps[module])
slt_tmp = None
for ii, c in enumerate(self.comps[module]):
cclass = c()
#Check is this components a subclass of other components
if TimingModel not in c.__bases__:
if hasattr(cclass,'model_special_params'):
# TODO : Need fix aliases part
if any(par in params_inpar.keys() for par in cclass.model_special_params):
selected_c = c
break
else: # If no special parameters, ignore.
continue
if cclass.is_in_parfile(params_inpar):
selected_c = c
# One module will have one selected component
if selected_c is not None and selected_c not in self.select_comp:
self.select_comp.append(selected_c)
[docs] def build_model(self):
""" Return a model with all components listed in the self.components
list.
"""
if self.select_comp ==[]:
raise(RuntimeError("No timing model components selected."))
return generate_timing_model(self.name,tuple(self.select_comp))
[docs] def add_components(self,components):
""" Add new components to constructing model.
"""
if not isinstance(components,list):
components = [components,]
for c in components:
if c not in self.select_comp:
self.select_comp.append(c)
[docs] 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
[docs] 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)
[docs]def get_model(parfile):
"""A one step function to build model from a parfile
Parameters
---------
name : str
Name for the model.
parfile : str
The parfile name
Return
---------
Model instance get from parfile.
"""
name = os.path.basename(os.path.splitext(parfile)[0])
mb = model_builder(name,parfile)
return mb.model_instance