"""
The classes defined here are hold information on an individual pulsar
or an interable list of pulsars.
"""
from .config import PSR_ALL_PARS, PSR_ALL
[docs]class Pulsar(object):
"""
An object to hold a single pulsar. The class requires a pulsar name. The
pulsar parameters are set as attributes of the class.
Args:
psrname (str): a string containing a pulsar name
query (:class:`psrqpy.QueryATNF`): a query
Additional keyword arguments are any of the valid queriable pulsar
parameters.
"""
def __init__(self, psrname, query=None, **kwargs):
self._name = psrname
self._query = query
for key, value in kwargs.items():
setattr(self, key, value)
def __repr__(self):
"""
Define the method return by repr
"""
return self.name
[docs] def keys(self):
"""
Return a list of the class attribute names for allowed pulsar
parameters.
"""
keys = (
PSR_ALL_PARS
+ [par + "_ERR" for par in PSR_ALL_PARS]
+ [par + "_REF" for par in PSR_ALL_PARS]
)
return [key for key in self.__dict__ if key in keys]
[docs] def items(self):
"""
Return a list of the class attribute values.
"""
keys = (
PSR_ALL_PARS
+ [par + "_ERR" for par in PSR_ALL_PARS]
+ [par + "_REF" for par in PSR_ALL_PARS]
)
return [value for key, value in self.__dict__.items() if key in keys]
@property
def name(self):
"""
Return the pulsar name
"""
return self._name
def __getitem__(self, key):
"""
If the class has a attribute given by the key then return it, otherwise
generate a query for that key to set it.
Args:
key (str): an item to get
"""
ukey = key.upper()
pulsarname = self.name
param = None
if key in self.__dict__:
return self.__dict__[key]
elif ukey in self.__dict__:
return self.__dict__[ukey]
else:
if ukey.endswith("_ERR") or ukey.endswith(
"_REF"
): # an error or reference parameter
tkey = ukey[:-4] # parameter name without error
else:
tkey = ukey
if tkey not in PSR_ALL_PARS:
raise KeyError(
'"{}" is not a recognised pulsar ' "parameter".format(tkey)
)
else:
# generate a query for the key and add it
if self._query is None:
try:
from .search import QueryATNF
self._query = QueryATNF()
except IOError:
raise Exception("Problem querying ATNF catalogue")
psrrow = self._query.get_pulsar(pulsarname)
if psrrow is None:
raise Exception('Pulsar "{}" is unknown'.format(pulsarname))
param = psrrow[ukey][0] # required output parameter
setattr(self, ukey, param) # set output parameter value
# set parameter value if an error value was requested
if PSR_ALL[tkey]["err"]:
if tkey != ukey and ukey.endswith(
"_ERR"
): # asking for error, so set actual value
setattr(self, tkey, psrrow[tkey][0])
else: # asking for value, so set error
setattr(self, tkey + "_ERR", psrrow[tkey + "_ERR"][0])
# set parameter value if a reference values was requested
if PSR_ALL[tkey]["ref"]:
if tkey != ukey and ukey.endswith(
"_REF"
): # asking for reference, so set actual value
setattr(self, tkey, psrrow[tkey][0])
else: # asking for value, so set reference
setattr(self, tkey + "_REF", psrrow[tkey + "_REF"][0])
return param
def __getattr__(self, key):
"""
If the class has a attribute given by the key then return it, otherwise
raise an error.
"""
ukey = key.upper()
# swapped from using hasattr to try...except... (see
# https://hynek.me/articles/hasattr/)
try:
return self.__dict__[key]
except KeyError:
try:
return self.__dict__[ukey]
except KeyError:
try:
if ukey in PSR_ALL_PARS:
return self[ukey]
except KeyError:
raise AttributeError(key)
def __getstate__(self):
"""
Define to allow pickling.
"""
return self.__dict__
def __setstate__(self, d):
"""
Define to allow pickling.
"""
self.__dict__.update(d)
def __dir__(self):
"""
Set this to what is returned for ipython's autocomplete (otherwise the
custom ``__getattr__`` caused problems!)
"""
return self.keys()
def __str__(self):
"""
Define the string method.
"""
return self.name
def __eq__(self, other):
"""
Define '==' rich comparison methods. True if pulsars have the same
name.
"""
if not isinstance(other, Pulsar):
return False
return bool(self.name == other.name)
def __ne__(self, other):
"""
Define '!=' rich comparison methods. False if pulsars have the same name.
"""
if not isinstance(other, Pulsar):
return True
return bool(self.name != other.name)
def __copy__(self):
"""
Define how the object should be copied with copy
"""
attrs = {}
for key, value in zip(self.keys(), self.items()):
attrs[key] = value
newpsr = type(self)(self.name, **attrs)
return newpsr
[docs]class Pulsars(object):
"""
Class to contain multiple :class:`~psrqpy.pulsar.Pulsar` objects.
"""
def __init__(self):
self._num_pulsars = 0 # number of pulsars in the object
self._psrs = {} # dictionary of Pulsar objects in the object, keyed to the name
def __iter__(self):
"""
Iterator for the class
"""
for psr in self._psrs:
yield psr
def __getitem__(self, key):
"""
Define getitem to get a Pulsar object from the _psrs dictionary
"""
if key in self._psrs.keys():
return self._psrs[key]
return None
def __getstate__(self):
"""
Define to allow pickling.
"""
return self.__dict__
def __setstate__(self, d):
"""
Define to allow pickling.
"""
self.__dict__.update(d)
def __len__(self):
"""
Define len method as the number of pulsars in the object
"""
return self._num_pulsars
[docs] def add_pulsar(self, psr):
"""
Add a pulsar into the object.
Args:
psr (:class:`~psrqpy.pulsar.Pulsar`, :class:`~psrqpy.pulsar.Pulsars`): a
:class:`~psrqpy.pulsar.Pulsar` object, or :class:`~psrqpy.pulsar.Pulsars`
object
"""
assert isinstance(psr, (Pulsar, Pulsars)), "psr is not a Pulsar type"
if isinstance(psr, Pulsar):
if psr.name not in self._psrs:
self._num_pulsars += 1 # add one pulsar
self._psrs[psr.name] = psr
else:
# check for duplicates
for psrname in psr:
if psrname not in self._psrs.keys(): # don't add duplicates
self._psrs[psrname] = psr[psrname]
self._num_pulsars += 1
[docs] def remove_pulsar(self, psrname):
"""
Remove a pulsar from the object. Only do one at a time.
Args:
psrname (str): a string with the name of a pulsar
"""
assert isinstance(psrname, str), "psrname is not a string"
if psrname in self._psrs:
del self._psrs[psrname]
self._num_pulsars -= 1
[docs] def pop(self, psrname):
"""
Remove a pulsar from the object and return the removed pulsar.
Args:
psrname (str): a string with the name of a pulsar
"""
assert isinstance(psrname, str), "psrname is not a string"
if psrname in self._psrs:
self._num_pulsars -= 1
return self._psrs.pop(psrname)
return None
def __str__(self):
"""
Define string method
"""
return "\n".join([self._psrs[psr].name for psr in self._psrs])