diff --git a/src/pint/models/__init__.py b/src/pint/models/__init__.py index ce35b6922..5aec3fde8 100644 --- a/src/pint/models/__init__.py +++ b/src/pint/models/__init__.py @@ -29,7 +29,7 @@ from pint.models.glitch import Glitch from pint.models.piecewise import PiecewiseSpindown from pint.models.ifunc import IFunc -from pint.models.jump import DelayJump, PhaseJump +from pint.models.jump import PhaseJump from pint.models.model_builder import get_model, get_model_and_toas from pint.models.noise_model import EcorrNoise, PLRedNoise, ScaleToaError from pint.models.solar_system_shapiro import SolarSystemShapiro diff --git a/src/pint/models/jump.py b/src/pint/models/jump.py index 9273fd669..0089d26dc 100644 --- a/src/pint/models/jump.py +++ b/src/pint/models/jump.py @@ -2,86 +2,84 @@ import logging import astropy.units as u -import numpy +import numpy as np from pint.models.parameter import maskParameter from pint.models.timing_model import DelayComponent, MissingParameter, PhaseComponent log = logging.getLogger(__name__) - -class DelayJump(DelayComponent): - """Phase jumps - - Parameters supported: - - .. paramtable:: - :class: pint.models.jump.DelayJump - - Note - ---- - This component is disabled for now, since we don't have any method - to identify the phase jumps and delay jumps. - """ - - register = False - category = "delay_jump" - - def __init__(self): - super().__init__() - self.add_param(maskParameter(name="JUMP", units="second")) - self.delay_funcs_component += [self.jump_delay] - - def setup(self): - super().setup() - self.jumps = [] - for mask_par in self.get_params_of_type("maskParameter"): - if mask_par.startswith("JUMP"): - self.jumps.append(mask_par) - for j in self.jumps: - self.register_deriv_funcs(self.d_delay_d_jump, j) - - def jump_delay(self, toas, acc_delay=None): - """This method returns the jump delays for each toas section collected by - jump parameters. The delay value is determined by jump parameter value - in the unit of seconds. - """ - tbl = toas.table - jdelay = numpy.zeros(len(tbl)) - for jump in self.jumps: - jump_par = getattr(self, jump) - mask = jump_par.select_toa_mask(toas) - # NOTE: Currently parfile jump value has opposite sign with our - # delay calculation. - jdelay[mask] += -jump_par.value - return jdelay * u.second - - def d_delay_d_jump(self, toas, jump_param, acc_delay=None): - tbl = toas.table - d_delay_d_j = numpy.zeros(len(tbl)) - jpar = getattr(self, jump_param) - mask = jpar.select_toa_mask(toas) - d_delay_d_j[mask] = -1.0 - return d_delay_d_j * u.second / jpar.units - - def print_par(self): - result = "" - for jump in self.jumps: - jump_par = getattr(self, jump) - result += jump_par.as_parfile_line() - return result +__all__ = ["PhaseJump"] class PhaseJump(PhaseComponent): """Arbitrary jumps in pulse phase. - In spite of the name, the amounts here are specified in seconds and - converted to phase using F0. + A JUMP adds a constant amount to the observed phase of all the TOAs it + applies to. JUMPs are specified using TOA flags:: + + JUMP -fish carp 0.1 + + will select all TOAs that have the flag ``-fish`` with the value ``carp`` + and add 0.1 seconds times ``F0`` to the phase observed at each of them. + This would frequently be used with a flag specifying the receiver or front + end to JUMP all TOAs coming from a particular combination of telescope and + frequency band. Users can of course add their own flags to allow the + selection of appropriate subsets of TOAs. + + An example of how you could JUMP a particular set of TOAs:: + + >>> toa_index_list = [1,3,5] + >>> for i in toa_index_list: + ... toas.table['flags'][i]['fish'] = 'carp' + >>> np = m.JUMP1.new_param(100) + >>> np.flag = '-fish' + >>> np.flag_value = 'carp' + >>> m.add_param_from_top(np, "PhaseJump") + + More briefly, you could use + ``m.add_jump_and_flags(toas.table['flags'][1,3,5], flag='-fish', flag_value='carp')``, + which adds the flag ``-fish`` with the value ``carp`` to TOAs numbers 1,3, and 5, + and also creates a new JUMP affecting those TOAs. + + Jumps are specified by :class:`~pint.models.parameter.maskParameter` + objects, so there is further documentation there on how these parameters + and their selection criteria work. In brief, in addition to matching + specific flags, these can also match MJD ranges (``JUMP mjd 57000 58000 + 0.1``), telescopes (``JUMP tel ao 0.1``), or frequency ranges + (``JUMP freq 1000 2000 0.1``). + + The set of TOAs matched by a particular jump, say ``JUMP1``, can be retrieved + by :func:`~pint.models.parameter.maskParameter.select_toa_mask` as in + ``model.JUMP1.select_toa_mask(toas)``. + + The original TEMPO supported JUMPs encoded in ``.tim`` files - a line + containing the command JUMP indicates the beginning of a block of TOAs, and + the next occurrence indicates the end of the block. The block of TOAs so + defined would then have an unnamed JUMP parameter associated with it and + fit for. When PINT encounters such a command, the affected TOAs get a flag + ``-jump N``, where N increases by 1 for each group encountered. A par file + can take advantage of this by including a line ``JUMP -jump 1 0.1``; such parameters + can be automatically added with the + :func:`~pint.models.timing_model.TimingModel.jump_flags_to_params` + function. Parameters supported: .. paramtable:: :class: pint.models.jump.PhaseJump + + Note + ---- + + In spite of the name, the amounts here are specified in seconds and + converted to phase using F0. They are treated as applying to the observed + phase, so these JUMPs do not affect where the pulsar is in its orbit when + the TOA was observed. This is more appropriate for things like a + redefinition in the zero of phase due to a change in pulse profile template + than for actual time delays. Unfortunately no standard way of specifying + that other kind of JUMPs exists, so although PINT contains code to + implement them they cannot be used in par files. """ register = True @@ -98,27 +96,49 @@ def __init__(self): ) self.phase_funcs_component += [self.jump_phase] - def setup(self): - super().setup() - self.jumps = [] + @property + def jumps(self): + """A list of all the JUMP parameter objects in the model.""" + r = [] for mask_par in self.get_params_of_type("maskParameter"): if mask_par.startswith("JUMP"): - self.jumps.append(mask_par) - for j in self.jumps: + r.append(getattr(self, mask_par)) + return r + + def setup(self): + """Set up support data structures to reflect parameters as set.""" + super().setup() + for pm in self.jumps: + j = pm.name # prevents duplicates from being added to phase_deriv_funcs - if j in self.deriv_funcs.keys(): + if j in self.deriv_funcs: del self.deriv_funcs[j] self.register_deriv_funcs(self.d_phase_d_jump, j) def jump_phase(self, toas, delay): - """This method returns the jump phase for each toas section collected by + """The extra phase contributed by the JUMPs. + + This method returns the jump phase for each toas section collected by jump parameters. The phase value is determined by jump parameter times F0. + + Parameters + ---------- + toas : pint.toa.TOAs + The TOAs for which the JUMP is to be computed. + delay : array-like + Ignored. + + Returns + ------- + astropy.units.Quantity + The phase shift for each TOA. """ tbl = toas.table - jphase = numpy.zeros(len(tbl)) * (self.JUMP1.units * self._parent.F0.units) - for jump in self.jumps: - jump_par = getattr(self, jump) + jphase = np.zeros(len(tbl)) * (self.JUMP1.units * self._parent.F0.units) + for jump_par in self.jumps: + if jump_par.value is None: + continue mask = jump_par.select_toa_mask(toas) # NOTE: Currently parfile jump value has opposite sign with our # phase calculation. @@ -126,110 +146,151 @@ def jump_phase(self, toas, delay): return jphase def d_phase_d_jump(self, toas, jump_param, delay): + """Derivative of phase with respect to the JUMP argument. + + Parameters + ---------- + toas : pint.toa.TOAs + The TOAs for which the JUMP is to be computed. + jump_param : pint.models.parameter.maskParameter + The jump parameter to differentiate with respect to. + delay : array-like + Ignored. + + Returns + ------- + astropy.units.Quantity + The derivative of phase shift with respect to the parameter for each TOA. + """ tbl = toas.table jpar = getattr(self, jump_param) - d_phase_d_j = numpy.zeros(len(tbl)) + d_phase_d_j = np.zeros(len(tbl)) mask = jpar.select_toa_mask(toas) d_phase_d_j[mask] = self._parent.F0.value return (d_phase_d_j * self._parent.F0.units).to(1 / u.second) def print_par(self): + """Return a string representation of all JUMP parameters appropriate for a par file.""" result = "" - for jump in self.jumps: - jump_par = getattr(self, jump) + for jump_par in self.jumps: result += jump_par.as_parfile_line() return result - def get_number_of_jumps(self): - """Returns the number of jumps contained in this PhaseJump object.""" - return len(self.jumps) - - def get_jump_param_objects(self): - """ - Returns a list of the maskParameter objects representing the jumps - in this PhaseJump object. - """ - jump_obs = [getattr(self, jump) for jump in self.jumps] - return jump_obs + def add_jump_and_flags(self, toa_flags, flag="gui_jump", flag_value=None): + """Add jump object to PhaseJump and appropriate flags to TOA tables. - def jump_params_to_flags(self, toas): - """Take jumps created from .par file and add appropriate flags to toa table. + Given a subset of TOAs (specified by a reference to their flags objects), + create a new JUMP and assign flags to those TOAs so that they are selected + by it. - This function was made specifically with pintk in mind for a way to properly - load jump flags at the same time a .par file with jumps is loaded (like how - jump_flags_to_params loads jumps from .tim files). + This will add a parameter to the model corresponding to:: - Parameters - ---------- - toas: TOAs object - The TOAs which contain the TOA table to be modified - """ - # for every jump, set appropriate flag for TOAs it jumps - for jump_par in self.get_jump_param_objects(): - # find TOAs jump applies to - mask = jump_par.select_toa_mask(toas) - # apply to dictionaries - for d in toas.table["flags"][mask]: - if "jump" in d: - index_list = d["jump"].split(",") - if str(jump_par.index) in index_list: - continue - index_list.append(str(jump_par.index)) - d["jump"] = ",".join(index_list) - else: - d["jump"] = str(jump_par.index) - - def add_jump_and_flags(self, toa_table): - """Add jump object to PhaseJump and appropriate flags to TOA tables. + JUMP -gui_jump N 0 1 - Helper function for pintk. Primarily to be used when applying a jump through - pintk to TOAs - since these jumps don't have keys that match to preexisting - flags in the TOA tables, we must add the flags when adding the jump. + where ``N`` some number not currently in use by any JUMP. This + function will also add the flag ``-gui_jump N`` to all the TOAs in the segment + of the table that is passed to this function. Parameters ---------- - toa_table: list object - The TOA table which must be modified. In pintk (pulsar.py), this will + toa_flags: array of dict + The TOA flags which must be modified. In pintk (pulsar.py), this will be all_toas.table["flags"][selected] + flag: str + The name of the flag to use for the JUMP. + flag_value: str or None + The flag value to associate with this JUMP; if not specified, find the first + integer N not associated with a JUMP and use its string representation. + + Returns + ------- + str + The name of the new JUMP parameter. """ - ind = None # index of jump - name = None # name of jump - # check if this is first jump added - if len(self.jumps) == 0 or ( - len(self.jumps) == 1 and getattr(self, "JUMP1").key == None - ): - param = maskParameter( - name="JUMP", - index=1, - key="-gui_jump", - key_value="1", - value=0.0, - units="second", - frozen=False, - ) - self.add_param(param) - # otherwise add on jump with next index - else: - # first, search for TOAs already jumped in inputted selection - pintk does not allow jumps added through GUI to overlap with existing jumps - for d in toa_table: - if "gui_jump" in d.keys(): - log.warning( - "The selected toa(s) overlap an existing jump. Remove all interfering jumps before attempting to jump these toas." - ) - return None - param = maskParameter( - name="JUMP", - index=len(self.jumps) + 1, - key="-gui_jump", - key_value=str(len(self.jumps) + 1), - value=0.0, - units="second", - frozen=False, - ) - self.add_param(param) - ind = param.index + in_use = set() + for pm in self.jumps: + if pm.flag == flag: + in_use.add(pm.flag_value) + if flag_value is None: + i = 1 + while True: + flag_value = str(i) + if flag_value not in in_use: + break + i += 1 + elif flag_value in in_use: + raise ValueError(f"A JUMP -{flag} {flag_value} is already present.") + + used_indices = set() + for pm in self.jumps: + used_indices.add(pm.index) + i = 1 + while i in used_indices: + i += 1 + + param = maskParameter( + name="JUMP", + index=i, + flag=flag, + flag_value=flag_value, + value=0.0, + units="second", + frozen=False, + ) name = param.name + for d in toa_flags: + if flag in d: + raise ValueError( + "The selected toa(s) overlap an existing jump. Remove all " + "interfering jumps before attempting to jump these toas." + ) + self.add_param(param) self.setup() - for dict1 in toa_table: - dict1["gui_jump"] = str(ind) + # add appropriate flags to TOA table to link jump with appropriate TOA + for d in toa_flags: + d[flag] = flag_value return name + + def tidy_jumps_for_fit(self, toas): + """Adjust the JUMPs so that this set of TOAs can be safely fit. + + This is particularly intended for use when working with a subset of + a larger set of TOAs. + + - If all TOAs are affected by free JUMPs, some or all will be frozen until + at least one TOA is unaffected by any JUMP. + - If any JUMP does not affect any TOAs, it will be frozen. + + Parameters + ---------- + toas : pint.toa.TOAs + The TOAs that this model is to be used with. + """ + masks = {} + for pm in self.jumps: + if pm.frozen: + continue + c = np.zeros(len(toas), dtype=bool) + c[pm.select_toa_mask(toas)] = True + if not np.any(c): + log.info(f"No TOAs affected by {pm.name}, freezing it") + pm.frozen = True + else: + masks[pm.name] = pm, c + while True: + affected = np.zeros(len(toas), dtype=bool) + most_n = None + most_pm = None + most_count = 0 + for n, (pm, c) in masks.items(): + affected |= c + if c.sum() > most_count: + most_count = c.sum() + most_pm = pm + most_n = n + if np.all(affected): + log.info(f"Freezing {n} to avoid all TOAs being JUMPed") + most_pm.frozen = True + del masks[most_n] + else: + break diff --git a/src/pint/models/noise_model.py b/src/pint/models/noise_model.py index 5bab0c134..33e688b5b 100644 --- a/src/pint/models/noise_model.py +++ b/src/pint/models/noise_model.py @@ -91,32 +91,32 @@ def setup(self): for mask_par in self.get_params_of_type("maskParameter"): if mask_par.startswith("EFAC"): par = getattr(self, mask_par) - self.EFACs[mask_par] = (par.key, par.key_value) + self.EFACs[mask_par] = (par.flag, par.flag_value) elif mask_par.startswith("EQUAD"): par = getattr(self, mask_par) - self.EQUADs[mask_par] = (par.key, par.key_value) + self.EQUADs[mask_par] = (par.flag, par.flag_value) elif mask_par.startswith("TNEQ"): par = getattr(self, mask_par) - self.TNEQs[mask_par] = (par.key, par.key_value) + self.TNEQs[mask_par] = (par.flag, par.flag_value) else: continue # convert all the TNEQ to EQUAD for tneq in self.TNEQs: tneq_par = getattr(self, tneq) - if tneq_par.key is None: + if tneq_par.flag is None: continue if self.TNEQs[tneq] in list(self.EQUADs.values()): log.warning( "'%s %s %s' is provided by parameter EQUAD, using" - " EQUAD instead. " % (tneq, tneq_par.key, tneq_par.key_value) + " EQUAD instead. " % (tneq, tneq_par.flag, tneq_par.flag_value) ) else: EQUAD_name = "EQUAD" + str(tneq_par.index) - if EQUAD_name in list(self.EQUADs.keys()): + if EQUAD_name in list(self.EQUADs.flags()): EQUAD_par = getattr(self, EQUAD_name) - EQUAD_par.key = tneq_par.key - EQUAD_par.key_value = tneq_par.key_value + EQUAD_par.flag = tneq_par.flag + EQUAD_par.flag_value = tneq_par.flag_value EQUAD_par.quantity = tneq_par.quantity.to(u.us) else: self.add_param( @@ -131,13 +131,13 @@ def setup(self): ) ) EQUAD_par = getattr(self, EQUAD_name) - EQUAD_par.key = tneq_par.key - EQUAD_par.key_value = tneq_par.key_value + EQUAD_par.flag = tneq_par.flag + EQUAD_par.flag_value = tneq_par.flag_value EQUAD_par.quantity = tneq_par.quantity.to(u.us) for pp in self.params: if pp.startswith("EQUAD"): par = getattr(self, pp) - self.EQUADs[pp] = (par.key, par.key_value) + self.EQUADs[pp] = (par.flag, par.flag_value) def validate(self): super().validate() @@ -145,7 +145,7 @@ def validate(self): for el in ["EFACs", "EQUADs"]: l = list(getattr(self, el).values()) if [x for x in l if l.count(x) > 1] != []: - raise ValueError("'%s' have duplicated keys and key values." % el) + raise ValueError("'%s' have duplicated flags and flag values." % el) def scale_toa_sigma(self, toas): sigma_scaled = toas.table["error"].quantity.copy() @@ -222,12 +222,12 @@ def setup(self): for mask_par in self.get_params_of_type("maskParameter"): if mask_par.startswith("DMEFAC"): par = getattr(self, mask_par) - if par.key is not None: - self.DMEFACs[mask_par] = (par.key, tuple(par.key_value)) + if par.flag is not None: + self.DMEFACs[mask_par] = (par.flag, tuple(par.flag_value)) elif mask_par.startswith("DMEQUAD"): par = getattr(self, mask_par) - if par.key is not None: - self.DMEQUADs[mask_par] = (par.key, tuple(par.key_value)) + if par.flag is not None: + self.DMEQUADs[mask_par] = (par.flag, tuple(par.flag_value)) else: continue @@ -242,7 +242,7 @@ def validate(self): for el in ["DMEFACs", "DMEQUADs"]: l = list(getattr(self, el).values()) if [x for x in l if l.count(x) > 1] != []: - raise ValueError("'%s' have duplicated keys and key values." % el) + raise ValueError("'%s' have duplicated flags and flag values." % el) def scale_dm_sigma(self, toas): """ @@ -319,7 +319,7 @@ def setup(self): for mask_par in self.get_params_of_type("maskParameter"): if mask_par.startswith("ECORR"): par = getattr(self, mask_par) - self.ECORRs[mask_par] = (par.key, par.key_value) + self.ECORRs[mask_par] = (par.flag, par.flag_value) else: continue @@ -330,11 +330,11 @@ def validate(self): for el in ["ECORRs"]: l = list(getattr(self, el).values()) if [x for x in l if l.count(x) > 1] != []: - raise ValueError("'%s' have duplicated keys and key values." % el) + raise ValueError("'%s' have duplicated flags and flag values." % el) def get_ecorrs(self): ecorrs = [] - for ecorr, ecorr_key in list(self.ECORRs.items()): + for ecorr in self.ECORRs: ecorrs.append(getattr(self, ecorr)) return ecorrs diff --git a/src/pint/models/parameter.py b/src/pint/models/parameter.py index 73508585a..ce9202bf4 100644 --- a/src/pint/models/parameter.py +++ b/src/pint/models/parameter.py @@ -25,7 +25,7 @@ import numbers from warnings import warn -import astropy.time as time +import astropy.time import astropy.units as u import numpy as np from astropy.coordinates.angles import Angle @@ -42,7 +42,7 @@ time_to_longdouble, time_to_mjd_string, ) -from pint.toa_select import TOASelect +from pint.toa import FlagDict from pint.utils import split_prefixed_name log = logging.getLogger(__name__) @@ -476,7 +476,7 @@ class floatParameter(Parameter): ---------- name : str The name of the parameter. - value : number, str, or astropy.units.Quantity + value : number or str or astropy.units.Quantity The input parameter float value. units : str or astropy.units.Quantity Parameter default unit. Parameter .value and .uncertainty_value attribute @@ -851,7 +851,7 @@ class MJDParameter(Parameter): ---------- name : str The name of the parameter. - value : astropy Time, str, float in mjd, str in mjd. + value : astropy.time.Time or str or float in mjd or str in mjd. The input parameter MJD value. description : str, optional A short description of what this parameter means. @@ -901,7 +901,7 @@ def __init__( continuous=continuous, aliases=aliases, ) - self.value_type = time.Time + self.value_type = astropy.time.Time self.paramType = "MJDParameter" self.special_arg += ["time_scale"] @@ -959,8 +959,10 @@ def _set_quantity(self, val): val = np.longdouble(val) result = time_from_longdouble(val, self.time_scale) elif isinstance(val, (str, bytes)): - result = Time(val, scale=self.time_scale, format="pulsar_mjd_string") - elif isinstance(val, time.Time): + result = astropy.time.Time( + val, scale=self.time_scale, format="pulsar_mjd_string" + ) + elif isinstance(val, astropy.time.Time): result = val else: raise ValueError( @@ -1403,7 +1405,7 @@ class maskParameter(floatParameter): parameters can be distinguished within the :class:`pint.models.timing_model.TimingModel` object. For example:: - >>> p = maskParameter(name='JUMP', index=2, key="-fe", key_value="G430") + >>> p = maskParameter(name='JUMP', index=2, flag="-fe", flag_value="G430") >>> p.name 'JUMP2' @@ -1451,8 +1453,8 @@ def __init__( self, name, index=1, - key=None, - key_value=[], + flag=None, + flag_value=None, value=None, long_double=False, units=None, @@ -1463,39 +1465,9 @@ def __init__( aliases=[], ): self.is_mask = True - # {key_name: (keyvalue parse function, keyvalue length)} - # Move this to some other places. - self.key_identifier = { - "mjd": (lambda x: time.Time(x, format="mjd").mjd, 2), - "freq": (lambda x: u.Quantity(x, u.MHz, copy=False), 2), - "name": (str, 1), - "tel": (lambda x: get_observatory(str(x)).name, 1), - } - if not isinstance(key_value, (list, tuple)): - key_value = [key_value] - - # Check key and key value - key_value_parser = str - if key is not None: - if key.lower() in self.key_identifier.keys(): - key_info = self.key_identifier[key.lower()] - if len(key_value) != key_info[1]: - errmsg = f"key {key} takes {key_info[1]} element(s)." - raise ValueError(errmsg) - key_value_parser = key_info[0] - else: - if not key.startswith("-"): - raise ValueError( - "A key to a TOA flag requires a leading '-'." - " Legal keywords that don't require a leading '-' " - "are MJD, FREQ, NAME, TEL." - ) - self.key = key - self.key_value = [ - key_value_parser(k) for k in key_value - ] # retains string format from .par file to ensure correct data type for comparison - self.key_value.sort() + self.flag = flag + self.flag_value = flag_value self.index = index name_param = name + str(index) self.origin_name = name @@ -1523,13 +1495,97 @@ def __init__( self.aliases.append(name) self.is_prefix = True + allowed_non_flags = {"mjd", "freq", "name", "tel"} + wants_two_values = {"mjd", "freq"} + + @staticmethod + def validate(flag, flag_value): + """Verify that flag_value is an appropriate type and return it. + + This incidentally converts lists to tuples and mildly normalizes values + but does not otherwise convert the value (for example it does not attempt + to parse strings). + """ + if flag is None: + if flag_value is not None: + raise ValueError("Cannot associate a value with no flag") + if flag == "mjd": + start, end = sorted(flag_value) + if not isinstance(start, (int, float, np.longdouble)) or not isinstance( + end, (int, float, np.longdouble) + ): + raise ValueError( + f"When selecting by MJD one must supply a " + f"pair of numbers not {flag_value}" + ) + return float(start), float(end) + elif flag == "freq": + low, high = sorted(flag_value) + if not isinstance(low, u.Quantity) or not isinstance(high, u.Quantity): + raise ValueError( + f"When selecting by frequency one must supply a " + f"pair of Quantities not {flag_value}" + ) + return low.to(u.MHz), high.to(u.MHz) + elif flag == "tel": + return get_observatory(flag_value).name + else: + FlagDict.check_allowed_value(flag, flag_value) + return flag_value + + @staticmethod + def convert(flag, flag_value): + """Convert flag_value strings to the appropriate form for the flag.""" + if flag == "mjd": + start, end = flag_value + return ( + float(start), + float(end), + ) + elif flag == "freq": + start, end = flag_value + return float(start) * u.MHz, float(end) * u.MHz + else: + return flag_value + + @property + def flag(self): + return self._flag + + @flag.setter + def flag(self, flag): + if flag is None: + self._flag = None + elif not isinstance(flag, str): + raise ValueError( + f"maskParameters can only select on strings not {repr(flag)}" + ) + else: + flag = flag.lower() + FlagDict.check_allowed_key(flag) + self._flag = flag + + @property + def flag_value(self): + return self._flag_value + + @flag_value.setter + def flag_value(self, flag_value): + if flag_value is None: + self._flag_value = None + else: + self._flag_value = maskParameter.validate(self.flag, flag_value) + def __repr__(self): out = self.__class__.__name__ + "(" + self.name - if self.key is not None: - out += " " + self.key - if self.key_value is not None: - for kv in self.key_value: - out += " " + str(kv) + if self.flag is not None: + out += " " + self.flag + if self.flag_value is not None: + if isinstance(self.flag_value, str): + out += " " + self.flag_value + else: + for kv in self.flag_value: + out += " " + str(kv) if self.quantity is not None: out += " " + self.str_quantity(self.quantity) else: @@ -1553,6 +1609,11 @@ def name_matches(self, name): def from_parfile_line(self, line): """Read mask parameter line (e.g. JUMP). + This operates on an existing maskParameter object, ``self``, and + requires that ``self.name`` match the parameter name on the par file + line. ``self.units`` is used to determine the units that values + and uncertainties are converted to. + Returns ------- bool @@ -1560,18 +1621,24 @@ def from_parfile_line(self, line): Notes ----- - The accepted format:: + The accepted formats for most flags:: - 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 + NAME -flag flag_value parameter_value + NAME -flag flag_value parameter_value fit_flag + NAME -flag flag_value parameter_value fit_flag uncertainty + NAME -flag flag_value parameter_value uncertainty + + If the flag is one of MJD or FREQ then:: + + NAME flag flag_value1 flag_value2 parameter_value + NAME flag flag_value1 flag_value2 parameter_value fit_flag + NAME flag flag_value1 flag_value2 parameter_value fit_flag uncertainty + NAME flag flag_value1 flag_value2 parameter_value uncertainty where NAME is the name for this class as reported by ``self.name_matches``. + + The cases here are distinguished by the fact that ``fit_flag`` must be ``0`` or ``1`` + while an uncertainty must be a floating-point number. """ k = line.split() if not k: @@ -1582,75 +1649,77 @@ def from_parfile_line(self, line): return False try: - self.key = k[1] + flag = k[1].lower() except IndexError: raise ValueError( - "{}: No key found on timfile line {!r}".format(self.name, line) + "{}: No flag found on timfile line {!r}".format(self.name, line) ) + if flag in self.allowed_non_flags: + self.flag = flag + elif not flag.startswith("-"): + raise ValueError( + f"Flags must be indicated with -, and the only " + f"non-flags allowed are {maskParameter.allowed_non_flags}." + ) + else: + # Strip leading - + self.flag = flag[1:] - 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: + if flag in self.wants_two_values: + flag_value_str = k[2], k[3] + len_flag_v = 2 + else: + flag_value_str = k[2] + len_flag_v = 1 + self.flag_value = maskParameter.convert(self.flag, flag_value_str) + if len(k) < 3 + len_flag_v: raise ValueError( "{}: Expected at least {} entries on timfile line {!r}".format( - self.name, 3 + len_key_v, line + self.name, 3 + len_flag_v, line ) ) + self.value = str2longdouble(k[2 + len_flag_v]) - for ii in range(len_key_v): - if key_value_info[0] != str: - try: - kval = float(k[2 + ii]) - except ValueError: - 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)) + k_left = k[3 + len_flag_v :] + if not k_left: + pass + elif len(k_left) == 1: + if k_left[0] in ("0", "1"): + self.frozen = not int(k_left[0]) else: - self.key_value[ii] = key_value_info[0](kval) - if len(k) >= 3 + len_key_v: - self.value = 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 ValueError: - try: - str2longdouble(k[3 + len_key_v]) - ucty = k[3 + len_key_v] - except ValueError: - 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) + self.uncertainty = str2longdouble(k_left[0]) * self.units + elif len(k_left) == 2: + self.frozen = not int(k_left[0]) + self.uncertainty = str2longdouble(k_left[1]) * self.units + else: + raise ValueError(f"Line has too many elements to parse: {k}") return True def as_parfile_line(self): if self.quantity is None: + # Parameter is not set so don't output anything return "" if self.use_alias is None: name = self.origin_name else: name = self.use_alias - line = "%-15s %s " % (name, self.key) - for kv in self.key_value: - if isinstance(kv, time.Time): - line += f"{time_to_mjd_string(kv)} " - elif isinstance(kv, u.Quantity): - line += f"{kv.value} " + if self.flag in self.allowed_non_flags: + flag = self.flag + else: + flag = "-" + self.flag + line = "%-15s %s " % (name, flag) + if isinstance(self.flag_value, str): + line += self.flag_value + else: + start, end = self.flag_value + if isinstance(start, float): + line += f"{start} {end} " + elif isinstance(start, u.Quantity): + line += f"{start.to_value(u.MHz)} {end.to_value(u.MHz)} " else: - line += f"{kv} " + raise ValueError( + f"Unexpected format in self.flag_value: {self.flag_value}" + ) line += "%25s" % self.str_quantity(self.quantity) if self.uncertainty is not None: line += " %d %s" % (0 if self.frozen else 1, str(self.uncertainty_value)) @@ -1672,8 +1741,8 @@ def new_param(self, index, copy_all=False): new_mask_param = maskParameter( name=self.origin_name, index=index, - key=self.key, - key_value=self.key_value, + flag=self.flag, + flag_value=self.flag_value, value=self.value, long_double=self.long_double, units=self.units, @@ -1690,55 +1759,40 @@ def select_toa_mask(self, toas): Parameter --------- - toas: :class:`pint.toas.TOAs` + toas: pint.toas.TOAs Returns ------- array An array of TOA indices selected by the mask. """ - column_match = { - "mjd": "mjd_float", - "freq": "freq", - "tel": "obs", - } - if len(self.key_value) == 1: - if not hasattr(self, "toa_selector"): - self.toa_selector = TOASelect(is_range=False, use_hash=True) - condition = {self.name: self.key_value[0]} - elif len(self.key_value) == 2: - if not hasattr(self, "toa_selector"): - self.toa_selector = TOASelect(is_range=True, use_hash=True) - condition = {self.name: tuple(self.key_value)} - elif len(self.key_value) == 0: - return np.array([], dtype=int) - else: - raise ValueError( - "Parameter %s has more key values than " - "expected.(Expect 1 or 2 key values)" % self.name - ) - # get the table columns - # TODO Right now it is only supports mjd, freq, tel, and flagkeys, - # We need to consider some more complicated situation - if self.key.startswith("-"): - key = self.key[1::] + if self.flag == "mjd": + start, end = self.flag_value + c = toas.table["mjd_float"] >= start + c &= toas.table["mjd_float"] <= end + r = np.nonzero(c)[0] + elif self.flag == "freq": + low, high = self.flag_value + c = toas.table["freq"] >= low + c &= toas.table["freq"] <= high + r = np.nonzero(c)[0] + elif self.flag == "tel": + c = toas.table["obs"] == self.flag_value + r = np.nonzero(c)[0] else: - key = self.key - - tbl = toas.table - if ( - self.key.lower() not in column_match - ): # This only works for the one with flags. - # The flags are recomputed every time. If don't - # recompute, flags can only be added to the toa table once and then never update, - # making it impossible to add additional jump parameters after the par file is read in (pintk) - flag_col = [x.get(key, None) for x in tbl["flags"]] - tbl[key] = flag_col - col = tbl[key] - else: - col = tbl[column_match[key.lower()]] - select_idx = self.toa_selector.get_select_index(condition, col) - return select_idx[self.name] + r = [] + if self.flag is not None: + if self.flag.startswith("-"): + flag = self.flag[1:] + else: + # only "name" is allowed here + flag = self.flag + if self.flag_value is not None: + for i, f in enumerate(toas.table["flags"]): + if str(f.get(flag, "")) == self.flag_value: + r.append(i) + r = np.array(r, dtype=int) + return r class pairParameter(floatParameter): diff --git a/src/pint/models/solar_wind_dispersion.py b/src/pint/models/solar_wind_dispersion.py index 2c8912abf..9b12ad8a9 100644 --- a/src/pint/models/solar_wind_dispersion.py +++ b/src/pint/models/solar_wind_dispersion.py @@ -8,7 +8,6 @@ import pint.utils as ut from pint.models.dispersion_model import Dispersion, DMconst from pint.models.parameter import floatParameter -from pint.toa_select import TOASelect class SolarWindDispersion(Dispersion): diff --git a/src/pint/models/timing_model.py b/src/pint/models/timing_model.py index 65e4bab26..f6c0beef7 100644 --- a/src/pint/models/timing_model.py +++ b/src/pint/models/timing_model.py @@ -865,11 +865,11 @@ def map_component(self, component): """ comps = self.components if isinstance(component, str): - if component not in list(comps.keys()): + if component not in comps: raise AttributeError("No '%s' in the timing model." % component) comp = comps[component] else: # When component is an component instance. - if component not in list(comps.values()): + if component not in comps.values(): raise AttributeError( "No '%s' in the timing model." % component.__class__.__name__ ) @@ -1328,84 +1328,65 @@ def noise_model_dimensions(self, toas): return result - def jump_flags_to_params(self, toas): + def jump_flags_to_params(self, toas, flag="jump"): """Convert jump flags in toas.table["flags"] (loaded in .tim file) to jump parameters in the model. - The flag processed is ``jump``. + Parameters + ---------- + toas : pint.toa.TOAs + The set of TOAs to extract jumps from. + flag : str + The flag to use; ``jump`` is produced when ``JUMP`` lines are + encountered in legacy ``.tim`` files. One ``JUMP`` parameter + will be introduced for each value associated with this flag. """ from . import jump + new_jumps = [] # check if any TOAs are jumped - jumped = ["jump" in flag_dict.keys() for flag_dict in toas.table["flags"]] - if not any(jumped): + jumped = set(toas[flag]) + if "" in jumped: + jumped.remove("") + if not jumped: log.info("No jump flags to process from .tim file") - return None - for flag_dict in toas.table["flags"][jumped]: - # add PhaseJump object if model does not have one already - if "PhaseJump" not in self.components: - log.info("PhaseJump component added") - a = jump.PhaseJump() - a.setup() - self.add_component(a) - self.remove_param("JUMP1") - # take jumps in TOA table and add them as parameters to the model - for num in flag_dict["jump"]: - if "JUMP" + str(num) not in self.params: - param = maskParameter( - name="JUMP", - index=num, - key="-tim_jump", - key_value=num, - value=0.0, - units="second", - uncertainty=0.0, - ) - self.add_param_from_top(param, "PhaseJump") - getattr(self, param.name).frozen = False - flag_dict["tim_jump"] = str( - num - ) # this is the value select_toa_mask uses - self.components["PhaseJump"].setup() - - def delete_jump_and_flags(self, toa_table, jump_num): - """Delete jump object from PhaseJump and remove its flags from TOA table - (helper function for pintk). - - Parameters - ---------- - toa_table: list or None - The TOA table which must be modified. In pintk (pulsar.py), for the - prefit model, this will be all_toas.table["flags"]. - For the postfit model, it will be None (one set of TOA tables for both - models). - jump_num: int - Specifies the index of the jump to be deleted. - """ - # remove jump of specified index - self.remove_param("JUMP" + str(jump_num)) - - # remove jump flags from selected TOA tables - if toa_table is not None: - for d in toa_table: - if "jump" in d: - index_list = d["jump"].split(",") - if str(jump_num) in index_list: - del index_list[index_list.index(str(jump_num))] - if not index_list: - del d["jump"] - else: - d["jump"] = ",".join(index_list) - - # if last jump deleted, remove PhaseJump object from model - if ( - self.components["PhaseJump"].get_number_of_jumps() == 1 - ): # means last jump just deleted - comp_list = getattr(self, "PhaseComponent_list") - for item in comp_list: - if isinstance(item, pint.models.jump.PhaseJump): - self.remove_component(item) - return + return new_jumps + if "PhaseJump" not in self.components: + log.info("PhaseJump component added") + a = jump.PhaseJump() + self.add_component(a, setup=False) + self.remove_param("JUMP1") + a.setup() + for pm in self.jumps: + if pm.flag == "-" + flag: + if pm.flag_value in jumped: + jumped.remove(pm.flag_value) + if not jumped: + log.info("All JUMPs appear to already be present.") + return new_jumps + used_indices = set() + for pm in self.jumps: + used_indices.add(pm.index) + next_free_index = 1 + while next_free_index in used_indices: + next_free_index += 1 + for j in jumped: + param = maskParameter( + name="JUMP", + index=next_free_index, + flag=flag, + flag_value=j, + value=0.0, + units="second", + uncertainty=0.0, + frozen=False, + ) + self.add_param_from_top(param, "PhaseJump") + used_indices.add(next_free_index) + while next_free_index in used_indices: + next_free_index += 1 + new_jumps.append(param.name) self.components["PhaseJump"].setup() + return new_jumps def get_barycentric_toas(self, toas, cutoff_component=""): """Conveniently calculate the barycentric TOAs. @@ -2264,7 +2245,7 @@ def validate_toas(self, toas): if "TNEQ" in str(par.name) or par.frozen: continue if len(par.select_toa_mask(toas)) == 0: - bad_parameters.append(f"'{maskpar}, {par.key}, {par.key_value}'") + bad_parameters.append(f"'{maskpar}, {par.flag}, {par.flag_value}'") for c in self.components.values(): try: c.validate_toas(toas) @@ -2587,7 +2568,7 @@ def register_deriv_funcs(self, func, param): """ pn = self.match_param_aliases(param) - if pn not in list(self.deriv_funcs.keys()): + if pn not in self.deriv_funcs: self.deriv_funcs[pn] = [func] else: # TODO: diff --git a/src/pint/models/troposphere_delay.py b/src/pint/models/troposphere_delay.py index 3036ed7bc..7b41d88fd 100644 --- a/src/pint/models/troposphere_delay.py +++ b/src/pint/models/troposphere_delay.py @@ -13,7 +13,6 @@ from pint.models.timing_model import DelayComponent from pint.observatory import get_observatory from pint.observatory.topo_obs import TopoObs -from pint.toa_select import TOASelect log = logging.getLogger(__name__) diff --git a/src/pint/observatory/__init__.py b/src/pint/observatory/__init__.py index 70c6f5a29..c908d3305 100644 --- a/src/pint/observatory/__init__.py +++ b/src/pint/observatory/__init__.py @@ -142,6 +142,10 @@ def get(cls, name): import pint.observatory.observatories # noqa import pint.observatory.special_locations # noqa + if not isinstance(name, str): + raise ValueError( + f"Observatories can only be looked up with strings not {repr(name)}" + ) if name == "": raise KeyError("No observatory name or code provided") diff --git a/src/pint/pintk/__init__.py b/src/pint/pintk/__init__.py index e69de29bb..1faa69b89 100644 --- a/src/pint/pintk/__init__.py +++ b/src/pint/pintk/__init__.py @@ -0,0 +1,5 @@ +"""Interactive pulsar timing tool and supporting code. + +The top level user interface is an instance of :class:`~pint.pintk.plk.PlkWidget`, +and most operations are implemented in the UI-free :class:`~pint.pintk.pulsar.Pulsar`. +""" diff --git a/src/pint/pintk/colormodes.py b/src/pint/pintk/colormodes.py index 4c7c23e43..5d06d0b5a 100644 --- a/src/pint/pintk/colormodes.py +++ b/src/pint/pintk/colormodes.py @@ -40,6 +40,8 @@ def plotColorMode(self): """ Plots application's residuals in proper color scheme. """ + jumped = self.application.psr.jumped + if self.application.yerrs is None: self.application.plkAxes.scatter( self.application.xvals[~self.application.selected], @@ -48,8 +50,8 @@ def plotColorMode(self): color="blue", ) self.application.plkAxes.scatter( - self.application.xvals[self.application.jumped], - self.application.yvals[self.application.jumped], + self.application.xvals[jumped], + self.application.yvals[jumped], marker=".", color="red", ) @@ -61,7 +63,7 @@ def plotColorMode(self): ) else: self.application.plotErrorbar(~self.application.selected, color="blue") - self.application.plotErrorbar(self.application.jumped, color="red") + self.application.plotErrorbar(jumped, color="red") self.application.plotErrorbar(self.application.selected, color="orange") diff --git a/src/pint/pintk/plk.py b/src/pint/pintk/plk.py index 59cd16eef..caa13ac84 100644 --- a/src/pint/pintk/plk.py +++ b/src/pint/pintk/plk.py @@ -13,6 +13,7 @@ import pint.pintk.pulsar as pulsar import pint.pintk.colormodes as cm +import pint.models.parameter import tkinter as tk import tkinter.filedialog as tkFileDialog @@ -61,41 +62,23 @@ helpstring = """The following interactions are currently supported by the Plk pane in the PINTkinter GUI: Left click: Select a point - Right click: Delete a point - r: Reset the pane - undo all deletions, selections, etc. - k: (K)orrect the pane - rescale the axes - f: Perform a fit on the selected points - d: Delete the highlighted points - u: Undo the most recent selection - c: Clear highlighter from map - j: Jump the selected points, or unjump them if already jumped - v: Jump all TOA groups except those selected - i: Print the prefit model as of this moment - o: Print the postfit model as of this moment (if it exists) - p: Print info about highlighted points (or all, if none are selected) - m: Print the range of MJDs with the highest density of TOAs - +: Increase pulse number for selected points - -: Decrease pulse number for selected points - >: Increase pulse number for all points to the right of selection - <: Decrease pulse number for all points to the right of selection - h: Print help """ @@ -162,10 +145,15 @@ def addFitCheckBoxes(self, model): self.compGrids.append([]) for pp, par in enumerate(showpars): self.parVars[par] = tk.IntVar() + pm = getattr(model, par) + if isinstance(pm, pint.models.parameter.maskParameter): + par_text = f"{pm.origin_name} {pm.key} {pm.key_value}" + else: + par_text = par self.compGrids[ii].append( tk.Checkbutton( self, - text=par, + text=par_text, variable=self.parVars[par], command=lambda p=par: self.changedFitCheckBox(p), ) @@ -553,7 +541,6 @@ def update(self): if self.psr is not None: self.psr.update_resids() self.selected = np.zeros(self.psr.all_toas.ntoas, dtype=bool) - self.jumped = np.zeros(self.psr.all_toas.ntoas, dtype=bool) self.actionsWidget.setFitButtonText("Fit") self.fitboxesWidget.addFitCheckBoxes(self.psr.prefit_model) self.randomboxWidget.addRandomCheckbox(self) @@ -567,16 +554,8 @@ def update(self): def setPulsar(self, psr, updates): self.psr = psr - # self.selected & self.jumped = boolean arrays, len = all_toas, True = selected/jumped + # self.selected boolean array, len = all_toas, True = selected self.selected = np.zeros(self.psr.all_toas.ntoas, dtype=bool) - self.jumped = np.zeros(self.psr.all_toas.ntoas, dtype=bool) - # update jumped with any jump params already in the file - for param in self.psr.prefit_model.params: - if ( - param.startswith("JUMP") - and getattr(self.psr.prefit_model, param).frozen == False - ): - self.updateJumped(getattr(self.psr.prefit_model, param).name) self.update_callbacks = updates if not hasattr(self, "base_state"): @@ -587,7 +566,6 @@ def setPulsar(self, psr, updates): self.psr.selected_toas.table["flags"] ) self.base_state.selected = copy.deepcopy(self.selected) - self.base_state.jumped = copy.deepcopy(self.jumped) self.state_stack.append(self.base_state) self.fitboxesWidget.setCallbacks(self.fitboxChecked) @@ -628,8 +606,6 @@ def fitboxChecked(self, parchanged, newstate): getattr(self.psr.prefit_model, parchanged).frozen = not newstate if self.psr.fitted: getattr(self.psr.postfit_model, parchanged).frozen = not newstate - if parchanged.startswith("JUMP"): - self.updateJumped(parchanged) self.call_updates() self.updatePlot(keepAxes=True) @@ -647,9 +623,6 @@ def fit(self): fit the selected points using the current pre-fit model """ if not self.psr is None: - # check jumps wont cancel fit, if so, exit here - if self.check_jump_invalid() == True: - return None if self.psr.fitted: # append the current state to the state stack self.current_state.psr = copy.deepcopy(self.psr) @@ -659,7 +632,6 @@ def fit(self): self.current_state.t_flags = copy.deepcopy( self.psr.selected_toas.table["flags"] ) - self.current_state.jumped = copy.deepcopy(self.jumped) self.state_stack.append(copy.deepcopy(self.current_state)) self.psr.fit(self.selected) self.current_state.selected = copy.deepcopy(self.selected) @@ -669,13 +641,6 @@ def fit(self): self.colorModeWidget.addColorModeCheckbox(self.color_modes) xid, yid = self.xyChoiceWidget.plotIDs() self.xyChoiceWidget.setChoice(xid=xid, yid="post-fit") - self.jumped = np.zeros(self.psr.all_toas.ntoas, dtype=bool) - for param in self.psr.prefit_model.params: - if ( - param.startswith("JUMP") - and getattr(self.psr.prefit_model, param).frozen == False - ): - self.updateJumped(getattr(self.psr.prefit_model, param).name) self.updatePlot(keepAxes=False) self.call_updates() @@ -691,13 +656,6 @@ def reset(self): self.psr.all_toas.table["flags"] = copy.deepcopy(self.base_state.ft_flags) self.psr.selected_toas.table["flags"] = copy.deepcopy(self.base_state.t_flags) self.selected = np.zeros(self.psr.all_toas.ntoas, dtype=bool) - self.jumped = np.zeros(self.psr.all_toas.ntoas, dtype=bool) - for param in self.psr.prefit_model.params: - if ( - param.startswith("JUMP") - and getattr(self.psr.prefit_model, param).frozen == False - ): - self.updateJumped(param) self.actionsWidget.setFitButtonText("Fit") self.fitboxesWidget.addFitCheckBoxes(self.base_state.psr.prefit_model) self.randomboxWidget.addRandomCheckbox(self) @@ -726,7 +684,8 @@ def writePar(self): % filename ) fout.close() - except: + except Exception as e: + log.error(f"Exception occurred: {e}") if filename == () or filename == "": print("Write Par cancelled.") else: @@ -744,7 +703,8 @@ def writeTim(self): try: log.info("Choose output file %s" % filename) self.psr.all_toas.write_TOA_file(filename, format="TEMPO2") - except: + except Exception as e: + log.error(f"Exception occurred: {e}") if filename == () or filename == "": print("Write Tim cancelled.") else: @@ -759,7 +719,6 @@ def revert(self): self.psr = copy.deepcopy(c_state.psr) self.psr.all_toas.table["flags"] = copy.deepcopy(c_state.ft_flags) self.psr.selected_toas.table["flags"] = copy.deepcopy(c_state.t_flags) - self.jumped = copy.deepcopy(c_state.jumped) self.selected = copy.deepcopy(c_state.selected) self.fitboxesWidget.addFitCheckBoxes(self.psr.prefit_model) self.randomboxWidget.addRandomCheckbox(self) @@ -896,15 +855,15 @@ def plotResiduals(self, keepAxes=False): self.plkAx2x.yaxis.set_major_locator( mpl.ticker.FixedLocator(self.plkAxes.get_yticks() * f0) ) - except: - pass + except Exception as e: + log.error(f"Exception occurred: {e}") else: self.plkAxes.set_ylabel(plotlabels[self.yid]) self.plkAxes.set_title(self.psr.name, y=1.1) # plot random models - if self.psr.fitted == True and self.randomboxWidget.getRandomModel() == 1: + if self.psr.fitted and self.randomboxWidget.getRandomModel() == 1: log.info("plotting random models") f_toas = self.psr.fake_toas print("Computing random models based on parameter covariance matrix...") @@ -973,8 +932,8 @@ def print_info(self): f0x = r.get_PSR_freq().to(u.MHz).value header += " %16s" % plotlabels[self.xid][1] xf = True - except: - pass + except Exception as e: + log.error(f"Exception occurred: {e}") else: header += " %16s" % plotlabels[self.xid] if self.yid in ["pre-fit", "post-fit"]: @@ -988,8 +947,8 @@ def print_info(self): f0y = r.get_PSR_freq().to(u.MHz).value header += " %16s" % plotlabels[self.yid][1] yf = True - except: - pass + except Exception as e: + log.error(f"Exception occurred: {e}") else: header += "%12s" % plotlabels[self.yid] @@ -1003,8 +962,8 @@ def print_info(self): self.psr.selected_toas.table["flags"] keys = True header += "%18s" % "Flags" - except: - pass + except Exception as e: + log.error(f"Exception occurred: {e}") print(header) print("-" * len(header)) @@ -1028,7 +987,8 @@ def print_info(self): line += ( " %1s" % self.psr.selected_toas.table["flags"][i][key] ) - except: + except Exception as e: + log.error(f"Exception occurred: {e}") line += ( " %16.8g" % self.psr.selected_toas.table["flags"][i][key] @@ -1041,7 +1001,8 @@ def print_info(self): line2 += ( " %1s" % self.psr.selected_toas.table["flags"][i][key] ) - except: + except Exception as e: + log.error(f"Exception occurred: {e}") line2 += ( " %16.8g" % self.psr.selected_toas.table["flags"][i][key] @@ -1120,45 +1081,6 @@ def coordToPoint(self, cx, cy): return ind - def check_jump_invalid(self): - """checks if jumps will cancel the attempted fit""" - if "PhaseJump" not in self.psr.prefit_model.components: - return False - fit_jumps = [] - for param in self.psr.prefit_model.params: - if getattr( - self.psr.prefit_model, param - ).frozen == False and param.startswith("JUMP"): - fit_jumps.append(int(param[4:])) - jumps = [ - "jump" in dict.keys() and any(np.in1d(dict["jump"], fit_jumps)) - for dict in self.psr.selected_toas.table["flags"] - ] - if all(jumps): - log.warn( - "toas being fit must not all be jumped. Remove or uncheck at least one jump in the selected toas before fitting." - ) - return True - - def updateJumped(self, jump_name): - """update self.jumped for the jump given""" - # if removing a jump, add_jump returns a boolean array rather than a name - if type(jump_name) == list: - self.jumped[jump_name] = False - return None - elif type(jump_name) != str: - log.error( - jump_name, - "Return value for the jump name is not a string, jumps not updated", - ) - return None - num = jump_name[4:] # string value - jump_select = [ - True if ("jump" in dict.keys() and num in dict["jump"]) else False - for dict in self.psr.all_toas.table["flags"] - ] - self.jumped[jump_select] = ~self.jumped[jump_select] - def canvasClickEvent(self, event): """ Call this function when the figure/canvas is clicked @@ -1201,64 +1123,48 @@ def stationaryClick(self, event): """ if event.inaxes == self.plkAxes: ind = self.coordToPoint(event.xdata, event.ydata) - if ind is not None: - # TODO: right click to delete doesn't work, needs to be reinstated - if event.button == 3: - # Right click is delete - # if the point is jumped, tell the user to delete the jump first - jumped_copy = copy.deepcopy(self.jumped) - for ( - param - ) in self.psr.prefit_model.params: # check for jumps in file - if ( - param.startswith("JUMP") - and getattr(self.psr.prefit_model, param).frozen == True - ): - self.updateJumped(param) - all_jumped = copy.deepcopy(self.jumped) - self.jumped = jumped_copy - # check if point to be deleted is jumped - if all_jumped[ind] == True: - log.warn( - "cannot delete jumped toas. Delete interfering jumps before deleting toas." + if ind is None: + return + # TODO: right click to delete doesn't work, needs to be reinstated + if event.button == 3: + # Right click is delete + # create boolean array to readjust all_toas without point to be deleted + toas_to_delete = np.zeros(self.psr.all_toas.ntoas, dtype=bool) + toas_to_delete[ind] = True + # FIXME: we now have TOAs indexing for deletion, not sure what all this + # table_selects business is + self.psr.all_toas.table = self.psr.all_toas.table[ + ~toas_to_delete + ].group_by("obs") + # adjust selected_toas to make sure it excludes toa to be deleted + self.psr.selected_toas = copy.deepcopy(self.psr.all_toas) + if hasattr(self.psr.all_toas, "table_selects"): + for i in range(len(self.psr.all_toas.table_selects)): + self.psr.all_toas.table_selects[ + i + ] = self.psr.all_toas.table_selects[i][ + ~toas_to_delete + ].group_by( + "obs" ) - return None - # create boolean array to readjust all_toas without point to be deleted - toas_to_delete = np.zeros(self.psr.all_toas.ntoas, dtype=bool) - toas_to_delete[ind] = True - self.psr.all_toas.table = self.psr.all_toas.table[ - ~toas_to_delete - ].group_by("obs") - # adjust selected_toas to make sure it excludes toa to be deleted + # update jumps and rest of graph + # FIXME: why forget the selection completely? + self.selected = np.zeros(self.psr.all_toas.ntoas, dtype=bool) + self.psr.update_resids() + self.updatePlot(keepAxes=True) + self.call_updates() + elif event.button == 1: + # Left click is select + self.selected[ind] = not self.selected[ind] + self.updatePlot(keepAxes=True) + # if point is being selected (instead of unselected) or + # point is unselected but other points remain selected + if self.selected[ind] or any(self.selected): + # update selected_toas object w/ selected points self.psr.selected_toas = copy.deepcopy(self.psr.all_toas) - if hasattr(self.psr.all_toas, "table_selects"): - for i in range(len(self.psr.all_toas.table_selects)): - self.psr.all_toas.table_selects[ - i - ] = self.psr.all_toas.table_selects[i][ - ~toas_to_delete - ].group_by( - "obs" - ) - # update jumps and rest of graph - self.jumped = self.jumped[~toas_to_delete] - print(self.jumped) - self.selected = np.zeros(self.psr.all_toas.ntoas, dtype=bool) + self.psr.selected_toas.select(self.selected) self.psr.update_resids() - self.updatePlot(keepAxes=True) self.call_updates() - if event.button == 1: - # Left click is select - self.selected[ind] = not self.selected[ind] - self.updatePlot(keepAxes=True) - # if point is being selected (instead of unselected) or - # point is unselected but other points remain selected - if self.selected[ind] or any(self.selected): - # update selected_toas object w/ selected points - self.psr.selected_toas = copy.deepcopy(self.psr.all_toas) - self.psr.selected_toas.select(self.selected) - self.psr.update_resids() - self.call_updates() def clickAndDrag(self, event): """ @@ -1287,7 +1193,8 @@ def canvasKeyEvent(self, event): A key is pressed. Handle all the shortcuts here """ fkey = event.key - xpos, ypos = event.xdata, event.ydata + # Position of key press ignored + # xpos, ypos = event.xdata, event.ydata ukey = ord(fkey[-1]) if ukey == ord("r"): @@ -1325,22 +1232,8 @@ def canvasKeyEvent(self, event): self.call_updates() log.info("Pulse numbers to the right of selection decreased.") elif ukey == ord("d"): - # if any of the points are jumped, tell the user to delete the jump(s) first - jumped_copy = copy.deepcopy(self.jumped) - for param in self.psr.prefit_model.params: - if ( - param.startswith("JUMP") - and getattr(self.psr.prefit_model, param).frozen == True - ): - self.updateJumped(param) - all_jumped = copy.deepcopy(self.jumped) - self.jumped = jumped_copy - if True in [a and b for a, b in zip(self.selected, all_jumped)]: - log.warn( - "cannot delete jumped toas. Delete interfering jumps before deleting toas." - ) - return None - # Delete the selected points + # FIXME: now we have self.psr.all_toas[~self.selected] - directly acting on TOAs + # but I'm not sure what all the other stuff here is supposed to do? self.psr.all_toas.table = self.psr.all_toas.table[~self.selected].group_by( "obs" ) @@ -1352,7 +1245,6 @@ def canvasKeyEvent(self, event): ] = self.psr.all_toas.table_selects[i][~self.selected].group_by( "obs" ) - self.jumped = self.jumped[~self.selected] self.selected = np.zeros(self.psr.all_toas.ntoas, dtype=bool) self.psr.update_resids() self.updatePlot(keepAxes=True) @@ -1360,39 +1252,16 @@ def canvasKeyEvent(self, event): elif ukey == ord("u"): self.unselect() elif ukey == ord("j"): - # jump the selected points, or unjump if already jumped - jump_name = self.psr.add_jump(self.selected) - self.updateJumped(jump_name) + self.psr.add_jump(self.selected) self.fitboxesWidget.addFitCheckBoxes(self.psr.prefit_model) self.randomboxWidget.addRandomCheckbox(self) self.colorModeWidget.addColorModeCheckbox(self.color_modes) self.updatePlot(keepAxes=True) self.call_updates() elif ukey == ord("v"): - # jump all groups except the one(s) selected, or jump all groups if none selected - jumped_copy = copy.deepcopy(self.jumped) - for param in self.psr.prefit_model.params: - if ( - param.startswith("JUMP") - and getattr(self.psr.prefit_model, param).frozen == True - ): - self.updateJumped(param) - all_jumped = copy.deepcopy(self.jumped) - self.jumped = jumped_copy - groups = list(self.psr.all_toas.table["groups"]) - # jump each group, check doesn't overlap with existing jumps and selected - for num in np.arange(max(groups) + 1): - group_bool = [ - num == group for group in self.psr.all_toas.table["groups"] - ] - if True in [ - a and b for a, b in zip(group_bool, self.selected) - ] or True in [a and b for a, b in zip(group_bool, all_jumped)]: - continue - self.psr.selected_toas = copy.deepcopy(self.psr.all_toas) - self.psr.selected_toas.select(group_bool) - jump_name = self.psr.add_jump(group_bool) - self.updateJumped(jump_name) + # FIXME: jump all groups except the one(s) selected, or jump all groups if none selected + # Easiest just to walk through all groups and ise .add_jump for each iff they do not + # intersect with the selected; what to do with overlapping/identical JUMPs? self.psr.selected_toas = copy.deepcopy(self.psr.all_toas) if ( self.selected is not None @@ -1406,7 +1275,8 @@ def canvasKeyEvent(self, event): self.updatePlot(keepAxes=True) self.call_updates() elif ukey == ord("c"): - self.selected = np.zeros(self.psr.selected_toas.ntoas, dtype=bool) + # FIXME: how does this differ from unselect()? + self.selected = np.zeros(len(self.psr.all_toas), dtype=bool) self.updatePlot(keepAxes=True) elif ukey == ord("i"): log.info("PREFIT MODEL") diff --git a/src/pint/pintk/pulsar.py b/src/pint/pintk/pulsar.py index fdaf9a38a..c5fb0dbcb 100644 --- a/src/pint/pintk/pulsar.py +++ b/src/pint/pintk/pulsar.py @@ -64,8 +64,6 @@ class Pulsar: """ def __init__(self, parfile=None, timfile=None, ephem=None): - super(Pulsar, self).__init__() - log.info("STARTING LOADING OF PULSAR %s" % str(parfile)) if parfile is not None and timfile is not None: @@ -88,11 +86,7 @@ def __init__(self, parfile=None, timfile=None, ephem=None): self.all_toas = get_TOAs(self.timfile, planets=True) # turns pre-existing jump flags in toas.table['flags'] into parameters in parfile - # TODO: fix jump_flags_to_params self.prefit_model.jump_flags_to_params(self.all_toas) - # adds flags to toas.table for existing jump parameters from .par file - if "PhaseJump" in self.prefit_model.components: - self.prefit_model.jump_params_to_flags(self.all_toas) self.selected_toas = copy.deepcopy(self.all_toas) print("prefit_model.as_parfile():") print(self.prefit_model.as_parfile()) @@ -112,7 +106,25 @@ def __init__(self, parfile=None, timfile=None, ephem=None): def name(self): return getattr(self.prefit_model, "PSR").value + @property + def jumped(self): + """Boolean array indicating which TOAs are affected by a JUMP. + + This reports only those affected by unfrozen jumps, and the boolean + array matches ``self.all_toas``. + """ + jumped = np.zeros(len(self.all_toas), dtype=bool) + # FIXME: prefit or postfit? + model = self.prefit_model + if hasattr(model, "jumps"): + for pm in model.jumps: + if not pm.frozen: + jumped[pm.select_toa_mask(self.all_toas)] = True + log.info(f"Model jumps {jumped.sum()} of {len(jumped)} TOAs.") + return jumped + def __getitem__(self, key): + # FIXME: why use this instead of just looking it up in prefit_model? try: return getattr(self.prefit_model, key) except AttributeError: @@ -284,76 +296,84 @@ def add_phase_wrap(self, selected, phase): self.update_resids() def add_jump(self, selected): - """ - jump the toas selected or unjump them if already jumped + """Jump the toas selected or unjump them if already jumped. - :param selected: boolean array to apply to toas, True = selected toa + This uses the ``-gui_jump`` flag. + + Possible cases: + + - No TOAs are part of any GUI jump: add a GUI jump flag and parameter for + these TOAs. + - All TOAs are part of the same GUI jump: remove the flag from these TOAs; + if no TOAs remain for the GUI jump, remove the parameter. + - TOAs are a mix of different GUI jumps, or some are GUI jumped and some + are not: raise an exception. + + Parameter + --------- + selected: boolean array + Which TOAs to apply to. """ - # TODO: split into two functions + if "PhaseJump" not in self.prefit_model.components: - # if no PhaseJump component, add one log.info("PhaseJump component added") a = pint.models.jump.PhaseJump() a.setup() self.prefit_model.add_component(a) - retval = self.prefit_model.add_jump_and_flags( + + log.info("Trying to add a jump from the GUI") + gui_jump_flags = set() + for f in self.all_toas.table["flags"][selected]: + gui_jump_flags.add(f.get("gui_jump", None)) + log.info(f"GUI jump flags present: {gui_jump_flags}") + if None in gui_jump_flags: + if len(gui_jump_flags) > 1: + raise ValueError("Some TOAs are already jumped") + # No jumps, add some! + log.info("Adding a GUI jump for these TOAs") + new_name = self.prefit_model.add_jump_and_flags( self.all_toas.table["flags"][selected] ) - if self.fitted: - self.postfit_model.add_component(a) - return retval - # if gets here, has at least one jump param already - # if doesnt overlap or cancel, add the param - numjumps = self.prefit_model.components["PhaseJump"].get_number_of_jumps() - if numjumps == 0: - log.warn( - "There are no jumps (maskParameter objects) in PhaseJump. Please delete the PhaseJump object and try again. " + log.info( + f"Added new GUI jump parameter {getattr(self.prefit_model, new_name)}" ) - return None - # delete jump if perfectly overlaps any existing jump - for num in range(1, numjumps + 1): - # create boolean array corresponding to TOAs to be jumped - toas_jumped = [ - "jump" in dict.keys() and str(num) in dict["jump"] - for dict in self.all_toas.table["flags"] - ] - if np.array_equal(toas_jumped, selected): - # if current jump exactly matches selected, remove it - self.prefit_model.delete_jump_and_flags( - self.all_toas.table["flags"], num + else: + if len(gui_jump_flags) > 1: + raise ValueError("TOAs are from different GUI jumps") + (the_value,) = gui_jump_flags + log.info(f"All TOAs appear JUMPed with {the_value}, removing flags") + for f in self.all_toas.table["flags"][selected]: + del gui_jump_flags["gui_jump"] + param_to_zap = None + for (k, kv), pm in self.prefit_model.jumps: + if k == "-gui_jump" and kv == the_value: + param_to_zap = pm + break + else: + raise ValueError( + f"Unable to find JUMP parameter for -gui_jump {the_value}" ) - if self.fitted: - self.postfit_model.delete_jump_and_flags(None, num) - log.info("removed param", "JUMP" + str(num)) - return toas_jumped - # if here, then doesn't match anything - # add jump flags to selected TOAs at their perspective indices in the TOA tables - retval = self.prefit_model.add_jump_and_flags( - self.all_toas.table["flags"][selected] - ) - if ( - self.fitted - and not self.prefit_model.components["PhaseJump"] - == self.postfit_model.components["PhaseJump"] - ): - param = self.prefit_model.components[ - "PhaseJump" - ].get_jump_param_objects() # array of jump objects - self.postfit_model.add_param_from_top( - param[-1], "PhaseJump" - ) # add last (newest) jump - getattr(self.postfit_model, param[-1].name).frozen = False - self.postfit_model.components["PhaseJump"].setup() - return retval + if len(param_to_zap.select_toa_mask(self.all_toas)) == 0: + # Parameter no longer selects any TOAs + log.info( + f"No remaining TOAs appear JUMPed with {the_value}, removing {param_to_zap}" + ) + self.prefit_model.remove_param(param_to_zap.name) def fit(self, selected, iters=1): - """ - Run a fit using the specified fitter - """ + """Run a fit using the specified fitter.""" # Select all the TOAs if none are explicitly set if not any(selected): selected = ~selected + if "PhaseJump" in self.prefit_model.components: + # Modifies jump flags. If attempted fit (selected) + # A) contains only jumps, don't do the fit and return an error + # B) excludes a jump, turn that jump off + # C) partially contains a jump, redefine that jump only with the overlap + + self.prefit_model.tidy_jumps_for_fit(self.selected_toas) + if self.fitted: self.prefit_model = self.postfit_model self.prefit_resids = self.postfit_resids diff --git a/src/pint/scripts/pintempo.py b/src/pint/scripts/pintempo.py index 65bbd6f69..5743d692b 100755 --- a/src/pint/scripts/pintempo.py +++ b/src/pint/scripts/pintempo.py @@ -56,10 +56,6 @@ def main(argv=None): # turns pre-existing jump flags in t.table['flags'] into parameters in parfile m.jump_flags_to_params(t) - # adds jump flags to t.table['flags'] for jump parameters already in parfile - if "PhaseJump" in m.components: - m.jump_params_to_flags(t) - if m.TRACK.value == "-2": if "pn" in t.table.colnames: log.info("Already have pulse numbers from TOA flags.") diff --git a/tests/test_B1855.py b/tests/test_B1855.py index 20a594c0d..16ba8626c 100644 --- a/tests/test_B1855.py +++ b/tests/test_B1855.py @@ -6,12 +6,12 @@ import astropy.units as u import numpy as np import pytest +import test_derivative_utils as tdu +from pinttestdata import datadir import pint.models.model_builder as mb import pint.toa as toa -import test_derivative_utils as tdu from pint.residuals import Residuals -from pinttestdata import datadir class TestB1855(unittest.TestCase): diff --git a/tests/test_B1855_9yrs.py b/tests/test_B1855_9yrs.py index 59549ad16..beee1a4e5 100644 --- a/tests/test_B1855_9yrs.py +++ b/tests/test_B1855_9yrs.py @@ -5,12 +5,12 @@ import astropy.units as u import numpy as np +import test_derivative_utils as tdu +from pinttestdata import datadir import pint.models.model_builder as mb import pint.toa as toa -import test_derivative_utils as tdu from pint.residuals import Residuals -from pinttestdata import datadir class TestB1855(unittest.TestCase): diff --git a/tests/test_B1953.py b/tests/test_B1953.py index 9946b5b1b..f8287db49 100644 --- a/tests/test_B1953.py +++ b/tests/test_B1953.py @@ -1,16 +1,16 @@ """Various tests to assess the performance of the B1953+29.""" -from astropy import log import os import unittest import astropy.units as u import numpy as np +import test_derivative_utils as tdu +from astropy import log +from pinttestdata import datadir import pint.models.model_builder as mb import pint.toa as toa -import test_derivative_utils as tdu from pint.residuals import Residuals -from pinttestdata import datadir class TestB1953(unittest.TestCase): diff --git a/tests/test_Galactic.py b/tests/test_Galactic.py index 24e66ae3e..f0e01b9d5 100644 --- a/tests/test_Galactic.py +++ b/tests/test_Galactic.py @@ -2,19 +2,18 @@ import os import unittest +import astropy.coordinates +import astropy.time import astropy.units as u import numpy as np +import test_derivative_utils as tdu +from pinttestdata import datadir import pint.models.model_builder as mb import pint.toa as toa -import test_derivative_utils as tdu -from pint.residuals import Residuals -from pinttestdata import datadir -from pint.pulsar_ecliptic import PulsarEcliptic from pint import utils - -import astropy.coordinates -import astropy.time +from pint.pulsar_ecliptic import PulsarEcliptic +from pint.residuals import Residuals class TestGalactic(unittest.TestCase): diff --git a/tests/test_J0613.py b/tests/test_J0613.py index 2181aeb41..89ccab5ee 100644 --- a/tests/test_J0613.py +++ b/tests/test_J0613.py @@ -5,12 +5,12 @@ import astropy.units as u import numpy as np +import test_derivative_utils as tdu +from pinttestdata import datadir import pint.models.model_builder as mb import pint.toa as toa -import test_derivative_utils as tdu from pint.residuals import Residuals -from pinttestdata import datadir class TestJ0613(unittest.TestCase): diff --git a/tests/test_TDB_method.py b/tests/test_TDB_method.py index 927ae72d8..ceeb705fb 100644 --- a/tests/test_TDB_method.py +++ b/tests/test_TDB_method.py @@ -3,9 +3,9 @@ import unittest import numpy as np +from pinttestdata import datadir import pint.toa as toa -from pinttestdata import datadir class TestTDBMethod(unittest.TestCase): diff --git a/tests/test_absphase.py b/tests/test_absphase.py index 053f15c6b..73419a743 100644 --- a/tests/test_absphase.py +++ b/tests/test_absphase.py @@ -2,9 +2,10 @@ import os import unittest +from pinttestdata import datadir + import pint.models import pint.toa -from pinttestdata import datadir parfile = os.path.join(datadir, "NGC6440E.par") timfile = os.path.join(datadir, "zerophase.tim") diff --git a/tests/test_astrometry.py b/tests/test_astrometry.py index 152cd7c27..4c2e3da4d 100644 --- a/tests/test_astrometry.py +++ b/tests/test_astrometry.py @@ -5,9 +5,9 @@ import numpy as np import pytest from astropy.coordinates import Latitude, Longitude +from pinttestdata import datadir from pint.models import get_model -from pinttestdata import datadir @pytest.fixture diff --git a/tests/test_astropyobservatory.py b/tests/test_astropyobservatory.py index bbcb05f9e..cfa1c6880 100644 --- a/tests/test_astropyobservatory.py +++ b/tests/test_astropyobservatory.py @@ -1,6 +1,8 @@ import logging import unittest + import numpy as np + import pint.observatory diff --git a/tests/test_barytoa.py b/tests/test_barytoa.py index 699c2f470..527fe3199 100644 --- a/tests/test_barytoa.py +++ b/tests/test_barytoa.py @@ -2,13 +2,13 @@ import os import astropy.units as u +from pinttestdata import datadir import pint.fitter import pint.models -from pint.models.model_builder import get_model import pint.residuals import pint.toa -from pinttestdata import datadir +from pint.models.model_builder import get_model def test_barytoa(): diff --git a/tests/test_binary_generic.py b/tests/test_binary_generic.py index 180d89c38..b7cac97e4 100644 --- a/tests/test_binary_generic.py +++ b/tests/test_binary_generic.py @@ -1,17 +1,17 @@ """Tests of PINT generic binary model """ import logging -from os.path import basename, join -from glob import glob import unittest -import pytest +from glob import glob +from os.path import basename, join from warnings import warn -from pint.models.model_builder import get_model -from pint.models.timing_model import MissingParameter, TimingModel, Component -from utils import verify_stand_alone_binary_parameter_updates +import pytest from pinttestdata import datadir +from utils import verify_stand_alone_binary_parameter_updates +from pint.models.model_builder import get_model +from pint.models.timing_model import Component, MissingParameter, TimingModel bad_trouble = ["J1923+2515_NANOGrav_9yv1.gls.par", "J1744-1134.basic.ecliptic.par"] diff --git a/tests/test_clockcorr.py b/tests/test_clockcorr.py index ee4d13f5e..75792a4b1 100644 --- a/tests/test_clockcorr.py +++ b/tests/test_clockcorr.py @@ -1,12 +1,12 @@ +import os import unittest +from os import path -import pytest -import os import astropy.units as u import numpy -from pinttestdata import datadir -from os import path +import pytest from astropy.time import Time +from pinttestdata import datadir from pint.observatory import Observatory from pint.observatory.clock_file import ClockFile diff --git a/tests/test_compare.py b/tests/test_compare.py index 1f876d984..a2768c691 100644 --- a/tests/test_compare.py +++ b/tests/test_compare.py @@ -1,13 +1,15 @@ -import numpy as np +import io +import os import unittest +from copy import deepcopy as cp + import astropy import astropy.units as u +import numpy as np +from pinttestdata import datadir + import pint import pint.models as mod -import os -import io -from copy import deepcopy as cp -from pinttestdata import datadir class TestCompare(unittest.TestCase): diff --git a/tests/test_copy.py b/tests/test_copy.py index f735f9048..112dd530c 100644 --- a/tests/test_copy.py +++ b/tests/test_copy.py @@ -1,17 +1,18 @@ """ Test for pint object copying """ -import os -import pytest -import numpy as np import copy +import os import sys import astropy.units as u -from pint.models import get_model +import numpy as np +import pytest +from pinttestdata import datadir + from pint.fitter import WidebandTOAFitter +from pint.models import get_model from pint.toa import get_TOAs -from pinttestdata import datadir os.chdir(datadir) diff --git a/tests/test_covariance_matrix.py b/tests/test_covariance_matrix.py index a100aba2d..2eebdc013 100644 --- a/tests/test_covariance_matrix.py +++ b/tests/test_covariance_matrix.py @@ -1,15 +1,15 @@ """ Various of tests for the pint covariance. """ -import pytest import os -import numpy as np import astropy.units as u -from pint.pint_matrix import CovarianceMatrix, combine_covariance_matrix - +import numpy as np +import pytest from pinttestdata import datadir +from pint.pint_matrix import CovarianceMatrix, combine_covariance_matrix + os.chdir(datadir) diff --git a/tests/test_d_phase_d_toa.py b/tests/test_d_phase_d_toa.py index 8cc0fe206..db44f3ae9 100644 --- a/tests/test_d_phase_d_toa.py +++ b/tests/test_d_phase_d_toa.py @@ -8,10 +8,11 @@ except ImportError: from astropy._erfa import DJM0 +from pinttestdata import datadir, testdir + import pint.toa as toa from pint.models import model_builder as mb from pint.polycos import Polycos -from pinttestdata import datadir, testdir class TestD_phase_d_toa(unittest.TestCase): diff --git a/tests/test_datafiles.py b/tests/test_datafiles.py index 4c693fe2a..32e0fe0e4 100644 --- a/tests/test_datafiles.py +++ b/tests/test_datafiles.py @@ -1,8 +1,10 @@ """Test installation of PINT data files""" import os +import tempfile + import pytest + import pint.config -import tempfile @pytest.fixture diff --git a/tests/test_dd.py b/tests/test_dd.py index 3c356051c..cc55756bb 100644 --- a/tests/test_dd.py +++ b/tests/test_dd.py @@ -4,12 +4,12 @@ import astropy.units as u import numpy as np +from pinttestdata import datadir +from utils import verify_stand_alone_binary_parameter_updates import pint.models.model_builder as mb import pint.toa as toa -from utils import verify_stand_alone_binary_parameter_updates from pint.residuals import Residuals -from pinttestdata import datadir class TestDD(unittest.TestCase): diff --git a/tests/test_ddk.py b/tests/test_ddk.py index 7cd14e874..8d16ca55f 100644 --- a/tests/test_ddk.py +++ b/tests/test_ddk.py @@ -174,7 +174,3 @@ def test_remove_PX(): m.remove_param("PX") with pytest.raises(MissingParameter): m.validate() - - -if __name__ == "__main__": - pass diff --git a/tests/test_density.py b/tests/test_density.py index 907f2dcea..097873941 100644 --- a/tests/test_density.py +++ b/tests/test_density.py @@ -1,14 +1,16 @@ -import numpy as np +import io +from copy import deepcopy + import astropy.units as u -from astropy.time import Time import matplotlib.pyplot as plt -import pint.toa as toa +import numpy as np +import pytest +from astropy.time import Time +from pylab import * + import pint.simulation as simulation +import pint.toa as toa from pint.models import get_model -from pylab import * -from copy import deepcopy -import io -import pytest @pytest.mark.parametrize("ndays", [7 * u.d, 20 * u.d]) diff --git a/tests/test_derived_quantities.py b/tests/test_derived_quantities.py index cf306e1ae..28e57dc60 100644 --- a/tests/test_derived_quantities.py +++ b/tests/test_derived_quantities.py @@ -28,17 +28,17 @@ from pint.derived_quantities import ( a1sini, companion_mass, + gamma, mass_funct, mass_funct2, + omdot, + omdot_to_mtot, + pbdot, pulsar_age, pulsar_B, pulsar_B_lightcyl, pulsar_edot, pulsar_mass, - omdot, - pbdot, - gamma, - omdot_to_mtot, ) diff --git a/tests/test_design_matrix.py b/tests/test_design_matrix.py index 3944d13fc..c4d0865e8 100644 --- a/tests/test_design_matrix.py +++ b/tests/test_design_matrix.py @@ -1,17 +1,18 @@ """ Test for pint design matrix""" import os -import pytest + +import astropy.units as u import numpy as np +import pytest +from pinttestdata import datadir from pint.models import get_model -from pint.toa import get_TOAs from pint.pint_matrix import ( DesignMatrixMaker, - combine_design_matrices_by_quantity, combine_design_matrices_by_param, + combine_design_matrices_by_quantity, ) -import astropy.units as u -from pinttestdata import datadir +from pint.toa import get_TOAs class TestDesignMatrix: diff --git a/tests/test_determinism.py b/tests/test_determinism.py index 42c5db406..25f2c9cf0 100644 --- a/tests/test_determinism.py +++ b/tests/test_determinism.py @@ -5,6 +5,7 @@ import numpy as np import numpy.random from numpy.testing import assert_array_equal +from pinttestdata import datadir, testdir import pint.fermi_toas as fermi import pint.models @@ -12,7 +13,6 @@ from pint.mcmc_fitter import MCMCFitter, MCMCFitterBinnedTemplate from pint.sampler import EmceeSampler from pint.scripts.event_optimize import marginalize_over_phase, read_gaussfitfile -from pinttestdata import datadir, testdir def test_sampler(): diff --git a/tests/test_dmefac_dmequad.py b/tests/test_dmefac_dmequad.py index d8c407261..9b0ca5658 100644 --- a/tests/test_dmefac_dmequad.py +++ b/tests/test_dmefac_dmequad.py @@ -2,13 +2,12 @@ """ from io import StringIO +import astropy.units as u import numpy as np import pytest -import astropy.units as u -from pint.toa import get_TOAs from pint.models.noise_model import ScaleDmError - +from pint.toa import get_TOAs tim = """FORMAT 1 fake 1400 54000 1.0 gbt -pp_dm 57.0 -pp_dme 0.01 -fe Rcvr_800 @@ -39,8 +38,8 @@ def test_no_efact_noequad(test_toas, test_model): def test_only_one_efact(test_toas, test_model): - test_model.DMEFAC1.key = "-fe" - test_model.DMEFAC1.key_value = ["Rcvr_800"] + test_model.DMEFAC1.flag = "fe" + test_model.DMEFAC1.flag_value = "Rcvr_800" test_model.DMEFAC1.value = 10 test_model.setup() scale_sigma = test_model.scale_dm_sigma(test_toas) @@ -51,8 +50,8 @@ def test_only_one_efact(test_toas, test_model): def test_only_one_equad(test_toas, test_model): - test_model.DMEQUAD1.key = "-fe" - test_model.DMEQUAD1.key_value = ["YUPPI"] + test_model.DMEQUAD1.flag = "fe" + test_model.DMEQUAD1.flag_value = "YUPPI" test_model.DMEQUAD1.value = 10 test_model.setup() scale_sigma = test_model.scale_dm_sigma(test_toas) @@ -66,11 +65,11 @@ def test_only_one_equad(test_toas, test_model): def test_only_one_equad_one_efact_same_backend(test_toas, test_model): - test_model.DMEQUAD1.key = "-fe" - test_model.DMEQUAD1.key_value = ["Rcvr_800"] + test_model.DMEQUAD1.flag = "fe" + test_model.DMEQUAD1.flag_value = "Rcvr_800" test_model.DMEQUAD1.value = 10 - test_model.DMEFAC1.key = "-fe" - test_model.DMEFAC1.key_value = ["Rcvr_800"] + test_model.DMEFAC1.flag = "fe" + test_model.DMEFAC1.flag_value = "Rcvr_800" test_model.DMEFAC1.value = 10 test_model.setup() scale_sigma = test_model.scale_dm_sigma(test_toas) @@ -89,11 +88,11 @@ def test_only_one_equad_one_efact_same_backend(test_toas, test_model): def test_only_one_equad_one_efact_different_backend(test_toas, test_model): - test_model.DMEQUAD1.key = "-fe" - test_model.DMEQUAD1.key_value = ["Rcvr_800"] + test_model.DMEQUAD1.flag = "fe" + test_model.DMEQUAD1.flag_value = "Rcvr_800" test_model.DMEQUAD1.value = 10 - test_model.DMEFAC1.key = "-fe" - test_model.DMEFAC1.key_value = ["YUPPI"] + test_model.DMEFAC1.flag = "fe" + test_model.DMEFAC1.flag_value = "YUPPI" test_model.DMEFAC1.value = 20 test_model.setup() scale_sigma = test_model.scale_dm_sigma(test_toas) diff --git a/tests/test_dmx.py b/tests/test_dmx.py index 8e79e1519..ea618caa6 100644 --- a/tests/test_dmx.py +++ b/tests/test_dmx.py @@ -4,11 +4,11 @@ import astropy.units as u import numpy as np +from pinttestdata import datadir import pint.toa as toa from pint import residuals from pint.models import model_builder as mb -from pinttestdata import datadir class TestDMX(unittest.TestCase): diff --git a/tests/test_dmxrange_add_sub.py b/tests/test_dmxrange_add_sub.py index f215bbc8c..ab56d6835 100644 --- a/tests/test_dmxrange_add_sub.py +++ b/tests/test_dmxrange_add_sub.py @@ -1,8 +1,10 @@ """Test functions for adding and removing DMX ranges.""" -import pytest import io + import numpy as np +import pytest + from pint.models import get_model from pint.models.dispersion_model import DispersionDM, DispersionDMX from pint.utils import PrefixError diff --git a/tests/test_downhill_fitter.py b/tests/test_downhill_fitter.py index f844dbafe..747175d5a 100644 --- a/tests/test_downhill_fitter.py +++ b/tests/test_downhill_fitter.py @@ -13,8 +13,8 @@ import pint.fitter from pint.models import get_model from pint.models.timing_model import MissingTOAs -from pint.toa import merge_TOAs from pint.simulation import make_fake_toas_uniform +from pint.toa import merge_TOAs par_eccentric = """ PSR J1234+5678 diff --git a/tests/test_early_chime_data.py b/tests/test_early_chime_data.py index ccdeb0bed..112bb699a 100644 --- a/tests/test_early_chime_data.py +++ b/tests/test_early_chime_data.py @@ -5,11 +5,11 @@ import astropy.units as u import numpy as np from astropy.tests.helper import assert_quantity_allclose +from pinttestdata import datadir, testdir import pint.models.model_builder as mb import pint.toa as toa from pint.residuals import Residuals -from pinttestdata import datadir, testdir class Test_CHIME_data(unittest.TestCase): diff --git a/tests/test_ecorr_average.py b/tests/test_ecorr_average.py index a5db99d3c..16cbc9310 100644 --- a/tests/test_ecorr_average.py +++ b/tests/test_ecorr_average.py @@ -5,11 +5,12 @@ import astropy.units as u import numpy as np +from pinttestdata import datadir import pint.models.model_builder as mb from pint import toa from pint.fitter import GLSFitter -from pinttestdata import datadir + # This function can be used to recreate the data # for this test if needed. diff --git a/tests/test_ell1h.py b/tests/test_ell1h.py index 115d71a10..561f237c9 100644 --- a/tests/test_ell1h.py +++ b/tests/test_ell1h.py @@ -2,22 +2,21 @@ import logging import os import unittest -import pytest +from io import StringIO from warnings import warn import astropy.units as u import numpy as np +import pytest +import test_derivative_utils as tdu +from pinttestdata import datadir +from utils import verify_stand_alone_binary_parameter_updates import pint.fitter as ff +import pint.toa as toa from pint.models import get_model from pint.models.timing_model import TimingModelError -import pint.toa as toa -import test_derivative_utils as tdu -from utils import verify_stand_alone_binary_parameter_updates from pint.residuals import Residuals -from pinttestdata import datadir -from io import StringIO - os.chdir(datadir) diff --git a/tests/test_event_optimize.py b/tests/test_event_optimize.py index a84af5e83..0fb77f0e7 100644 --- a/tests/test_event_optimize.py +++ b/tests/test_event_optimize.py @@ -4,12 +4,12 @@ import os import sys import unittest - from io import StringIO -from pint.scripts import event_optimize from pinttestdata import datadir +from pint.scripts import event_optimize + parfile = os.path.join(datadir, "PSRJ0030+0451_psrcat.par") eventfile = os.path.join( datadir, "J0030+0451_P8_15.0deg_239557517_458611204_ft1weights_GEO_wt.gt.0.4.fits" diff --git a/tests/test_event_optimize_MCMCFitter.py b/tests/test_event_optimize_MCMCFitter.py index ce52ef503..952ccee57 100644 --- a/tests/test_event_optimize_MCMCFitter.py +++ b/tests/test_event_optimize_MCMCFitter.py @@ -3,7 +3,6 @@ # to get the fftfit module. It can be run manually by people who have PRESTO import os import unittest - from io import StringIO from pinttestdata import datadir diff --git a/tests/test_event_optimize_multiple.py b/tests/test_event_optimize_multiple.py index e67640f82..50ef1e6aa 100644 --- a/tests/test_event_optimize_multiple.py +++ b/tests/test_event_optimize_multiple.py @@ -4,7 +4,6 @@ import os import sys import unittest - from io import StringIO from pinttestdata import datadir diff --git a/tests/test_event_toas.py b/tests/test_event_toas.py index 434a1798e..da30a3d0f 100644 --- a/tests/test_event_toas.py +++ b/tests/test_event_toas.py @@ -1,10 +1,14 @@ import os -import pytest -from pint.event_toas import read_mission_info_from_heasoft, create_mission_config -from pint.event_toas import load_fits_TOAs +import pytest from pinttestdata import datadir +from pint.event_toas import ( + create_mission_config, + load_fits_TOAs, + read_mission_info_from_heasoft, +) + def test_xselect_mdb_is_found_headas(monkeypatch, tmp_path): """Test event file reading.""" diff --git a/tests/test_eventstats.py b/tests/test_eventstats.py index 5fe0f1f98..b5bc937eb 100755 --- a/tests/test_eventstats.py +++ b/tests/test_eventstats.py @@ -1,8 +1,8 @@ #!/usr/bin/env python +import numpy as np from numpy.testing import assert_allclose, assert_equal import pint.eventstats as es -import numpy as np def test_sig2sigma(): diff --git a/tests/test_fake_toas.py b/tests/test_fake_toas.py index d4092542e..d51cb5208 100644 --- a/tests/test_fake_toas.py +++ b/tests/test_fake_toas.py @@ -1,15 +1,17 @@ -import astropy.units as u -import pint.simulation -from pint.models.model_builder import get_model, get_model_and_toas -from pint.toa import get_TOAs -import pint.residuals import io -import numpy as np -import tempfile import os +import tempfile + +import astropy.units as u +import numpy as np +from pinttestdata import datadir, testdir + import pint.config +import pint.residuals +import pint.simulation from pint.fitter import GLSFitter -from pinttestdata import datadir, testdir +from pint.models.model_builder import get_model, get_model_and_toas +from pint.toa import get_TOAs def test_noise_addition(): diff --git a/tests/test_fbx.py b/tests/test_fbx.py index 6300d4c06..bc2597ed4 100644 --- a/tests/test_fbx.py +++ b/tests/test_fbx.py @@ -4,12 +4,12 @@ import astropy.units as u import numpy as np +import test_derivative_utils as tdu +from pinttestdata import datadir import pint.models.model_builder as mb import pint.toa as toa -import test_derivative_utils as tdu from pint.residuals import Residuals -from pinttestdata import datadir class TestFBX(unittest.TestCase): diff --git a/tests/test_fd.py b/tests/test_fd.py index e27aeb5c6..3dc62bb4f 100644 --- a/tests/test_fd.py +++ b/tests/test_fd.py @@ -2,15 +2,15 @@ import copy import os import unittest -import pytest import astropy.units as u import numpy as np +import pytest +from pinttestdata import datadir import pint.models.model_builder as mb import pint.residuals import pint.toa as toa -from pinttestdata import datadir class TestFD(unittest.TestCase): diff --git a/tests/test_fermiphase.py b/tests/test_fermiphase.py index a2d570621..e091bf20c 100644 --- a/tests/test_fermiphase.py +++ b/tests/test_fermiphase.py @@ -2,18 +2,17 @@ import os import sys import unittest - -import pytest from io import StringIO +import pytest from astropy.io import fits +from pinttestdata import datadir import pint.models import pint.scripts.fermiphase as fermiphase import pint.toa as toa from pint.fermi_toas import load_Fermi_TOAs from pint.observatory.satellite_obs import get_satellite_observatory -from pinttestdata import datadir parfile = os.path.join(datadir, "PSRJ0030+0451_psrcat.par") eventfile = os.path.join( diff --git a/tests/test_fitter.py b/tests/test_fitter.py index 993c841e1..a3a6e55c9 100644 --- a/tests/test_fitter.py +++ b/tests/test_fitter.py @@ -2,17 +2,17 @@ import os from copy import deepcopy +import astropy.units as u + # import matplotlib # matplotlib.use('TKAgg') import matplotlib.pyplot as plt import pytest -import astropy.units as u +from pinttestdata import datadir import pint.models as tm -from pint import fitter, toa, simulation -from pinttestdata import datadir import pint.models.parameter as param -from pint import ls +from pint import fitter, ls, simulation, toa from pint.models import get_model, get_model_and_toas diff --git a/tests/test_fitter_compare.py b/tests/test_fitter_compare.py index 9e09b9c7b..d4aba4d26 100644 --- a/tests/test_fitter_compare.py +++ b/tests/test_fitter_compare.py @@ -10,17 +10,17 @@ import pint from pint.fitter import ( ConvergenceFailure, - MaxiterReached, DownhillGLSFitter, DownhillWLSFitter, GLSFitter, + MaxiterReached, WidebandDownhillFitter, WidebandTOAFitter, WLSFitter, ) from pint.models.model_builder import get_model -from pint.toa import get_TOAs from pint.simulation import make_fake_toas_uniform +from pint.toa import get_TOAs @pytest.fixture diff --git a/tests/test_fitter_error_checking.py b/tests/test_fitter_error_checking.py index 9b0828a7e..c35ffae75 100644 --- a/tests/test_fitter_error_checking.py +++ b/tests/test_fitter_error_checking.py @@ -1,10 +1,10 @@ #! /usr/bin/env python import io +import re -import numpy as np import astropy.units as u +import numpy as np import pytest -import re import pint.fitter from pint.models import get_model diff --git a/tests/test_flagging_clustering.py b/tests/test_flagging_clustering.py index ae11c54a3..03fd19642 100644 --- a/tests/test_flagging_clustering.py +++ b/tests/test_flagging_clustering.py @@ -1,19 +1,18 @@ """Tests for clustering and flagging""" +import copy import logging import os import unittest -import pytest -import copy import astropy.units as u import numpy as np +import pytest +from pinttestdata import datadir import pint.models.model_builder as mb import pint.toa as toa +from pint.models import PhaseJump, parameter as p from pint.residuals import Residuals -from pinttestdata import datadir -from pint.models import parameter as p -from pint.models import PhaseJump class SimpleSetup: @@ -42,7 +41,7 @@ def test_jump_by_cluster(setup_NGC6440E): setup_NGC6440E.m.add_component(PhaseJump(), validate=False) cp = setup_NGC6440E.m.components["PhaseJump"] par = p.maskParameter( - name="JUMP", key="mjd", value=0.2, key_value=[54099, 54100], units=u.s + name="JUMP", flag="mjd", value=0.2, flag_value=[54099, 54100], units=u.s ) # this should be the last group of any TOAs cp.add_param(par, setup=True) @@ -55,14 +54,10 @@ def test_jump_by_cluster(setup_NGC6440E): m_copy.add_component(PhaseJump(), validate=False) cp_copy = m_copy.components["PhaseJump"] par_copy = p.maskParameter( - name="JUMP", key="-toacluster", value=0.2, key_value=41, units=u.s + name="JUMP", flag="toacluster", value=0.2, flag_value="41", units=u.s ) # this should be identical to the cluster above cp_copy.add_param(par_copy, setup=True) assert set(cp.JUMP1.select_toa_mask(setup_NGC6440E.t)) == set( cp_copy.JUMP1.select_toa_mask(setup_NGC6440E.t) ) - - -if __name__ == "__main__": - pass diff --git a/tests/test_glitch.py b/tests/test_glitch.py index 0f7943f45..af349a911 100644 --- a/tests/test_glitch.py +++ b/tests/test_glitch.py @@ -5,12 +5,12 @@ import astropy.units as u import numpy as np import pytest +from pinttestdata import datadir import pint.fitter import pint.models import pint.residuals import pint.toa -from pinttestdata import datadir parfile = os.path.join(datadir, "prefixtest.par") timfile = os.path.join(datadir, "prefixtest.tim") diff --git a/tests/test_gls_fitter.py b/tests/test_gls_fitter.py index fa2234877..464e8bd5b 100644 --- a/tests/test_gls_fitter.py +++ b/tests/test_gls_fitter.py @@ -5,11 +5,11 @@ import astropy.units as u import numpy as np +from pinttestdata import datadir import pint.models.model_builder as mb from pint import toa from pint.fitter import GLSFitter -from pinttestdata import datadir class TestGLS(unittest.TestCase): diff --git a/tests/test_infostrings.py b/tests/test_infostrings.py index 8c7859070..f641a5e84 100644 --- a/tests/test_infostrings.py +++ b/tests/test_infostrings.py @@ -1,20 +1,20 @@ """Tests for adding info strings to parfiles and tim files""" -import logging -import os -import unittest -import pytest import copy import io +import logging +import os import platform +import unittest import astropy.units as u import numpy as np +import pytest +from pinttestdata import datadir +import pint import pint.models.model_builder as mb import pint.toa as toa from pint.residuals import Residuals -from pinttestdata import datadir -import pint class SimpleSetup: diff --git a/tests/test_jump.py b/tests/test_jump.py index 2cf3f4b15..fecfe0306 100644 --- a/tests/test_jump.py +++ b/tests/test_jump.py @@ -1,27 +1,29 @@ """Tests for jump model component """ import logging import os +import re import unittest -import pytest -import copy +from io import StringIO import astropy.units as u import numpy as np +import pytest +from numpy.testing import assert_allclose, assert_array_equal +from pinttestdata import datadir -import pint.models.model_builder as mb -import pint.toa as toa +import pint.models.parameter +import pint.toa +from pint.models import PhaseJump, get_model, parameter as p from pint.residuals import Residuals -from pinttestdata import datadir -from pint.models import parameter as p -from pint.models import PhaseJump +from pint.simulation import make_fake_toas_uniform class SimpleSetup: def __init__(self, par, tim): self.par = par self.tim = tim - self.m = mb.get_model(self.par) - self.t = toa.get_TOAs( + self.m = get_model(self.par) + self.t = pint.toa.get_TOAs( self.tim, ephem="DE405", planets=False, include_bipm=False ) @@ -44,7 +46,10 @@ def test_add_jumps_and_flags(setup_NGC6440E): # add second jump to different set of TOAs selected_toa_ind2 = [10, 11, 12] - cp.add_jump_and_flags(setup_NGC6440E.t.table["flags"][selected_toa_ind2]) + j2 = cp.add_jump_and_flags(setup_NGC6440E.t.table["flags"][selected_toa_ind2]) + jp2 = getattr(cp, j2) + assert jp2.flag == "gui_jump" + assert jp2.flag_value == "2" # check previous jump flags unaltered for d in setup_NGC6440E.t.table["flags"][selected_toa_ind]: assert d["gui_jump"] == "1" @@ -58,11 +63,12 @@ def test_add_overlapping_jump(setup_NGC6440E): cp = setup_NGC6440E.m.components["PhaseJump"] selected_toa_ind = [1, 2, 3] selected_toa_ind2 = [10, 11, 12] - cp.add_jump_and_flags(setup_NGC6440E.t.table["flags"][selected_toa_ind]) - cp.add_jump_and_flags(setup_NGC6440E.t.table["flags"][selected_toa_ind2]) + j1 = cp.add_jump_and_flags(setup_NGC6440E.t.table["flags"][selected_toa_ind]) + j2 = cp.add_jump_and_flags(setup_NGC6440E.t.table["flags"][selected_toa_ind2]) # attempt to add overlapping jump - should not add jump selected_toa_ind3 = [9, 10, 11] - cp.add_jump_and_flags(setup_NGC6440E.t.table["flags"][selected_toa_ind3]) + with pytest.raises(ValueError): + cp.add_jump_and_flags(setup_NGC6440E.t.table["flags"][selected_toa_ind3]) # check previous jump flags unaltered for d in setup_NGC6440E.t.table["flags"][selected_toa_ind]: assert d["gui_jump"] == "1" @@ -73,105 +79,32 @@ def test_add_overlapping_jump(setup_NGC6440E): assert "gui_jump" not in setup_NGC6440E.t.table[9].colnames -def test_remove_jump_and_flags(setup_NGC6440E): - setup_NGC6440E.m.add_component(PhaseJump(), validate=False) - cp = setup_NGC6440E.m.components["PhaseJump"] - selected_toa_ind = [1, 2, 3] - selected_toa_ind2 = [10, 11, 12] - cp.add_jump_and_flags(setup_NGC6440E.t.table["flags"][selected_toa_ind]) - cp.add_jump_and_flags(setup_NGC6440E.t.table["flags"][selected_toa_ind2]) - # test delete_jump_and_flags - setup_NGC6440E.m.delete_jump_and_flags(setup_NGC6440E.t.table["flags"], 1) - assert len(cp.jumps) == 1 - - # delete last jump - setup_NGC6440E.m.delete_jump_and_flags(setup_NGC6440E.t.table["flags"], 2) - for d in setup_NGC6440E.t.table["flags"][selected_toa_ind2]: - assert "jump" not in d - assert "PhaseJump" not in setup_NGC6440E.m.components - - -def test_jump_params_to_flags(setup_NGC6440E): - """ Check jump_params_to_flags function. """ - setup_NGC6440E.m.add_component(PhaseJump(), validate=False) - cp = setup_NGC6440E.m.components["PhaseJump"] - - par = p.maskParameter( - name="JUMP", key="freq", value=0.2, key_value=[1440, 1700], units=u.s - ) # TOAs indexed 48, 49, 54 in NGC6440E are within this frequency range - cp.add_param(par, setup=True) - - # sanity check - ensure no jump flags from initialization - for i in range(setup_NGC6440E.t.ntoas): - assert "jump" not in setup_NGC6440E.t.table["flags"][i] - - # add flags based off jumps added to model - setup_NGC6440E.m.jump_params_to_flags(setup_NGC6440E.t) - - # index to affected TOAs and ensure appropriate flags set - toa_indeces = [48, 49, 54] - for i in toa_indeces: - assert "jump" in setup_NGC6440E.t.table["flags"][i] - assert setup_NGC6440E.t.table["flags"][i]["jump"][0] == "1" - # ensure no extraneous flags added to unaffected TOAs - for i in range(setup_NGC6440E.t.ntoas): - if i not in toa_indeces: - assert "jump" not in setup_NGC6440E.t.table["flags"][i] - - # check case where multiple calls are performed (no-ops) - old_table = setup_NGC6440E.t.table - setup_NGC6440E.m.jump_params_to_flags(setup_NGC6440E.t) - assert all(old_table) == all(setup_NGC6440E.t.table) - - # check that adding overlapping jump works - par2 = p.maskParameter( - name="JUMP", key="freq", value=0.2, key_value=[1600, 1900], units=u.s - ) # frequency range overlaps with par, 2nd jump will have common TOAs w/ 1st - cp.add_param(par2, setup=True) - # add flags based off jumps added to model - setup_NGC6440E.m.jump_params_to_flags(setup_NGC6440E.t) - mask2 = par2.select_toa_mask(setup_NGC6440E.t) - intersect = np.intersect1d(toa_indeces, mask2) - assert intersect is not [] - for i in mask2: - assert "2" in setup_NGC6440E.t.table["flags"][i]["jump"] - for i in toa_indeces: - assert "1" in setup_NGC6440E.t.table["flags"][i]["jump"] - - def test_multijump_toa(setup_NGC6440E): setup_NGC6440E.m.add_component(PhaseJump(), validate=False) cp = setup_NGC6440E.m.components["PhaseJump"] par = p.maskParameter( - name="JUMP", key="freq", value=0.2, key_value=[1440, 1700], units=u.s + name="JUMP", + flag="freq", + value=0.2, + flag_value=[1440 * u.MHz, 1700 * u.MHz], + units=u.s, ) # TOAs indexed 48, 49, 54 in NGC6440E are within this frequency range selected_toa_ind = [48, 49, 54] cp.add_param(par, setup=True) # check that one can still add "gui jumps" to model-jumped TOAs cp.add_jump_and_flags(setup_NGC6440E.t.table["flags"][selected_toa_ind]) - # add flags based off jumps added to model - setup_NGC6440E.m.jump_params_to_flags(setup_NGC6440E.t) - for dict in setup_NGC6440E.t.table["flags"][selected_toa_ind]: - assert dict["jump"] == "1,2" - assert dict["gui_jump"] == "2" + assert_array_equal(setup_NGC6440E.t["gui_jump", selected_toa_ind], "1") assert len(cp.jumps) == 2 - setup_NGC6440E.m.delete_jump_and_flags(setup_NGC6440E.t.table["flags"], 2) - for dict in setup_NGC6440E.t.table["flags"][selected_toa_ind]: - assert "jump" in dict - assert len(cp.jumps) == 1 - assert "JUMP1" in cp.jumps - class TestJUMP(unittest.TestCase): @classmethod def setUpClass(cls): - os.chdir(datadir) - cls.parf = "B1855+09_NANOGrav_dfg+12_TAI.par" - cls.timf = "B1855+09_NANOGrav_dfg+12.tim" - cls.JUMPm = mb.get_model(cls.parf) - cls.toas = toa.get_TOAs( + cls.parf = os.path.join(datadir, "B1855+09_NANOGrav_dfg+12_TAI.par") + cls.timf = os.path.join(datadir, "B1855+09_NANOGrav_dfg+12.tim") + cls.JUMPm = get_model(cls.parf) + cls.toas = pint.toa.get_TOAs( cls.timf, ephem="DE405", planets=False, include_bipm=False ) # libstempo calculation @@ -179,7 +112,7 @@ def setUpClass(cls): cls.parf + ".tempo_test", unpack=False, names=True, dtype=np.longdouble ) - def test_jump(self): + def test_jump_agrees_with_tempo(self): presids_s = Residuals( self.toas, self.JUMPm, use_weighted_mean=False ).time_resids.to(u.s) @@ -187,6 +120,17 @@ def test_jump(self): np.abs(presids_s.value - self.ltres["residuals"]) < 1e-7 ), "JUMP test failed." + def test_jump_selects_toas(self): + for p in self.JUMPm.params: + if not p.startswith("JUMP"): + continue + pm = getattr(self.JUMPm, p) + if pm.flag != "-chanid": + continue + assert len( + [l for l in open(self.timf).readlines() if re.search(pm.flag_value, l)] + ) == len(pm.select_toa_mask(self.toas)) + def test_derivative(self): log = logging.getLogger("Jump phase test") p = "JUMP2" @@ -205,5 +149,231 @@ def test_derivative(self): assert np.nanmax(relative_diff) < 0.001, msg -if __name__ == "__main__": - pass +@pytest.mark.parametrize( + "tim, flag_ranges", + [ + ( + """ +FORMAT 1 +unk 999999.000000 57000.0000000078830324 1.000 gbt -pn -2273593021.0 +unk 999999.000000 57052.6315789538116088 1.000 gbt -pn -124285.0 +JUMP +unk 999999.000000 57105.2631578955917593 1.000 gbt -pn 2273470219.0 +unk 999999.000000 57157.8947368420341204 1.000 gbt -pn 4547256435.0 +unk 999999.000000 57210.5263157897339815 1.000 gbt -pn 6821152750.0 +JUMP +JUMP +unk 999999.000000 57263.1578947318551852 1.000 gbt -pn 9095000767.0 +unk 999999.000000 57315.7894736865498264 1.000 gbt -pn 11368677703.0 +unk 999999.000000 57368.4210526391189699 1.000 gbt -pn 13642183645.0 +JUMP +unk 999999.000000 57421.0526315687085764 1.000 gbt -pn 15915655534.0 +unk 999999.000000 57473.6842105144226621 1.000 gbt -pn 18189261252.0 +unk 999999.000000 57526.3157894708454977 1.000 gbt -pn 20463057581.0 +unk 999999.000000 57578.9473684237653588 1.000 gbt -pn 22736955090.0 +JUMP +unk 999999.000000 57631.5789473769360416 1.000 gbt -pn 25010795383.0 +unk 999999.000000 57684.2105263208505671 1.000 gbt -pn 27284460486.0 +unk 999999.000000 57736.8421052672976158 1.000 gbt -pn 29557959408.0 +unk 999999.000000 57789.4736842041808449 1.000 gbt -pn 31831435551.0 +unk 999999.000000 57842.1052631637698032 1.000 gbt -pn 34105052652.0 +unk 999999.000000 57894.7368420936182176 1.000 gbt -pn 36378858766.0 +unk 999999.000000 57947.3684210606924768 1.000 gbt -pn 38652757406.0 +unk 999999.000000 57999.9999999883338542 1.000 gbt -pn 40926589498.0 +JUMP +""", + [(57100, 57250, 1), (57250, 57400, 2), (57600, 59000, 3)], + ), + ( + """ +FORMAT 1 +unk 999999.000000 57000.0000000078830324 1.000 gbt -pn -2273593021.0 +unk 999999.000000 57052.6315789538116088 1.000 gbt -pn -124285.0 +JUMP +unk 999999.000000 57105.2631578955917593 1.000 gbt -pn 2273470219.0 +unk 999999.000000 57157.8947368420341204 1.000 gbt -pn 4547256435.0 +unk 999999.000000 57210.5263157897339815 1.000 gbt -pn 6821152750.0 +JUMP +unk 999999.000000 57263.1578947318551852 1.000 gbt -pn 9095000767.0 +unk 999999.000000 57315.7894736865498264 1.000 gbt -pn 11368677703.0 +unk 999999.000000 57368.4210526391189699 1.000 gbt -pn 13642183645.0 +JUMP +JUMP +unk 999999.000000 57421.0526315687085764 1.000 gbt -pn 15915655534.0 +unk 999999.000000 57473.6842105144226621 1.000 gbt -pn 18189261252.0 +unk 999999.000000 57526.3157894708454977 1.000 gbt -pn 20463057581.0 +unk 999999.000000 57578.9473684237653588 1.000 gbt -pn 22736955090.0 +JUMP +unk 999999.000000 57631.5789473769360416 1.000 gbt -pn 25010795383.0 +unk 999999.000000 57684.2105263208505671 1.000 gbt -pn 27284460486.0 +unk 999999.000000 57736.8421052672976158 1.000 gbt -pn 29557959408.0 +unk 999999.000000 57789.4736842041808449 1.000 gbt -pn 31831435551.0 +unk 999999.000000 57842.1052631637698032 1.000 gbt -pn 34105052652.0 +unk 999999.000000 57894.7368420936182176 1.000 gbt -pn 36378858766.0 +unk 999999.000000 57947.3684210606924768 1.000 gbt -pn 38652757406.0 +unk 999999.000000 57999.9999999883338542 1.000 gbt -pn 40926589498.0 +JUMP +""", + [(57100, 57250, 1), (57600, 59000, 3)], + ), + ], +) +def test_tim_file_gets_jump_flags(tim, flag_ranges): + toas = pint.toa.get_TOAs(StringIO(tim)) + for start, end, n in flag_ranges: + for i in range(len(toas)): + f = toas.table["flags"][i] + m = int(f.get("jump", -1)) + assert (start < toas.table["tdbld"][i] < end) == (n == m) + + +def test_multiple_jumps_add(): + m = get_model( + StringIO( + """ + PSR J1234+5678 + ELAT 0 + ELONG 0 + PEPOCH 57000 + POSEPOCH 57000 + F0 500 + JUMP mjd 58000 60000 0 + JUMP mjd 59000 60000 0 + """ + ) + ) + for jmp in m.jumps: + if jmp.flag == "mjd": + start, end = jmp.flag_value + if start < 58500: + first_jump = jmp + else: + second_jump = jmp + toas = make_fake_toas_uniform(57000, 60000 - 1, 10, m) + + first_jump.quantity = 100 * u.us + second_jump.quantity = 0 * u.us + r_first = pint.residuals.Residuals(toas, m) + + first_jump.quantity = 0 * u.us + second_jump.quantity = 75 * u.us + r_second = pint.residuals.Residuals(toas, m) + + first_jump.quantity = 100 * u.us + second_jump.quantity = 75 * u.us + r_sum = pint.residuals.Residuals(toas, m) + + assert_allclose( + (r_first.resids + r_second.resids).to_value(u.us), + r_sum.resids.to_value(u.us), + atol=1e-3, + ) + + +@pytest.mark.parametrize( + "j", + [ + pint.models.parameter.maskParameter( + name="JUMP", + flag="fish", + flag_value="carp", + units=u.s, + value=7, + frozen=False, + uncertainty=0.1, + ), + pint.models.parameter.maskParameter( + name="JUMP", flag="tel", flag_value="ao", units=u.s, value=7, frozen=False, + ), + pint.models.parameter.maskParameter( + name="JUMP", + flag="MJD", + flag_value=(57000, 58000,), + units=u.s, + value=7, + frozen=False, + ), + ], +) +def test_jump_parfile_roundtrip(j): + l = j.as_parfile_line() + nj = pint.models.parameter.maskParameter(name="JUMP", units=u.s) + nj.from_parfile_line(l) + + assert nj.flag == j.flag + assert nj.flag_value == j.flag_value + if nj.quantity != j.quantity: + assert_allclose(nj.quantity, j.quantity) + assert nj.frozen == j.frozen + if nj.uncertainty != j.uncertainty: + assert_allclose(nj.uncertainty, j.uncertainty) + + +@pytest.fixture +def small(): + m = get_model( + StringIO( + """ + PSR J1234+5678 + F0 1 + PEPOCH 58000 + POSEPOCH 58000 + ELONG 0 + ELAT 0 + JUMP mjd 59000 70000 0 + """ + ) + ) + t = make_fake_toas_uniform(58000, 60000, 20, m) + + class R: + pass + + r = R() + r.m = m + r.t = t + for j in m.jumps: + if j.flag == "mjd": + r.j = j + return r + + +def test_tidy_jumps_all_ok(small): + small.j.frozen = False + small.m.tidy_jumps_for_fit(small.t) + assert not small.j.frozen + + +def test_tidy_jumps_all_jumped(small): + small.j.frozen = False + small.j.flag_value = ( + 56000, + 70000, + ) + small.m.tidy_jumps_for_fit(small.t) + assert small.j.frozen + + +def test_tidy_jumps_irrelevant(small): + small.j.frozen = False + j2 = small.j.new_param(index=100, copy_all=True) + j2.flag_value = ( + 50000, + 55000, + ) + small.m.components["PhaseJump"].add_param(j2) + small.m.tidy_jumps_for_fit(small.t) + assert not small.j.frozen + assert j2.frozen + + +def test_tidy_jumps_cover_all_freeze_one(small): + small.j.frozen = False + j2 = small.j.new_param(index=100, copy_all=True) + j2.flag_value = ( + 50000, + 59000, + ) + small.m.components["PhaseJump"].add_param(j2) + small.m.tidy_jumps_for_fit(small.t) + assert small.j.frozen != j2.frozen diff --git a/tests/test_mask_parameter.py b/tests/test_mask_parameter.py index 3c5f200c0..d93969221 100644 --- a/tests/test_mask_parameter.py +++ b/tests/test_mask_parameter.py @@ -1,19 +1,18 @@ """Various tests for the maskParameter""" +import copy import os -import pytest from io import StringIO + import astropy -import astropy.time as time import astropy.units as u import numpy as np +import pytest +from pinttestdata import datadir from pint.models.model_builder import get_model from pint.models.parameter import maskParameter from pint.toa import get_TOAs -from pinttestdata import datadir - -import copy @pytest.fixture @@ -24,9 +23,9 @@ def toas(): def test_mjd_mask(toas): - mp = maskParameter("test1", key="mjd", key_value=[54000, 54100]) - assert mp.key == "mjd" - assert mp.key_value == [54000, 54100] + mp = maskParameter("test1", flag="mjd", flag_value=[54000, 54100],) + assert mp.flag == "mjd" + assert mp.flag_value == (54000, 54100) assert mp.value == None select_toas = mp.select_toa_mask(toas) assert len(select_toas) > 0 @@ -36,83 +35,91 @@ def test_mjd_mask(toas): assert np.all(select_toas == raw_selection[0]) assert np.all(toas.table["mjd_float"][select_toas] <= 54100) assert np.all(toas.table["mjd_float"][select_toas] >= 54000) - mp_str_keyval = maskParameter("test2", key="mjd", key_value=["54000", "54100"]) - assert mp_str_keyval.key_value == [54000, 54100] - mp_value_switch = maskParameter("test1", key="mjd", key_value=[54100, 54000]) - assert mp_value_switch.key_value == [54000, 54100] with pytest.raises(ValueError): - mp_str_keyval = maskParameter("test2", key="mjd", key_value=["54000"]) + maskParameter( + "test2", flag="mjd", flag_value=["54000", "54100"], + ) + mp_value_switch = maskParameter("test1", flag="mjd", flag_value=[54100, 54000],) + assert mp_value_switch.flag_value == (54000, 54100,) + with pytest.raises(ValueError): + maskParameter("test2", flag="mjd", flag_value=[54000]) def test_freq_mask(toas): - mp = maskParameter("test2", key="freq", key_value=[1400, 1430]) - assert mp.key_value == [1400 * u.MHz, 1430 * u.MHz] + mp = maskParameter("test2", flag="freq", flag_value=[1400 * u.MHz, 1430 * u.MHz]) + assert mp.flag_value == (1400 * u.MHz, 1430 * u.MHz) select_toas = mp.select_toa_mask(toas) assert len(select_toas) > 0 raw_selection = np.where( (toas.table["freq"] <= 1430 * u.MHz) * (toas.table["freq"] >= 1400 * u.MHz) ) assert np.all(select_toas == raw_selection[0]) - mp_str = maskParameter("test2", key="freq", key_value=["1400", "2000"]) - assert mp_str.key_value == [1400 * u.MHz, 2000 * u.MHz] - mp_switch = maskParameter("test2", key="freq", key_value=[2000, 1400]) - assert mp_switch.key_value == [1400 * u.MHz, 2000 * u.MHz] + with pytest.raises(ValueError): + maskParameter("test2", flag="freq", flag_value=["1400", "2000"]) + mp_switch = maskParameter( + "test2", flag="freq", flag_value=[2000 * u.MHz, 1400 * u.MHz] + ) + assert mp_switch.flag_value == (1400 * u.MHz, 2000 * u.MHz) mp_quantity = maskParameter( - "test2", key="freq", key_value=[2000 * u.MHz, 1400 * u.MHz] + "test2", flag="freq", flag_value=[2000 * u.MHz, 1400 * u.MHz] ) - assert mp_quantity.key_value == [1400 * u.MHz, 2000 * u.MHz] + assert mp_quantity.flag_value == (1400 * u.MHz, 2000 * u.MHz) with pytest.raises(ValueError): - mp_wrong_keyvalue = maskParameter("test2", key="freq", key_value=[1400]) + maskParameter("test2", flag="freq", flag_value=[1400 * u.MHz]) def test_tel_mask(toas): - mp_ao = maskParameter("test2", key="tel", key_value="ao") - assert mp_ao.key_value == ["arecibo"] + mp_ao = maskParameter("test2", flag="tel", flag_value="ao") + assert mp_ao.flag_value == "arecibo" select_toas = mp_ao.select_toa_mask(toas) assert len(select_toas) > 0 raw_selection = np.where(toas.table["obs"] == "arecibo") assert np.all(select_toas == raw_selection[0]) - mp_gbt = maskParameter("test2", key="tel", key_value=["gbt"]) - assert mp_gbt.key_value == ["gbt"] + mp_gbt = maskParameter("test2", flag="tel", flag_value="gbt") + assert mp_gbt.flag_value == "gbt" select_toas = mp_gbt.select_toa_mask(toas) assert np.all(toas.table["obs"][select_toas] == "gbt") - mp_special_obs1 = maskParameter("test2", key="tel", key_value="@") - assert mp_special_obs1.key_value == ["barycenter"] - mp_special_obs2 = maskParameter("test2", key="tel", key_value=0) - assert mp_special_obs2.key_value == ["geocenter"] + mp_special_obs1 = maskParameter("test2", flag="tel", flag_value="@") + assert mp_special_obs1.flag_value == "barycenter" + mp_special_obs2 = maskParameter("test2", flag="tel", flag_value="0") + assert mp_special_obs2.flag_value == "geocenter" with pytest.raises(ValueError): - mp_wrong_keyvalue = maskParameter("test2", key="tel", key_value=["gbt", "ao"]) + maskParameter("test2", flag="tel", flag_value=["gbt", "ao"]) def test_name_mask(toas): # Not sure about the use case for name mask mp_name = maskParameter( - "test2", key="name", key_value="53393.000009.3.000.000.9y.x.ff" + "test2", flag="name", flag_value="53393.000009.3.000.000.9y.x.ff" ) - assert mp_name.key_value == ["53393.000009.3.000.000.9y.x.ff"] + assert mp_name.flag_value == "53393.000009.3.000.000.9y.x.ff" select_toas = mp_name.select_toa_mask(toas) assert len(select_toas) > 0 - raw_selection = np.where(toas.table["name"] == "53393.000009.3.000.000.9y.x.ff") - assert np.all(select_toas == raw_selection[0]) + raw_selection = [] + for i, f in enumerate(toas.table["flags"]): + if f["name"] == "53393.000009.3.000.000.9y.x.ff": + raw_selection.append(i) + assert np.all(select_toas == raw_selection) with pytest.raises(ValueError): - mp_wrong_keyvalue = maskParameter( - "test2", key="name", key_value=["name1", "name2"] - ) + maskParameter("test2", flag="name", flag_value=["name1", "name2"]) def test_flag_mask(toas): - mp_flag = maskParameter("test2", key="-fe", key_value=430) - assert mp_flag.key_value == ["430"] - mp_flag2 = maskParameter("test2", key="-fe", key_value="430") - assert mp_flag2.key_value == ["430"] with pytest.raises(ValueError): - mp_wrong_key = maskParameter("test2", key="fe", key_value="430") - mp_flag3 = maskParameter("test2", key="-fe", key_value="L-wide") - assert mp_flag3.key_value == ["L-wide"] + maskParameter("test2", flag="fe", flag_value=430) + mp_flag2 = maskParameter("test2", flag="fe", flag_value="430") + assert mp_flag2.flag_value == "430" + with pytest.raises(ValueError): + maskParameter("test2", flag="-fe", flag_value="430") + mp_flag3 = maskParameter("test2", flag="fe", flag_value="L-wide") + assert mp_flag3.flag_value == "L-wide" select_toas = mp_flag3.select_toa_mask(toas) assert len(select_toas) > 0 - raw_selection = np.where(toas.table["fe"] == "L-wide") - assert np.all(select_toas == raw_selection[0]) + raw_selection = [] + for i, f in enumerate(toas.table["flags"]): + if f["fe"] == "L-wide": + raw_selection.append(i) + assert np.all(select_toas == raw_selection) def test_read_from_par(toas): diff --git a/tests/test_model.py b/tests/test_model.py index 9c9255957..bcd245302 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -6,11 +6,11 @@ import matplotlib.pyplot as plt import numpy as np from astropy import log +from pinttestdata import datadir import pint.models as tm from pint import toa from pint.residuals import Residuals -from pinttestdata import datadir def test_model(): diff --git a/tests/test_model_derivatives.py b/tests/test_model_derivatives.py index 8c1348963..eb23e4a6e 100644 --- a/tests/test_model_derivatives.py +++ b/tests/test_model_derivatives.py @@ -8,13 +8,13 @@ import numdifftools import numpy as np import pytest +from astropy import units as u from hypothesis import HealthCheck, Verbosity, assume, given, settings from hypothesis.strategies import composite, floats, integers, sampled_from from numpy.testing import assert_allclose -from astropy import units as u -import pint.toa import pint.simulation +import pint.toa from pint.models import get_model diff --git a/tests/test_model_ifunc.py b/tests/test_model_ifunc.py index 1dc11503b..8bddc011e 100644 --- a/tests/test_model_ifunc.py +++ b/tests/test_model_ifunc.py @@ -3,12 +3,12 @@ import unittest import astropy.units as u +from pinttestdata import datadir +import pint.fitter import pint.models import pint.residuals import pint.toa -import pint.fitter -from pinttestdata import datadir # Not included in the test here, but as a sanity check I used this same # ephemeris to phase up Fermi data, and it looks good. diff --git a/tests/test_model_manual.py b/tests/test_model_manual.py index 4fbec8b75..3d84d18fc 100644 --- a/tests/test_model_manual.py +++ b/tests/test_model_manual.py @@ -1,17 +1,18 @@ """Test model building and structure for simple models.""" +from collections import defaultdict from glob import glob from os.path import basename, join -from collections import defaultdict -import pytest import astropy.units as u +import pytest +from pinttestdata import datadir + from pint.models.astrometry import AstrometryEquatorial from pint.models.dispersion_model import DispersionDM, DispersionDMX -from pint.models.spindown import Spindown from pint.models.model_builder import UnknownBinaryModel, get_model, get_model_new -from pint.models.timing_model import MissingParameter, TimingModel, Component -from pinttestdata import datadir +from pint.models.spindown import Spindown +from pint.models.timing_model import Component, MissingParameter, TimingModel @pytest.fixture diff --git a/tests/test_model_wave.py b/tests/test_model_wave.py index 39a9ce024..037dc915c 100644 --- a/tests/test_model_wave.py +++ b/tests/test_model_wave.py @@ -1,15 +1,15 @@ #! /usr/bin/env python import os import unittest -import pytest import astropy.units as u +import pytest +from pinttestdata import datadir import pint.fitter import pint.models import pint.residuals import pint.toa -from pinttestdata import datadir # Not included in the test here, but as a sanity check I used this same # ephemeris to phase up Fermi data, and it looks good. diff --git a/tests/test_modelutils.py b/tests/test_modelutils.py index ec6d46bad..4be28d0f8 100644 --- a/tests/test_modelutils.py +++ b/tests/test_modelutils.py @@ -4,13 +4,12 @@ import astropy.units as u import numpy as np +from pinttestdata import datadir import pint.toa as toa from pint.models import get_model +from pint.modelutils import model_ecliptic_to_equatorial, model_equatorial_to_ecliptic from pint.residuals import Residuals -from pinttestdata import datadir - -from pint.modelutils import model_equatorial_to_ecliptic, model_ecliptic_to_equatorial class TestEcliptic(unittest.TestCase): diff --git a/tests/test_new_observatories.py b/tests/test_new_observatories.py index bc1c02e91..2fa7bebda 100644 --- a/tests/test_new_observatories.py +++ b/tests/test_new_observatories.py @@ -1,14 +1,14 @@ +import os +import os.path from io import StringIO import numpy as np -import os -import os.path import pytest +from pinttestdata import datadir -from pint.toa import get_TOAs from pint.models import get_model from pint.observatory import get_observatory -from pinttestdata import datadir +from pint.toa import get_TOAs parfile = os.path.join(datadir, "NGC6440E.par") diff --git a/tests/test_observatory.py b/tests/test_observatory.py index 3c81a57f9..5708bf8f5 100644 --- a/tests/test_observatory.py +++ b/tests/test_observatory.py @@ -4,11 +4,11 @@ import numpy as np import pytest -from pint.pulsar_mjd import Time +from pinttestdata import datadir from pint.observatory import get_observatory from pint.observatory.topo_obs import TopoObs -from pinttestdata import datadir +from pint.pulsar_mjd import Time class TestObservatory(unittest.TestCase): diff --git a/tests/test_observatory_metadata.py b/tests/test_observatory_metadata.py index 0a70b1f36..eedb9c1b5 100644 --- a/tests/test_observatory_metadata.py +++ b/tests/test_observatory_metadata.py @@ -1,7 +1,9 @@ import logging import os import unittest + import numpy as np + import pint.observatory diff --git a/tests/test_orbit_phase.py b/tests/test_orbit_phase.py index fcd3b53e8..98e01e8bc 100644 --- a/tests/test_orbit_phase.py +++ b/tests/test_orbit_phase.py @@ -5,10 +5,10 @@ import astropy.time as time import astropy.units as u import numpy as np +from pinttestdata import datadir -import pint.toa as t import pint.models as m -from pinttestdata import datadir +import pint.toa as t class TestOrbitPhase(unittest.TestCase): diff --git a/tests/test_parametercovariancematrix.py b/tests/test_parametercovariancematrix.py index 3438a8fd7..b91417e38 100644 --- a/tests/test_parametercovariancematrix.py +++ b/tests/test_parametercovariancematrix.py @@ -1,9 +1,9 @@ -import numpy as np import os import unittest from os.path import join import astropy.units as u +import numpy as np import pytest from pinttestdata import datadir @@ -12,13 +12,13 @@ DownhillGLSFitter, DownhillWLSFitter, GLSFitter, - WLSFitter, WidebandDownhillFitter, WidebandTOAFitter, + WLSFitter, ) from pint.models.model_builder import get_model -from pint.toa import get_TOAs from pint.simulation import make_fake_toas_uniform +from pint.toa import get_TOAs @pytest.fixture diff --git a/tests/test_parameters.py b/tests/test_parameters.py index 7217b2b82..3f1d0b375 100644 --- a/tests/test_parameters.py +++ b/tests/test_parameters.py @@ -7,6 +7,7 @@ import astropy.units as u import numpy as np import pytest +from astropy.time import Time from numpy.testing import assert_allclose from pinttestdata import datadir @@ -78,21 +79,21 @@ def test_read_par_line_expected_values(): assert_allclose(test_m.F8.value, 0.0) assert_allclose(test_m.F8.uncertainty_value, 10) assert test_m.JUMP1.frozen - assert test_m.JUMP1.key == "MJD" - assert_allclose(test_m.JUMP1.key_value[0], 52742.0, atol=1e-10) - assert_allclose(test_m.JUMP1.key_value[1], 52745.0, atol=1e-10) + assert test_m.JUMP1.flag == "mjd" + assert_allclose(test_m.JUMP1.flag_value[0], 52742.0, atol=1e-10) + assert_allclose(test_m.JUMP1.flag_value[1], 52745.0, atol=1e-10) assert_allclose(test_m.JUMP1.value, 0.2) assert test_m.JUMP2.frozen assert_allclose(test_m.JUMP2.value, 0.1) - assert_allclose(test_m.JUMP2.uncertainty_value, 0.0) + assert test_m.JUMP2.uncertainty_value is None assert_allclose(test_m.JUMP7.value, 0.1) assert_allclose(test_m.JUMP7.uncertainty_value, 10.5) assert_allclose(test_m.JUMP6.value, 0.1) assert_allclose(test_m.JUMP6.uncertainty_value, 10.0) - assert test_m.JUMP12.key == "-testflag" + assert test_m.JUMP12.flag == "testflag" assert not test_m.JUMP12.frozen - assert test_m.JUMP12.key_value[0] == "flagvalue" + assert test_m.JUMP12.flag_value == "flagvalue" assert_allclose(test_m.JUMP12.value, 0.1) assert_allclose(test_m.JUMP12.uncertainty_value, 2.0) assert_allclose( @@ -144,6 +145,55 @@ def setUpClass(cls): cls.m = get_model("B1855+09_NANOGrav_dfg+12_modified.par") cls.mp = get_model("prefixtest.par") + def test_read_par_line(self): + test_m = get_model("test_par_read.par") + self.assertEqual(test_m.F2.frozen, True) + self.assertEqual(test_m.F3.frozen, True) + self.assertTrue(np.isclose(test_m.F3.value, 0.0)) + self.assertTrue(np.isclose(test_m.F3.uncertainty_value, 0.0)) + self.assertEqual(test_m.F4.frozen, True) + self.assertTrue(np.isclose(test_m.F4.value, 0.0)) + self.assertTrue(np.isclose(test_m.F4.uncertainty_value, 0.001)) + self.assertEqual(test_m.F5.frozen, True) + self.assertTrue(np.isclose(test_m.F5.value, 0.0)) + self.assertTrue(np.isclose(test_m.F5.uncertainty_value, 0.001)) + self.assertEqual(test_m.F6.frozen, False) + self.assertTrue(np.isclose(test_m.F6.value, 0.0)) + self.assertTrue(np.isclose(test_m.F6.uncertainty_value, 0.001)) + self.assertEqual(test_m.F7.frozen, True) + self.assertTrue(np.isclose(test_m.F7.value, 0.0)) + self.assertTrue(np.isclose(test_m.F7.uncertainty_value, 3.0)) + self.assertEqual(test_m.F8.frozen, True) + self.assertTrue(np.isclose(test_m.F8.value, 0.0)) + self.assertTrue(np.isclose(test_m.F8.uncertainty_value, 10)) + self.assertEqual(test_m.JUMP1.frozen, True) + self.assertEqual(test_m.JUMP1.flag, "mjd") + self.assertTrue(np.isclose(test_m.JUMP1.flag_value[0], 52742.0, atol=1e-10)) + self.assertTrue(np.isclose(test_m.JUMP1.flag_value[1], 52745.0, atol=1e-10)) + self.assertTrue(np.isclose(test_m.JUMP1.value, 0.2)) + + self.assertEqual(test_m.JUMP2.frozen, True) + self.assertTrue(np.isclose(test_m.JUMP2.value, 0.1)) + self.assertEqual(test_m.JUMP2.uncertainty_value, None) + self.assertTrue(np.isclose(test_m.JUMP7.value, 0.1)) + self.assertTrue(np.isclose(test_m.JUMP7.uncertainty_value, 10.5)) + self.assertTrue(np.isclose(test_m.JUMP6.value, 0.1)) + self.assertTrue(np.isclose(test_m.JUMP6.uncertainty_value, 10.0)) + self.assertEqual(test_m.JUMP12.flag, "testflag") + self.assertEqual(test_m.JUMP12.frozen, False) + self.assertEqual(test_m.JUMP12.flag_value, "flagvalue") + self.assertTrue(np.isclose(test_m.JUMP12.value, 0.1)) + self.assertTrue(np.isclose(test_m.JUMP12.uncertainty_value, 2.0)) + assert_allclose( + test_m.RAJ.uncertainty, + 476.94611148516092061223 * pint_units["hourangle_second"], + ) + + assert_allclose( + test_m.DECJ.uncertainty, + 190996312986311097848351.00000000000000000000 * u.arcsec, + ) + def test_RAJ(self): """Check whether the value and units of RAJ parameter are ok""" units = u.hourangle @@ -540,3 +590,52 @@ def test_set_uncertainty_bogus_raises(p): ) def test_parameter_can_be_pickled(p): pickle.dumps(p) + + +# Testing maskParameters: +# Their flag and flag_value attributes can be set three ways - by setting the attribute, during construction, or upon reading a parfile line. Each of these should produce only results of the correct type. + +valid_settings = [ + ("tel", "ao"), + ("freq", (1000.0 * u.MHz, 2000.0 * u.MHz)), + ("mjd", (57000.0, 58000.0)), + ("mjd", [57000.0, 58000.0]), + ("fish", "carp"), + ("freq", (2000.0 * u.MHz, np.inf * u.MHz)), + ("freq", np.array([1000, 2000], dtype=np.longdouble) * u.MHz), +] +invalid_settings = [ + ("tel", (10, 20)), + ("freq", "ao"), + ("mjd", ([], [])), + ("-fish", "carp"), + ("fish", (1, 2)), + ("fish", ["c", "a", "r", "p"]), +] + + +@pytest.mark.parametrize("flag, flag_value", valid_settings) +def test_maskParameter_construction_valid(flag, flag_value): + maskParameter( + name="JUMP", + index=467, + flag=flag, + flag_value=flag_value, + value=0, + units=u.s, + description="Generic description of a JUMP", + ) + + +@pytest.mark.parametrize("flag, flag_value", invalid_settings) +def test_maskParameter_construction_invalid(flag, flag_value): + with pytest.raises(ValueError): + maskParameter( + name="JUMP", + index=467, + flag=flag, + flag_value=flag_value, + value=0, + units=u.s, + description="Generic description of a JUMP", + ) diff --git a/tests/test_parfile.py b/tests/test_parfile.py index bcdca5541..06774624c 100644 --- a/tests/test_parfile.py +++ b/tests/test_parfile.py @@ -3,9 +3,9 @@ import tempfile import pytest +from pinttestdata import testdir import pint.models as tm -from pinttestdata import testdir datadir = os.path.join(testdir, "datafile") parfile = os.path.join(datadir, "J1744-1134.basic.par") diff --git a/tests/test_parfile_writing.py b/tests/test_parfile_writing.py index 97796b447..cb31efe6a 100644 --- a/tests/test_parfile_writing.py +++ b/tests/test_parfile_writing.py @@ -3,16 +3,16 @@ import os import unittest from io import StringIO -import pytest import astropy.units as u import numpy as np +import pytest +from pinttestdata import datadir import pint.models.model_builder as mb import pint.models.parameter as mp import pint.toa as toa from pint.residuals import Residuals -from pinttestdata import datadir class TestParfileWriting(unittest.TestCase): diff --git a/tests/test_phase.py b/tests/test_phase.py index 6972bb09c..f8599893a 100644 --- a/tests/test_phase.py +++ b/tests/test_phase.py @@ -1,10 +1,13 @@ # Test for phase.py -import pytest -import numpy as np +import math + import astropy.units as u +import numpy as np +import pytest + from pint.phase import Phase -import math + # modified from @mhvk's test_phase_class.py def assert_equal(first, second): diff --git a/tests/test_phase_commands.py b/tests/test_phase_commands.py index 53947ffda..44a130e2a 100644 --- a/tests/test_phase_commands.py +++ b/tests/test_phase_commands.py @@ -2,12 +2,13 @@ import os import unittest +import astropy.units as u +import numpy as np +from pinttestdata import datadir + import pint.models import pint.toa from pint.residuals import Residuals -from pinttestdata import datadir -import astropy.units as u -import numpy as np parfile = os.path.join(datadir, "NGC6440E_PHASETEST.par") timfile = os.path.join(datadir, "NGC6440E_PHASETEST.tim") diff --git a/tests/test_photonphase.py b/tests/test_photonphase.py index e54ac9b00..c10afe140 100644 --- a/tests/test_photonphase.py +++ b/tests/test_photonphase.py @@ -2,12 +2,12 @@ import os import unittest +import numpy as np import pytest +from astropy.io import fits +from pinttestdata import datadir import pint.scripts.photonphase as photonphase -from pinttestdata import datadir -from astropy.io import fits -import numpy as np parfile = os.path.join(datadir, "J1513-5908_PKS_alldata_white.par") eventfile = os.path.join(datadir, "B1509_RXTE_short.fits") diff --git a/tests/test_piecewise.py b/tests/test_piecewise.py index a0bc0be07..cbf86cb58 100644 --- a/tests/test_piecewise.py +++ b/tests/test_piecewise.py @@ -5,14 +5,14 @@ import astropy.units as u import numpy as np import pytest +from pinttestdata import datadir import pint.fitter import pint.models import pint.residuals import pint.toa -from pinttestdata import datadir -from pint.models.timing_model import MissingParameter from pint import fitter, toa +from pint.models.timing_model import MissingParameter parfile = os.path.join(datadir, "piecewise.par") parfile2 = os.path.join(datadir, "piecewise_twocomps.par") diff --git a/tests/test_pintbary.py b/tests/test_pintbary.py index 7def071a0..9fb5f4b47 100644 --- a/tests/test_pintbary.py +++ b/tests/test_pintbary.py @@ -1,9 +1,9 @@ #!/usr/bin/env python import sys import unittest +from io import StringIO import numpy as np -from io import StringIO import pint.scripts.pintbary as pintbary diff --git a/tests/test_pintempo.py b/tests/test_pintempo.py index 1f8963350..8a48bac0d 100644 --- a/tests/test_pintempo.py +++ b/tests/test_pintempo.py @@ -1,12 +1,12 @@ #!/usr/bin/env python import os import sys - from io import StringIO -from pint.scripts import pintempo from pinttestdata import datadir +from pint.scripts import pintempo + parfile = os.path.join(datadir, "NGC6440E.par") timfile = os.path.join(datadir, "NGC6440E.tim") diff --git a/tests/test_pintk.py b/tests/test_pintk.py index 92c1a2a3a..428f161cb 100644 --- a/tests/test_pintk.py +++ b/tests/test_pintk.py @@ -1,13 +1,13 @@ #!/usr/bin/env python import os import unittest +from io import StringIO import numpy as np import pytest -from io import StringIO +from pinttestdata import datadir import pint.scripts.pintk as pintk -from pinttestdata import datadir parfile = os.path.join(datadir, "NGC6440E.par") timfile = os.path.join(datadir, "NGC6440E.tim") diff --git a/tests/test_pintk_pulsar.py b/tests/test_pintk_pulsar.py index 0421bbd0e..4c6b2df5c 100644 --- a/tests/test_pintk_pulsar.py +++ b/tests/test_pintk_pulsar.py @@ -43,6 +43,7 @@ PEPOCH 57000 POSEPOCH 57000 F0 500 +JUMP mjd 56000 60000 0 JUMP mjd 58000 60000 0 JUMP mjd 59000 60000 0 """ @@ -67,3 +68,47 @@ def test_fit_summary(tmp_path, capsys): assert not re.search(r"Post-Fit Chi2:\s*[0-9.]+ +us", captured_fit_summary.out) assert re.search(r"Post-Fit Weighted RMS:\s*[0-9.]+ +us", captured_fit_summary.out) assert re.search(r"\s*JUMP", captured_fit_summary.out) + + +def test_fit_timfile_jumps(tmp_path): + # Pulsar can't cope with file-like objects in place of filenames - I think? + par_file = tmp_path / "file.par" + with open(par_file, "wt") as f: + f.write(par) + tim_file = tmp_path / "file.tim" + with open(tim_file, "wt") as f: + f.write(tim) + p = pint.pintk.pulsar.Pulsar(parfile=str(par_file), timfile=str(tim_file)) + p.fit(np.ones(len(p.all_toas), dtype=bool)) + + for i in range(1, 4): + for j in p.prefit_model.jumps: + if j.flag == "jump" and j.flag_value == str(i): + assert len(j.select_toa_mask(p.all_toas)) > 0 + break + else: + assert False, f"Jump {i} not found" + + +def test_fit_freezes_enough_jumps(tmp_path): + # Pulsar can't cope with file-like objects in place of filenames - I think? + par_file = tmp_path / "file.par" + with open(par_file, "wt") as f: + f.write(par) + tim_file = tmp_path / "file.tim" + with open(tim_file, "wt") as f: + f.write(tim) + p = pint.pintk.pulsar.Pulsar(parfile=str(par_file), timfile=str(tim_file)) + for j in p.prefit_model.jumps: + j.frozen = False + p.fit(np.ones(len(p.all_toas), dtype=bool)) + + jumped_toas = set(range(len(p.all_toas))) + for j in p.prefit_model.jumps: + if not j.frozen: + t = j.select_toa_mask(p.all_toas) + assert len(t) + for i in t: + jumped_toas.discard(i) + + assert jumped_toas diff --git a/tests/test_plk_widget.py b/tests/test_plk_widget.py index 00f6cdb94..ca0817c0e 100644 --- a/tests/test_plk_widget.py +++ b/tests/test_plk_widget.py @@ -1,7 +1,9 @@ +from tkinter import Frame + import astropy.units as u import pytest + from pint.pintk.plk import PlkWidget -from tkinter import Frame @pytest.fixture diff --git a/tests/test_polycos.py b/tests/test_polycos.py index 3e36acde5..b94127468 100644 --- a/tests/test_polycos.py +++ b/tests/test_polycos.py @@ -1,12 +1,14 @@ """Test polycos.""" -import pytest +from pathlib import Path + import numpy as np -from pint.polycos import Polycos -from pint.models import get_model -import pint.toa as toa +import pytest from pinttestdata import datadir -from pathlib import Path + +import pint.toa as toa +from pint.models import get_model +from pint.polycos import Polycos @pytest.fixture diff --git a/tests/test_priors.py b/tests/test_priors.py index 8d67e6801..961d1495a 100644 --- a/tests/test_priors.py +++ b/tests/test_priors.py @@ -3,18 +3,18 @@ import unittest import numpy as np +from pinttestdata import datadir from scipy.stats import norm import pint.models # from pint.models.priors import * from pint.models.priors import ( - Prior, - UniformBoundedRV, GaussianBoundedRV, + Prior, RandomInclinationPrior, + UniformBoundedRV, ) -from pinttestdata import datadir class TestPriors(unittest.TestCase): diff --git a/tests/test_pulsar_mjd.py b/tests/test_pulsar_mjd.py index 8c1185e93..4f3d0d453 100644 --- a/tests/test_pulsar_mjd.py +++ b/tests/test_pulsar_mjd.py @@ -3,10 +3,10 @@ import unittest import numpy as np -from pint.pulsar_mjd import Time - from pinttestdata import datadir +from pint.pulsar_mjd import Time + class TestPsrMjd(unittest.TestCase): @classmethod diff --git a/tests/test_pulsar_position.py b/tests/test_pulsar_position.py index c4eb1b6ca..067a0a499 100644 --- a/tests/test_pulsar_position.py +++ b/tests/test_pulsar_position.py @@ -4,9 +4,9 @@ import unittest import numpy as np +from pinttestdata import datadir import pint.models.model_builder as mb -from pinttestdata import datadir class TestPulsarPosition(unittest.TestCase): diff --git a/tests/test_pulse_number.py b/tests/test_pulse_number.py index 74a39ac3d..e47d42c56 100644 --- a/tests/test_pulse_number.py +++ b/tests/test_pulse_number.py @@ -1,19 +1,19 @@ #!/usr/bin/python -from io import StringIO import os -import pytest from copy import deepcopy +from io import StringIO import numpy as np +import pytest from astropy import units as u from numpy.testing import assert_almost_equal - -from pint.residuals import Residuals from pinttestdata import datadir + import pint.fitter from pint.models import get_model -from pint.toa import get_TOAs +from pint.residuals import Residuals from pint.simulation import make_fake_toas_uniform +from pint.toa import get_TOAs parfile = os.path.join(datadir, "withpn.par") timfile = os.path.join(datadir, "withpn.tim") diff --git a/tests/test_random_models.py b/tests/test_random_models.py index 2110f5228..4cb73eb43 100644 --- a/tests/test_random_models.py +++ b/tests/test_random_models.py @@ -1,19 +1,18 @@ import os from copy import deepcopy +import astropy.units as u + # import matplotlib # matplotlib.use('TKAgg') import matplotlib.pyplot as plt -import pytest import numpy as np -import astropy.units as u - -from pint.models import get_model, get_model_and_toas -from pint import fitter, toa, simulation +import pytest from pinttestdata import datadir + import pint.models.parameter as param -from pint import ls -from pint import utils +from pint import fitter, ls, simulation, toa, utils +from pint.models import get_model, get_model_and_toas def test_random_models(): diff --git a/tests/test_residuals.py b/tests/test_residuals.py index 856d1944b..ac7547d1b 100644 --- a/tests/test_residuals.py +++ b/tests/test_residuals.py @@ -16,8 +16,8 @@ from pint.models import get_model from pint.models.dispersion_model import Dispersion from pint.residuals import CombinedResiduals, Residuals, WidebandTOAResiduals -from pint.toa import get_TOAs from pint.simulation import make_fake_toas_uniform +from pint.toa import get_TOAs from pint.utils import weighted_mean os.chdir(datadir) diff --git a/tests/test_satobs.py b/tests/test_satobs.py index 9e957e630..c053ae8f6 100644 --- a/tests/test_satobs.py +++ b/tests/test_satobs.py @@ -1,11 +1,11 @@ import os -import pytest -from astropy.time import Time import numpy as np +import pytest +from astropy.time import Time +from pinttestdata import datadir from pint.observatory.satellite_obs import get_satellite_observatory -from pinttestdata import datadir def test_good_calls(): diff --git a/tests/test_solar_system_body.py b/tests/test_solar_system_body.py index 6ac9a91bd..45a5e9191 100644 --- a/tests/test_solar_system_body.py +++ b/tests/test_solar_system_body.py @@ -1,15 +1,15 @@ #!/usr/bin/env python import os import unittest -import pytest import astropy.time as time import numpy as np +import pytest from astropy.coordinates import solar_system_ephemeris +from pinttestdata import datadir import pint.config from pint.solar_system_ephemerides import objPosVel, objPosVel_wrt_SSB -from pinttestdata import datadir # Hack to support FileNotFoundError in Python 2 try: diff --git a/tests/test_solar_wind.py b/tests/test_solar_wind.py index 6d83553dc..c8db21849 100644 --- a/tests/test_solar_wind.py +++ b/tests/test_solar_wind.py @@ -1,21 +1,22 @@ """ Test for pint solar wind module """ -import os -from io import StringIO -import pytest -import numpy as np -from numpy.testing import assert_allclose import copy +import os import sys +from io import StringIO import astropy.units as u +import numpy as np +import pytest from astropy.time import Time -from pint.models import get_model +from numpy.testing import assert_allclose +from pinttestdata import datadir + from pint.fitter import WidebandTOAFitter -from pint.toa import get_TOAs +from pint.models import get_model from pint.simulation import make_fake_toas_uniform -from pinttestdata import datadir +from pint.toa import get_TOAs os.chdir(datadir) diff --git a/tests/test_stigma.py b/tests/test_stigma.py index 54157e0f3..512da742a 100644 --- a/tests/test_stigma.py +++ b/tests/test_stigma.py @@ -1,19 +1,18 @@ +import io import logging import os -import io +import tempfile import astropy.units as u import numpy as np import pytest -import tempfile - -from pint.models import get_model -import pint.toa as toa -import pint.simulation as simulation import test_derivative_utils as tdu -from pint.residuals import Residuals from pinttestdata import datadir +import pint.simulation as simulation +import pint.toa as toa +from pint.models import get_model +from pint.residuals import Residuals stigma_template = """ PSR FAKE diff --git a/tests/test_templates.py b/tests/test_templates.py index 89f597d7b..2519ad680 100644 --- a/tests/test_templates.py +++ b/tests/test_templates.py @@ -1,8 +1,8 @@ import logging import numpy as np - from pinttestdata import datadir + from pint.templates import lcfitters, lcprimitives, lctemplate diff --git a/tests/test_tempox_compatibility.py b/tests/test_tempox_compatibility.py index 9ac780925..e4928c93b 100644 --- a/tests/test_tempox_compatibility.py +++ b/tests/test_tempox_compatibility.py @@ -1,11 +1,12 @@ -from io import StringIO import re +from io import StringIO import astropy.units as u import numpy as np import pytest -from pint.models import get_model + import pint.toa +from pint.models import get_model par_basic = """ PSR J1234+5678 diff --git a/tests/test_tim_writing.py b/tests/test_tim_writing.py index 2be63b9f5..a1795486e 100644 --- a/tests/test_tim_writing.py +++ b/tests/test_tim_writing.py @@ -1,11 +1,12 @@ -from io import StringIO import re +from io import StringIO import astropy.units as u import numpy as np import pytest from hypothesis import given, settings -from hypothesis.strategies import lists, tuples, one_of, from_regex, just +from hypothesis.strategies import from_regex, just, lists, one_of, tuples + from pint.toa import get_TOAs basic_tim_header = "FORMAT 1\n" diff --git a/tests/test_times.py b/tests/test_times.py index 7481fa014..8376231b8 100644 --- a/tests/test_times.py +++ b/tests/test_times.py @@ -4,12 +4,12 @@ import astropy.units as u import numpy from astropy import log +from pinttestdata import datadir from pint import erfautils, toa from pint.observatory import Observatory from pint.pulsar_mjd import Time from pint.utils import PosVel -from pinttestdata import datadir def test_times_against_tempo2(): diff --git a/tests/test_timing_model.py b/tests/test_timing_model.py index 04b0be131..bf563146b 100644 --- a/tests/test_timing_model.py +++ b/tests/test_timing_model.py @@ -6,6 +6,7 @@ import astropy.units as u import numpy as np import pytest +from astropy.time import Time from numpy.testing import assert_allclose from pinttestdata import datadir @@ -13,7 +14,7 @@ DEFAULT_ORDER, AstrometryEquatorial, BinaryELL1, - DelayJump, + PhaseJump, Spindown, TimingModel, Wave, @@ -88,23 +89,31 @@ def test_add_component(self): "TestTimingModel", [BinaryELL1(), AstrometryEquatorial(), Spindown()] ) - tm.add_component(DelayJump(), validate=False) + tm.add_component(PhaseJump(), validate=False) # Test link # TODO may be add a get_component function - cp = tm.components["DelayJump"] + cp = tm.components["PhaseJump"] assert cp._parent == tm # Test order - cp_pos = tm.DelayComponent_list.index(cp) - assert cp_pos == 2 + # Wait why do we know this should be number 1? + cp_pos = tm.PhaseComponent_list.index(cp) + assert cp_pos == 1 print(cp.params) print(cp.get_prefix_mapping_component("JUMP")) print(id(cp), "test") add_jumps = [ - ("JUMP", {"value": 0.1, "key": "mjd", "key_value": [55000, 56000]}), - ("JUMP", {"value": 0.2, "key": "freq", "key_value": [1440, 2000]}), - ("JUMP", {"value": 0.3, "key": "tel", "key_value": "ao"}), + ("JUMP", {"value": 0.1, "flag": "mjd", "flag_value": [55000, 56000],},), + ( + "JUMP", + { + "value": 0.2, + "flag": "freq", + "flag_value": [1440 * u.MHz, 2000 * u.MHz], + }, + ), + ("JUMP", {"value": 0.3, "flag": "tel", "flag_value": "ao"}), ] for jp in add_jumps: @@ -113,9 +122,9 @@ def test_add_component(self): p_vals = jp[1] par = p.maskParameter( name=p_name, - key=p_vals["key"], + flag=p_vals["flag"], value=p_vals["value"], - key_value=p_vals["key_value"], + flag_value=p_vals["flag_value"], units=u.s, ) print("test", par.name) @@ -131,18 +140,16 @@ def test_add_component(self): jump1 = getattr(tm, "JUMP1") jump2 = getattr(tm, "JUMP2") jump3 = getattr(tm, "JUMP3") - assert jump1.key == "mjd" - assert jump2.key == "freq" - assert jump3.key == "tel" - # Check jump value + assert jump1.flag == "mjd" + assert jump2.flag == "freq" + assert jump3.flag == "tel" assert jump1.value == 0.1 assert jump2.value == 0.2 assert jump3.value == 0.3 - # Check jump key value - assert jump1.key_value == [55000, 56000] - assert jump2.key_value == [1440 * u.MHz, 2000 * u.MHz] - assert jump3.key_value == ["arecibo"] - assert tm.jumps == ["JUMP1", "JUMP2", "JUMP3"] + assert jump1.flag_value == (55000, 56000) + assert jump2.flag_value == (1440 * u.MHz, 2000 * u.MHz) + assert jump3.flag_value == "arecibo" + assert len(tm.jumps) == 3 def test_remove_component(self): tm = TimingModel( @@ -153,7 +160,7 @@ def test_remove_component(self): # test remove by name tm.remove_component("BinaryELL1") - assert "BinaryELL1" not in tm.components.keys() + assert "BinaryELL1" not in tm.components assert remove_cp not in tm.DelayComponent_list # test remove by component @@ -163,7 +170,7 @@ def test_remove_component(self): remove_cp2 = tm2.components["BinaryELL1"] tm2.remove_component(remove_cp2) - assert "BinaryELL1" not in tm2.components.keys() + assert "BinaryELL1" not in tm2.components assert remove_cp2 not in tm2.DelayComponent_list def test_free_params(self): @@ -333,8 +340,12 @@ def test_jump_flags_to_params(timfile_jumps, timfile_nojumps, model_0437): m.jump_flags_to_params(t) assert "PhaseJump" in m.components assert len(m.components["PhaseJump"].jumps) == 2 - assert "JUMP1" in m.components["PhaseJump"].jumps - assert "JUMP2" in m.components["PhaseJump"].jumps + assert ("jump", "1") in [ + (j.flag, j.flag_value) for j in m.components["PhaseJump"].jumps + ] + assert ("jump", "2") in [ + (j.flag, j.flag_value) for j in m.components["PhaseJump"].jumps + ] def test_supports_rm(): diff --git a/tests/test_toa.py b/tests/test_toa.py index 18bfeb2e4..b624de8f4 100644 --- a/tests/test_toa.py +++ b/tests/test_toa.py @@ -9,8 +9,8 @@ from pint.models import get_model from pint.observatory import get_observatory -from pint.toa import TOA, TOAs from pint.simulation import make_fake_toas_uniform +from pint.toa import TOA, TOAs class TestTOA(unittest.TestCase): diff --git a/tests/test_toa_flag.py b/tests/test_toa_flag.py index 5c60b16b2..706695d8f 100644 --- a/tests/test_toa_flag.py +++ b/tests/test_toa_flag.py @@ -4,9 +4,10 @@ import os import unittest -import pint.toa as toa from pinttestdata import datadir +import pint.toa as toa + class TestToaFlag(unittest.TestCase): """Compare delays from the dd model with tempo and PINT""" diff --git a/tests/test_toa_indexing.py b/tests/test_toa_indexing.py index 6cc2b40a6..bf1bded8f 100644 --- a/tests/test_toa_indexing.py +++ b/tests/test_toa_indexing.py @@ -2,9 +2,9 @@ import numpy as np import pytest -from hypothesis import given, assume -from hypothesis.strategies import slices, integers, booleans, one_of, lists -from hypothesis.extra.numpy import arrays, array_shapes +from hypothesis import assume, given +from hypothesis.extra.numpy import array_shapes, arrays +from hypothesis.strategies import booleans, integers, lists, one_of, slices from pint.toa import get_TOAs diff --git a/tests/test_toa_pickle.py b/tests/test_toa_pickle.py index 50327719b..fbc6f4e21 100644 --- a/tests/test_toa_pickle.py +++ b/tests/test_toa_pickle.py @@ -13,8 +13,7 @@ import pint.models import pint.toa -from pint import toa -from pint import simulation +from pint import simulation, toa @pytest.fixture diff --git a/tests/test_toa_reader.py b/tests/test_toa_reader.py index 9861ae40e..58dbc38e3 100644 --- a/tests/test_toa_reader.py +++ b/tests/test_toa_reader.py @@ -1,21 +1,22 @@ import os -import unittest -import pytest -import numpy as np import shutil -from io import StringIO +import unittest from glob import glob +from io import StringIO + +import numpy as np +import pytest +# For this test, turn off the check for the age of the IERS A table +from astropy.utils.iers import conf from hypothesis import given -from hypothesis.strategies import integers, floats, sampled_from from hypothesis.extra.numpy import arrays -from pint import toa, simulation -from pint.observatory import bipm_default -from pint.models import get_model, get_model_and_toas +from hypothesis.strategies import floats, integers, sampled_from from pinttestdata import datadir -# For this test, turn off the check for the age of the IERS A table -from astropy.utils.iers import conf +from pint import simulation, toa +from pint.models import get_model, get_model_and_toas +from pint.observatory import bipm_default conf.auto_max_age = None diff --git a/tests/test_toa_selection.py b/tests/test_toa_selection.py index f96424253..444474672 100644 --- a/tests/test_toa_selection.py +++ b/tests/test_toa_selection.py @@ -5,15 +5,15 @@ import astropy.units as u import numpy as np +from pinttestdata import datadir + +import pint.models.model_builder as mb +import pint.toa as toa # import matplotlib # matplotlib.use('TKAgg') # import matplotlib.pyplot as plt -import pint.models.model_builder as mb -import pint.toa as toa -from pinttestdata import datadir - class TestTOAselection(unittest.TestCase): @classmethod diff --git a/tests/test_toa_writer.py b/tests/test_toa_writer.py index 873cb1a39..5062bd5c4 100644 --- a/tests/test_toa_writer.py +++ b/tests/test_toa_writer.py @@ -1,14 +1,15 @@ -from io import StringIO import os import os.path +from io import StringIO + +import astropy.units as u +import numpy as np import pytest +from astropy.time import Time +from pinttestdata import datadir +from pint import simulation, toa from pint.models import get_model -from pint import toa, simulation -from pinttestdata import datadir -from astropy.time import Time -import numpy as np -import astropy.units as u def test_roundtrip_bary_toa_Tempo2format(tmpdir): @@ -141,7 +142,7 @@ def test_roundtrip_topo_toa_TEMPOformat(tmpdir): """ -def test_tim_writing_order(): +def test_tim_writing_order(tmpdir): m = get_model(StringIO(simplepar)) fakes = [ simulation.make_fake_toas_uniform(55000, 56000, 5, model=m, obs="ao"), @@ -153,7 +154,8 @@ def test_tim_writing_order(): o = StringIO() toas.write_TOA_file(o, order_by_index=False) - toas.write_TOA_file("test.tim", order_by_index=False) + # make sure we can write to an actual file I guess? + toas.write_TOA_file(os.path.join(tmpdir, "test.tim"), order_by_index=False) obs = [ ln.split()[4] for ln in o.getvalue().split("\n")[1:] diff --git a/tests/test_troposphere_model.py b/tests/test_troposphere_model.py index 071b774bf..0209ca938 100644 --- a/tests/test_troposphere_model.py +++ b/tests/test_troposphere_model.py @@ -5,15 +5,15 @@ import astropy.units as u import numpy as np +from astropy.coordinates import AltAz, SkyCoord +from astropy.time import Time +from pinttestdata import testdir + import pint.models as models import pint.observatory import pint.toa as toa -from astropy.coordinates import AltAz, SkyCoord -from astropy.time import Time from pint.observatory import get_observatory -from pinttestdata import testdir - class TestTroposphereDelay(unittest.TestCase): diff --git a/tests/test_widebandTOA_fitting.py b/tests/test_widebandTOA_fitting.py index 6b5af854d..be52fe44e 100644 --- a/tests/test_widebandTOA_fitting.py +++ b/tests/test_widebandTOA_fitting.py @@ -1,15 +1,16 @@ """ Various of tests for the general data fitter using wideband TOAs. """ -import pytest import os -import numpy as np import astropy.units as u +import numpy as np +import pytest +from pinttestdata import datadir + +from pint.fitter import WidebandTOAFitter from pint.models import get_model from pint.toa import get_TOAs -from pint.fitter import WidebandTOAFitter -from pinttestdata import datadir os.chdir(datadir) diff --git a/tests/test_wideband_dm_data.py b/tests/test_wideband_dm_data.py index f30d8cd27..083205797 100644 --- a/tests/test_wideband_dm_data.py +++ b/tests/test_wideband_dm_data.py @@ -9,13 +9,13 @@ import numpy as np import pytest from astropy.time import TimeDelta -from pinttestdata import datadir from numpy.testing import assert_allclose +from pinttestdata import datadir +from pint.fitter import WidebandTOAFitter from pint.models import get_model from pint.residuals import Residuals, WidebandTOAResiduals from pint.toa import get_TOAs -from pint.fitter import WidebandTOAFitter os.chdir(datadir) @@ -118,7 +118,7 @@ def test_dm_jumps(self): dm_jump_value = self.model.jump_dm(self.toas) dm_jump_map = {} for dmj in self.dm_jump_params: - dm_jump_map[dmj.key_value[0]] = dmj + dm_jump_map[dmj.flag_value] = dmj for be in all_backends: assert all( dm_jump_value[self.toa_backends == be] == -dm_jump_map[be].quantity @@ -148,7 +148,7 @@ def test_dmjump_derivative(self): for dmj_param in self.dm_jump_params: # test derivative function in the dmjump component d_dm_d_dmjump = self.model.d_dm_d_dmjump(self.toas, dmj_param.name) - be = dmj_param.key_value + be = dmj_param.flag_value # The derivative of dm with respect to dm jump is -1 for the jumped # TOAs/DM data, the others are zero assert all(d_dm_d_dmjump[self.toa_backends == be] == -1.0 * u.Unit("")) diff --git a/tests/test_wls_fitter.py b/tests/test_wls_fitter.py index 089bf179d..7e7106543 100644 --- a/tests/test_wls_fitter.py +++ b/tests/test_wls_fitter.py @@ -2,10 +2,11 @@ import os import unittest -from pint.models.model_builder import get_model +from pinttestdata import datadir + from pint import toa from pint.fitter import WLSFitter -from pinttestdata import datadir +from pint.models.model_builder import get_model class Testwls(unittest.TestCase): diff --git a/tests/test_zima.py b/tests/test_zima.py index 267a6fe7b..6f15f19d1 100644 --- a/tests/test_zima.py +++ b/tests/test_zima.py @@ -2,12 +2,12 @@ import os import sys import unittest +from io import StringIO import numpy as np -from io import StringIO +from pinttestdata import datadir, testdir import pint.scripts.zima as zima -from pinttestdata import datadir, testdir parfile = os.path.join(datadir, "NGC6440E.par") timfile = os.path.join(datadir, "fake_testzima.tim") diff --git a/tests/utils.py b/tests/utils.py index 54874af59..cb75eb387 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,10 +1,11 @@ """Utililty functions for the tests""" import logging import os -from io import StringIO import unittest -import pytest import warnings +from io import StringIO + +import pytest def verify_stand_alone_binary_parameter_updates(m):