From d82bdd20e49a1b3125679be45201f4edaedeadae Mon Sep 17 00:00:00 2001 From: Anne Archibald Date: Wed, 14 Jul 2021 22:36:27 +0100 Subject: [PATCH 01/17] Document JUMP objects --- src/pint/models/jump.py | 119 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 111 insertions(+), 8 deletions(-) diff --git a/src/pint/models/jump.py b/src/pint/models/jump.py index 9273fd669..fde12ae05 100644 --- a/src/pint/models/jump.py +++ b/src/pint/models/jump.py @@ -9,9 +9,16 @@ log = logging.getLogger(__name__) +__all__ = ["PhaseJump"] + class DelayJump(DelayComponent): - """Phase jumps + """Delay jumps + + This is a different kind of JUMP, conceptually affecting the TOAs directly, + so that a 60s jump would advance the pulsar around its orbit by 60 extra + seconds. The conventional JUMP affects only the observed phase, as you + would expect if they are due to a redefinition of phase zero in a template. Parameters supported: @@ -75,13 +82,53 @@ def print_par(self): 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. + + 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``. 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 @@ -99,6 +146,7 @@ def __init__(self): self.phase_funcs_component += [self.jump_phase] def setup(self): + """Set up support data structures to reflect parameters as set.""" super().setup() self.jumps = [] for mask_par in self.get_params_of_type("maskParameter"): @@ -111,9 +159,23 @@ def setup(self): 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 JUMP. + + 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) @@ -126,6 +188,22 @@ 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 phase shift for each TOA. + """ tbl = toas.table jpar = getattr(self, jump_param) d_phase_d_j = numpy.zeros(len(tbl)) @@ -134,6 +212,7 @@ def d_phase_d_jump(self, toas, jump_param, delay): 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) @@ -145,10 +224,7 @@ def get_number_of_jumps(self): return len(self.jumps) def get_jump_param_objects(self): - """ - Returns a list of the maskParameter objects representing the jumps - in this PhaseJump object. - """ + """Returns a list of the maskParameter objects for all JUMPs.""" jump_obs = [getattr(self, jump) for jump in self.jumps] return jump_obs @@ -159,6 +235,17 @@ def jump_params_to_flags(self, toas): 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 function steps through all the defined JUMP parameters, and for each it + chooses all the TOAs affected by that JUMP and ensures that they have a flag + ``-jump`` whose argument contains the index of that JUMP. + + At the end of the process, every TOA that is affected by any JUMP has a + flag ``-jump`` whose argument is either the index of the jump + parameter, or a list containing the index of the jump parameter. This + flag cannot be used in a JUMP as these only select on exact matches, so + that ``JUMP -jump 17 0.1`` would fail to match a TOA with the flag + ``-jump [17, 18]``. + Parameters ---------- toas: TOAs object @@ -186,6 +273,14 @@ def add_jump_and_flags(self, toa_table): 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. + This will add a parameter to the model corresponding to:: + + JUMP -gui_jump N 0 1 + + where ``N`` is one more than the number of jumps that currently exist. 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 @@ -211,10 +306,18 @@ def add_jump_and_flags(self, toa_table): # 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 +<<<<<<< HEAD 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." +======= + for dict in toa_table: + if "gui_jump" in dict.keys(): + log.error( + "The selected toa(s) overlap an existing jump. Remove all " + "interfering jumps before attempting to jump these toas." +>>>>>>> Document JUMP objects ) return None param = maskParameter( From 92c98f7f2ed5fd03510838e76f03776e392453d3 Mon Sep 17 00:00:00 2001 From: Anne Archibald Date: Wed, 14 Jul 2021 23:39:30 +0100 Subject: [PATCH 02/17] Check a few JUMP behaviours --- tests/test_jump.py | 114 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 107 insertions(+), 7 deletions(-) diff --git a/tests/test_jump.py b/tests/test_jump.py index 2cf3f4b15..60fee30af 100644 --- a/tests/test_jump.py +++ b/tests/test_jump.py @@ -3,13 +3,14 @@ import os import unittest import pytest -import copy +from io import StringIO import astropy.units as u import numpy as np +from numpy.testing import assert_allclose import pint.models.model_builder as mb -import pint.toa as toa +import pint.toa from pint.residuals import Residuals from pinttestdata import datadir from pint.models import parameter as p @@ -21,7 +22,7 @@ def __init__(self, par, tim): self.par = par self.tim = tim self.m = mb.get_model(self.par) - self.t = toa.get_TOAs( + self.t = pint.toa.get_TOAs( self.tim, ephem="DE405", planets=False, include_bipm=False ) @@ -171,7 +172,7 @@ def setUpClass(cls): 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.toas = pint.toa.get_TOAs( cls.timf, ephem="DE405", planets=False, include_bipm=False ) # libstempo calculation @@ -204,6 +205,105 @@ 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 = mb.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 j in m.jumps: + jmp = getattr(m, j) + if jmp.key == 'mjd': + start, end = jmp.key_value + if start < 58500: + first_jump = jmp + else: + second_jump = jmp + toas = pint.toa.make_fake_toas(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, r_sum.resids, atol=1e-3*u.us) From 43e40262df7d2f758d5238eac8d3d9920003c8f6 Mon Sep 17 00:00:00 2001 From: Anne Archibald Date: Wed, 14 Jul 2021 23:41:16 +0100 Subject: [PATCH 03/17] Fix docstring for derivative --- src/pint/models/jump.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pint/models/jump.py b/src/pint/models/jump.py index fde12ae05..9cb198a86 100644 --- a/src/pint/models/jump.py +++ b/src/pint/models/jump.py @@ -202,7 +202,7 @@ def d_phase_d_jump(self, toas, jump_param, delay): Returns ------- astropy.units.Quantity - The phase shift for each TOA. + The derivative of phase shift with respect to the parameter for each TOA. """ tbl = toas.table jpar = getattr(self, jump_param) From beead5868f127e19c968e128ba635e516937b014 Mon Sep 17 00:00:00 2001 From: Anne Archibald Date: Tue, 20 Jul 2021 14:01:55 +0100 Subject: [PATCH 04/17] Clean up maskParameter so jumps make more sense --- src/pint/models/jump.py | 17 +- src/pint/models/parameter.py | 289 ++++++++++++++---------- src/pint/models/timing_model.py | 46 ++-- src/pint/scripts/pintempo.py | 4 - tests/test_B1855.py | 4 +- tests/test_B1855_9yrs.py | 4 +- tests/test_B1953.py | 6 +- tests/test_Galactic.py | 13 +- tests/test_J0613.py | 4 +- tests/test_TDB_method.py | 2 +- tests/test_absphase.py | 3 +- tests/test_astrometry.py | 1 + tests/test_astropyobservatory.py | 2 + tests/test_barytoa.py | 4 +- tests/test_binary_generic.py | 12 +- tests/test_clockcorr.py | 8 +- tests/test_compare.py | 12 +- tests/test_copy.py | 11 +- tests/test_covariance_matrix.py | 8 +- tests/test_d_phase_d_toa.py | 3 +- tests/test_dd.py | 4 +- tests/test_ddk.py | 4 - tests/test_density.py | 14 +- tests/test_design_matrix.py | 11 +- tests/test_determinism.py | 2 +- tests/test_dmefac_dmequad.py | 17 +- tests/test_dmx.py | 2 +- tests/test_dmxrange_add_sub.py | 4 +- tests/test_early_chime_data.py | 2 +- tests/test_ecorr_average.py | 3 +- tests/test_ell1h.py | 13 +- tests/test_event_optimize.py | 4 +- tests/test_event_optimize_MCMCFitter.py | 1 - tests/test_event_optimize_multiple.py | 1 - tests/test_event_toas.py | 10 +- tests/test_eventstats.py | 2 +- tests/test_fbx.py | 4 +- tests/test_fd.py | 4 +- tests/test_fermiphase.py | 5 +- tests/test_fitter.py | 4 +- tests/test_fitter_compare.py | 2 +- tests/test_fitter_error_checking.py | 4 +- tests/test_glitch.py | 2 +- tests/test_gls_fitter.py | 2 +- tests/test_jump.py | 139 +++++++++--- tests/test_mask_parameter.py | 44 +++- tests/test_model.py | 2 +- tests/test_model_ifunc.py | 4 +- tests/test_model_manual.py | 11 +- tests/test_model_wave.py | 4 +- tests/test_modelutils.py | 5 +- tests/test_new_observatories.py | 8 +- tests/test_observatory.py | 4 +- tests/test_observatory_metadata.py | 2 + tests/test_orbit_phase.py | 4 +- tests/test_parametercovariancematrix.py | 4 +- tests/test_parameters.py | 95 ++++++++ tests/test_parfile.py | 2 +- tests/test_parfile_writing.py | 4 +- tests/test_phase.py | 9 +- tests/test_phase_commands.py | 7 +- tests/test_photonphase.py | 6 +- tests/test_piecewise.py | 4 +- tests/test_pintbary.py | 2 +- tests/test_pintempo.py | 4 +- tests/test_pintk.py | 4 +- tests/test_polycos.py | 12 +- tests/test_priors.py | 6 +- tests/test_pulsar_mjd.py | 4 +- tests/test_pulsar_position.py | 2 +- tests/test_pulse_number.py | 8 +- tests/test_random_models.py | 9 +- tests/test_satobs.py | 6 +- tests/test_solar_system_body.py | 4 +- tests/test_solar_wind.py | 13 +- tests/test_stigma.py | 5 +- tests/test_templates.py | 2 +- tests/test_tempox_compatibility.py | 5 +- tests/test_tim_writing.py | 5 +- tests/test_times.py | 2 +- tests/test_toa_flag.py | 3 +- tests/test_toa_indexing.py | 6 +- tests/test_toa_reader.py | 18 +- tests/test_toa_selection.py | 8 +- tests/test_toa_writer.py | 7 +- tests/test_troposphere_model.py | 8 +- tests/test_widebandTOA_fitting.py | 9 +- tests/test_wideband_dm_data.py | 4 +- tests/test_wls_fitter.py | 5 +- tests/test_zima.py | 4 +- tests/utils.py | 5 +- 91 files changed, 699 insertions(+), 403 deletions(-) diff --git a/src/pint/models/jump.py b/src/pint/models/jump.py index 9cb198a86..2be79ad81 100644 --- a/src/pint/models/jump.py +++ b/src/pint/models/jump.py @@ -94,6 +94,13 @@ class PhaseJump(PhaseComponent): 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' + >>> model + 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 @@ -111,7 +118,10 @@ class PhaseJump(PhaseComponent): 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``. + 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: @@ -149,9 +159,12 @@ def setup(self): """Set up support data structures to reflect parameters as set.""" super().setup() self.jumps = [] + self.jump_dict = {} for mask_par in self.get_params_of_type("maskParameter"): if mask_par.startswith("JUMP"): self.jumps.append(mask_par) + mp = getattr(self, mask_par) + self.jump_dict[mp.key, mp.key_value] = mp for j in self.jumps: # prevents duplicates from being added to phase_deriv_funcs if j in self.deriv_funcs.keys(): @@ -159,7 +172,7 @@ def setup(self): self.register_deriv_funcs(self.d_phase_d_jump, j) def jump_phase(self, toas, delay): - """The extra phase contributed by the JUMP. + """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 diff --git a/src/pint/models/parameter.py b/src/pint/models/parameter.py index 73508585a..fe150c91d 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 @@ -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( @@ -1452,7 +1454,7 @@ def __init__( name, index=1, key=None, - key_value=[], + key_value=None, value=None, long_double=False, units=None, @@ -1466,36 +1468,14 @@ def __init__( # {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), + "mjd": (lambda x: astropy.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.key_value = key_value self.index = index name_param = name + str(index) self.origin_name = name @@ -1523,13 +1503,101 @@ 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(key, key_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 key is None: + if key_value is not None: + raise ValueError(f"Cannot associate a value with no flag") + if key == "mjd": + start, end = sorted(key_value) + if not isinstance(start, astropy.time.Time) or not isinstance( + end, astropy.time.Time + ): + raise ValueError( + f"When selecting by MJD one must supply a " + f"pair of Times not {key_value}" + ) + return start, end + elif key == "freq": + low, high = sorted(key_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 {key_value}" + ) + return low.to(u.MHz), high.to(u.MHz) + elif key == "tel": + return get_observatory(key_value).name + else: + if not isinstance(key_value, str): + raise ValueError( + f"When selecting by {key} one must supply a " + f"string not {key_value}" + ) + return key_value + + @staticmethod + def convert(key, key_value): + """Convert strings to the appropriate form for the key.""" + if key == "mjd": + start, end = key_value + return ( + astropy.time.Time(start, format="mjd"), + astropy.time.Time(end, format="mjd"), + ) + elif key == "freq": + start, end = key_value + return float(start) * u.MHz, float(end) * u.MHz + else: + return key_value + + @property + def key(self): + return self._key + + @key.setter + def key(self, key): + if key is None: + self._key = None + else: + key = key.lower() + if key not in self.allowed_non_flags and not key.startswith("-"): + raise ValueError( + f"Flags must be indicated with -, and the only " + f"non-flags allowed are {maskParameter.allowed_non_flags}." + ) + self._key = key + + @property + def key_value(self): + return self._key_value + + @key_value.setter + def key_value(self, key_value): + if key_value is None: + self._key_value = None + else: + self._key_value = maskParameter.validate(self.key, key_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 isinstance(self.key_value, str): + out += " " + self.key_value + else: + for kv in self.key_value: + out += " " + str(kv) if self.quantity is not None: out += " " + self.str_quantity(self.quantity) else: @@ -1553,6 +1621,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 +1633,24 @@ def from_parfile_line(self, line): Notes ----- - The accepted format:: + The accepted formats for most keys:: 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 + + If the key is one of MJD or FREQ then:: + 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 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: @@ -1588,70 +1667,58 @@ def from_parfile_line(self, line): "{}: No key found on timfile line {!r}".format(self.name, line) ) - key_value_info = self.key_identifier.get(self.key.lower(), (str, 1)) - len_key_v = key_value_info[1] + if self.key in self.wants_two_values: + key_value_str = k[2], k[3] + len_key_v = 2 + else: + key_value_str = k[2] + len_key_v = 1 + self.key_value = maskParameter.convert(self.key, key_value_str) if len(k) < 3 + len_key_v: raise ValueError( "{}: Expected at least {} entries on timfile line {!r}".format( self.name, 3 + len_key_v, line ) ) + self.value = str2longdouble(k[2 + len_key_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_key_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 isinstance(self.key_value, str): + line += self.key_value + else: + start, end = self.key_value + if isinstance(start, astropy.time.Time): + line += f"{time_to_mjd_string(start)} {time_to_mjd_string(end)} " + elif isinstance(start, u.Quantity): + line += f"{start.to_value(u.MHz)} {end.to_value(u.MHz)} " else: - line += f"{kv} " - line += "%25s" % self.str_quantity(self.quantity) + raise ValueError( + f"Unexpected format in self.key_value: {self.key_value}" + ) + line += "%25s" % self.print_quantity(self.quantity) if self.uncertainty is not None: line += " %d %s" % (0 if self.frozen else 1, str(self.uncertainty_value)) elif not self.frozen: @@ -1690,55 +1757,35 @@ 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::] - 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] + column_match = {"mjd": "mjd_float", "freq": "freq", "tel": "obs"} + if self.key == "mjd": + start, end = self.key_value + c = toas.table["mjd_float"] >= start.mjd + c &= toas.table["mjd_float"] <= end.mjd + r = np.nonzero(c)[0] + elif self.key == "freq": + low, high = self.key_value + c = toas.table["freq"] >= low + c &= toas.table["freq"] <= high + r = np.nonzero(c)[0] + elif self.key == "tel": + c = toas.table["obs"] == self.key_value + r = np.nonzero(c)[0] 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.key is not None and self.key_value is not None: + for i, f in enumerate(toas.table["flags"]): + if f.get(self.key[1:], None) == self.key_value: + r.append(i) + r = np.array(r, dtype=int) + return r class pairParameter(floatParameter): diff --git a/src/pint/models/timing_model.py b/src/pint/models/timing_model.py index 65e4bab26..94d15c7a6 100644 --- a/src/pint/models/timing_model.py +++ b/src/pint/models/timing_model.py @@ -1328,15 +1328,22 @@ 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 # check if any TOAs are jumped - jumped = ["jump" in flag_dict.keys() for flag_dict in toas.table["flags"]] + jumped = [flag in flag_dict for flag_dict in toas.table["flags"]] if not any(jumped): log.info("No jump flags to process from .tim file") return None @@ -1348,24 +1355,23 @@ def jump_flags_to_params(self, toas): a.setup() self.add_component(a) self.remove_param("JUMP1") + # FIXME: numbers don't make sense here! # 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() + existing_jumps = self.components["PhaseJump"].jump_dict + val = flag_dict[flag] + if (flag, val) not in existing_jumps: + param = maskParameter( + name="JUMP", + index=None, + key="-" + flag, + key_value=val, + value=0.0, + units="second", + uncertainty=0.0, + frozen=False, + ) + self.add_param_from_top(param, "PhaseJump") + 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 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..c95a8045a 100644 --- a/tests/test_astrometry.py +++ b/tests/test_astrometry.py @@ -5,6 +5,7 @@ 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 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_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..169b582d3 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 numpy as np +import pytest +from astropy.time import Time +from pylab import * + import pint.toa as toa import pint.simulation as simulation 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_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..44dc1191f 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 @@ -40,7 +39,7 @@ 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.key_value = "Rcvr_800" test_model.DMEFAC1.value = 10 test_model.setup() scale_sigma = test_model.scale_dm_sigma(test_toas) @@ -52,7 +51,7 @@ 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.key_value = "YUPPI" test_model.DMEQUAD1.value = 10 test_model.setup() scale_sigma = test_model.scale_dm_sigma(test_toas) @@ -67,10 +66,10 @@ 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.key_value = "Rcvr_800" test_model.DMEQUAD1.value = 10 test_model.DMEFAC1.key = "-fe" - test_model.DMEFAC1.key_value = ["Rcvr_800"] + test_model.DMEFAC1.key_value = "Rcvr_800" test_model.DMEFAC1.value = 10 test_model.setup() scale_sigma = test_model.scale_dm_sigma(test_toas) @@ -90,10 +89,10 @@ 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.key_value = "Rcvr_800" test_model.DMEQUAD1.value = 10 test_model.DMEFAC1.key = "-fe" - test_model.DMEFAC1.key_value = ["YUPPI"] + test_model.DMEFAC1.key_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_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_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..b306738be 100644 --- a/tests/test_fitter.py +++ b/tests/test_fitter.py @@ -2,11 +2,13 @@ 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 diff --git a/tests/test_fitter_compare.py b/tests/test_fitter_compare.py index 9e09b9c7b..bf6664e53 100644 --- a/tests/test_fitter_compare.py +++ b/tests/test_fitter_compare.py @@ -10,10 +10,10 @@ import pint from pint.fitter import ( ConvergenceFailure, - MaxiterReached, DownhillGLSFitter, DownhillWLSFitter, GLSFitter, + MaxiterReached, WidebandDownhillFitter, WidebandTOAFitter, WLSFitter, 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_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_jump.py b/tests/test_jump.py index 60fee30af..e1ec9489b 100644 --- a/tests/test_jump.py +++ b/tests/test_jump.py @@ -1,20 +1,22 @@ """Tests for jump model component """ import logging import os +import re import unittest -import pytest from io import StringIO +import astropy.time import astropy.units as u import numpy as np +import pytest from numpy.testing import assert_allclose +from pinttestdata import datadir import pint.models.model_builder as mb +import pint.models.parameter import pint.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: @@ -98,7 +100,11 @@ def test_jump_params_to_flags(setup_NGC6440E): 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", + key="freq", + value=0.2, + key_value=[1440 * u.MHz, 1700 * u.MHz], + units=u.s, ) # TOAs indexed 48, 49, 54 in NGC6440E are within this frequency range cp.add_param(par, setup=True) @@ -126,7 +132,11 @@ def test_jump_params_to_flags(setup_NGC6440E): # check that adding overlapping jump works par2 = p.maskParameter( - name="JUMP", key="freq", value=0.2, key_value=[1600, 1900], units=u.s + name="JUMP", + key="freq", + value=0.2, + key_value=[1600 * u.MHz, 1900 * u.MHz], + 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 @@ -144,7 +154,11 @@ 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", + key="freq", + value=0.2, + key_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) @@ -168,9 +182,8 @@ def test_multijump_toa(setup_NGC6440E): 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.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 = mb.get_model(cls.parf) cls.toas = pint.toa.get_TOAs( cls.timf, ephem="DE405", planets=False, include_bipm=False @@ -180,7 +193,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) @@ -188,6 +201,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.key != "-chanid": + continue + assert len( + [l for l in open(self.timf).readlines() if re.search(pm.key_value, l)] + ) == len(pm.select_toa_mask(self.toas)) + def test_derivative(self): log = logging.getLogger("Jump phase test") p = "JUMP2" @@ -205,8 +229,12 @@ def test_derivative(self): ) assert np.nanmax(relative_diff) < 0.001, msg -@pytest.mark.parametrize("tim, flag_ranges", [ -(""" + +@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 @@ -234,8 +262,11 @@ def test_derivative(self): 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)]), -(""" +""", + [(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 @@ -263,8 +294,11 @@ def test_derivative(self): 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)]), -]) +""", + [(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: @@ -273,8 +307,11 @@ def test_tim_file_gets_jump_flags(tim, flag_ranges): m = int(f.get("jump", -1)) assert (start < toas.table["tdbld"][i] < end) == (n == m) + def test_multiple_jumps_add(): - m = mb.get_model(StringIO(""" + m = mb.get_model( + StringIO( + """ PSR J1234+5678 ELAT 0 ELONG 0 @@ -283,27 +320,71 @@ def test_multiple_jumps_add(): F0 500 JUMP mjd 58000 60000 0 JUMP mjd 59000 60000 0 - """)) + """ + ) + ) for j in m.jumps: jmp = getattr(m, j) - if jmp.key == 'mjd': + if jmp.key == "mjd": start, end = jmp.key_value - if start < 58500: + if start.mjd < 58500: first_jump = jmp else: second_jump = jmp - toas = pint.toa.make_fake_toas(57000, 60000-1, 10, m) + toas = pint.toa.make_fake_toas(57000, 60000 - 1, 10, m) - first_jump.quantity = 100*u.us - second_jump.quantity = 0*u.us + 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 + 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 + 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, r_sum.resids, atol=1e-3*u.us) + assert_allclose(r_first.resids + r_second.resids, r_sum.resids, atol=1e-3 * u.us) + + +@pytest.mark.parametrize( + "j", + [ + pint.models.parameter.maskParameter( + name="JUMP", + key="-fish", + key_value="carp", + units=u.s, + value=7, + frozen=False, + uncertainty=0.1, + ), + pint.models.parameter.maskParameter( + name="JUMP", key="tel", key_value="ao", units=u.s, value=7, frozen=False, + ), + pint.models.parameter.maskParameter( + name="JUMP", + key="MJD", + key_value=( + astropy.time.Time(57000, format="mjd"), + astropy.time.Time(58000, format="mjd"), + ), + 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.key == j.key + assert nj.key_value == j.key_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) diff --git a/tests/test_mask_parameter.py b/tests/test_mask_parameter.py index 3c5f200c0..e68298b76 100644 --- a/tests/test_mask_parameter.py +++ b/tests/test_mask_parameter.py @@ -1,19 +1,19 @@ """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 astropy.time import Time +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 +24,13 @@ def toas(): def test_mjd_mask(toas): - mp = maskParameter("test1", key="mjd", key_value=[54000, 54100]) + mp = maskParameter( + "test1", + key="mjd", + key_value=[Time(54000, format="mjd"), Time(54100, format="mjd")], + ) assert mp.key == "mjd" - assert mp.key_value == [54000, 54100] + assert mp.key_value == (Time(54000, format="mjd"), Time(54100, format="mjd")) assert mp.value == None select_toas = mp.select_toa_mask(toas) assert len(select_toas) > 0 @@ -36,12 +40,28 @@ 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] + mp_str_keyval = maskParameter( + "test2", + key="mjd", + key_value=[Time("54000", format="mjd"), Time("54100", format="mjd")], + ) + assert mp_str_keyval.key_value == ( + Time(54000, format="mjd"), + Time(54100, format="mjd"), + ) + mp_value_switch = maskParameter( + "test1", + key="mjd", + key_value=[Time(54100, format="mjd"), Time(54000, format="mjd")], + ) + assert mp_value_switch.key_value == ( + Time(54000, format="mjd"), + Time(54100, format="mjd"), + ) with pytest.raises(ValueError): - mp_str_keyval = maskParameter("test2", key="mjd", key_value=["54000"]) + mp_str_keyval = maskParameter( + "test2", key="mjd", key_value=[Time("54000", format="mjd")] + ) def test_freq_mask(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_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..536b9183c 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,9 +12,9 @@ DownhillGLSFitter, DownhillWLSFitter, GLSFitter, - WLSFitter, WidebandDownhillFitter, WidebandTOAFitter, + WLSFitter, ) from pint.models.model_builder import get_model from pint.toa import get_TOAs diff --git a/tests/test_parameters.py b/tests/test_parameters.py index 7217b2b82..ef90cf12e 100644 --- a/tests/test_parameters.py +++ b/tests/test_parameters.py @@ -7,7 +7,11 @@ import astropy.units as u import numpy as np import pytest +<<<<<<< HEAD from numpy.testing import assert_allclose +======= +from astropy.time import Time +>>>>>>> Clean up maskParameter so jumps make more sense from pinttestdata import datadir import pint.fitter @@ -20,8 +24,11 @@ floatParameter, intParameter, maskParameter, +<<<<<<< HEAD pairParameter, prefixParameter, +======= +>>>>>>> Clean up maskParameter so jumps make more sense strParameter, ) from pint.toa import get_TOAs @@ -144,6 +151,61 @@ def setUpClass(cls): cls.m = get_model("B1855+09_NANOGrav_dfg+12_modified.par") cls.mp = get_model("prefixtest.par") +<<<<<<< HEAD +======= + 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.key, "mjd") + self.assertTrue(np.isclose(test_m.JUMP1.key_value[0].mjd, 52742.0, atol=1e-10)) + self.assertTrue(np.isclose(test_m.JUMP1.key_value[1].mjd, 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.key, "-testflag") + self.assertEqual(test_m.JUMP12.frozen, False) + self.assertEqual(test_m.JUMP12.key_value, "flagvalue") + self.assertTrue(np.isclose(test_m.JUMP12.value, 0.1)) + self.assertTrue(np.isclose(test_m.JUMP12.uncertainty_value, 2.0)) + self.assertTrue( + np.isclose(test_m.RAJ.uncertainty_value, 476.94611148516092061223) + ) + + self.assertTrue( + np.isclose( + test_m.DECJ.uncertainty_value, + 190996312986311097848351.00000000000000000000, + ) + ) + self.assertTrue(test_m.RAJ.uncertainty.unit, pint_units["hourangle_second"]) + self.assertTrue(test_m.RAJ.uncertainty.unit, u.arcsecond) + +>>>>>>> Clean up maskParameter so jumps make more sense def test_RAJ(self): """Check whether the value and units of RAJ parameter are ok""" units = u.hourangle @@ -540,3 +602,36 @@ def test_set_uncertainty_bogus_raises(p): ) def test_parameter_can_be_pickled(p): pickle.dumps(p) + +# Testing maskParameters: +# Their key and key_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", (Time(57000.0, format="mjd"), Time(58000.0, format="mjd"))), + ("mjd", [Time(57000.0, format="mjd"), Time(58000.0, format="mjd")]), + ("-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", (1, 2)), + ("-fish", ["c", "a", "r", "p"]), +] + + +@pytest.mark.parametrize("key, key_value", valid_settings) +def test_maskParameter_construction_valid(key, key_value): + j = maskParameter( + name="JUMP", + index=467, + key=key, + key_value=key_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_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..9c880f5f3 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.simulation import make_fake_toas_uniform +from pint.residuals import Residuals 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..5a167f181 100644 --- a/tests/test_random_models.py +++ b/tests/test_random_models.py @@ -1,19 +1,22 @@ 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, toa, utils +from pint.models import get_model, get_model_and_toas def test_random_models(): 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..2e2cf08a0 100644 --- a/tests/test_solar_wind.py +++ b/tests/test_solar_wind.py @@ -1,17 +1,18 @@ """ 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.simulation import make_fake_toas_uniform diff --git a/tests/test_stigma.py b/tests/test_stigma.py index 54157e0f3..686a46d6d 100644 --- a/tests/test_stigma.py +++ b/tests/test_stigma.py @@ -1,6 +1,7 @@ +import io import logging import os -import io +import tempfile import astropy.units as u import numpy as np @@ -11,9 +12,9 @@ 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 +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_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_reader.py b/tests/test_toa_reader.py index 9861ae40e..09211001a 100644 --- a/tests/test_toa_reader.py +++ b/tests/test_toa_reader.py @@ -1,21 +1,25 @@ 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 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..3e0104b21 100644 --- a/tests/test_toa_writer.py +++ b/tests/test_toa_writer.py @@ -1,7 +1,12 @@ -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.models import get_model from pint import toa, simulation 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..63c5dd797 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) 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): From c80b3cc2094db4345c1877c138b1126455bd4d72 Mon Sep 17 00:00:00 2001 From: Anne Archibald Date: Tue, 20 Jul 2021 16:04:13 +0100 Subject: [PATCH 05/17] Clean up handling of unset values --- src/pint/models/jump.py | 169 ++++++++++++------------------- src/pint/models/parameter.py | 20 +++- src/pint/models/timing_model.py | 109 ++++++++------------ src/pint/observatory/__init__.py | 4 + tests/test_jump.py | 13 ++- tests/test_mask_parameter.py | 62 +++++++----- tests/test_timing_model.py | 36 +++++-- tests/test_wideband_dm_data.py | 2 +- 8 files changed, 198 insertions(+), 217 deletions(-) diff --git a/src/pint/models/jump.py b/src/pint/models/jump.py index 2be79ad81..c93aed3d4 100644 --- a/src/pint/models/jump.py +++ b/src/pint/models/jump.py @@ -158,16 +158,15 @@ def __init__(self): def setup(self): """Set up support data structures to reflect parameters as set.""" super().setup() - self.jumps = [] - self.jump_dict = {} + self.jumps = {} for mask_par in self.get_params_of_type("maskParameter"): if mask_par.startswith("JUMP"): - self.jumps.append(mask_par) mp = getattr(self, mask_par) - self.jump_dict[mp.key, mp.key_value] = mp - for j in self.jumps: + self.jumps[mp.key, mp.key_value] = mp + for pm in self.jumps.values(): + 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) @@ -192,8 +191,7 @@ def jump_phase(self, toas, delay): """ 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) + for jump, jump_par in self.jumps.items(): mask = jump_par.select_toa_mask(toas) # NOTE: Currently parfile jump value has opposite sign with our # phase calculation. @@ -227,125 +225,86 @@ def d_phase_d_jump(self, toas, jump_param, delay): 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, jump_par in self.jumps.items(): 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 for all JUMPs.""" - jump_obs = [getattr(self, jump) for jump in self.jumps] + jump_obs = list(self.jump.values()) return jump_obs - def jump_params_to_flags(self, toas): - """Take jumps created from .par file and add appropriate flags to toa table. - - 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 function steps through all the defined JUMP parameters, and for each it - chooses all the TOAs affected by that JUMP and ensures that they have a flag - ``-jump`` whose argument contains the index of that JUMP. - - At the end of the process, every TOA that is affected by any JUMP has a - flag ``-jump`` whose argument is either the index of the jump - parameter, or a list containing the index of the jump parameter. This - flag cannot be used in a JUMP as these only select on exact matches, so - that ``JUMP -jump 17 0.1`` would fail to match a TOA with the flag - ``-jump [17, 18]``. - - 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): + def add_jump_and_flags(self, toa_table, key="gui_jump", key_value=None): """Add jump object to PhaseJump and appropriate flags to TOA tables. - 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. + 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 will add a parameter to the model corresponding to:: JUMP -gui_jump N 0 1 - where ``N`` is one more than the number of jumps that currently exist. This + 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 + toa_table: list The TOA table which must be modified. In pintk (pulsar.py), this will be all_toas.table["flags"][selected] + key: str + The name of the flag to use for the JUMP. + key_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 -<<<<<<< HEAD - 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." -======= - for dict in toa_table: - if "gui_jump" in dict.keys(): - log.error( - "The selected toa(s) overlap an existing jump. Remove all " - "interfering jumps before attempting to jump these toas." ->>>>>>> Document JUMP objects - ) - 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.values(): + if pm.key == "-" + key: + in_use.add(pm.key_value) + if key_value is None: + i = 1 + while True: + key_value = str(i) + if key_value not in in_use: + break + i += 1 + elif key_value in in_use: + raise ValueError(f"A JUMP -{key} {key_value} is already present.") + + used_indices = set() + for pm in self.jumps.values(): + used_indices.add(pm.index) + i = 1 + while i in used_indices: + i += 1 + + param = maskParameter( + name="JUMP", + index=i, + key="-" + key, + key_value=key_value, + value=0.0, + units="second", + frozen=False, + ) name = param.name + for d in toa_table: + if key 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_table: + d[key] = key_value return name diff --git a/src/pint/models/parameter.py b/src/pint/models/parameter.py index fe150c91d..fda78aaea 100644 --- a/src/pint/models/parameter.py +++ b/src/pint/models/parameter.py @@ -1543,6 +1543,8 @@ def validate(key, key_value): f"When selecting by {key} one must supply a " f"string not {key_value}" ) + if len(key_value.split()) != 1: + raise ValueError(f"Key value {repr(key_value)} cannot occur in TOAs.") return key_value @staticmethod @@ -1568,6 +1570,10 @@ def key(self): def key(self, key): if key is None: self._key = None + elif not isinstance(key, str): + raise ValueError( + f"maskParameters can only select on strings not {repr(key)}" + ) else: key = key.lower() if key not in self.allowed_non_flags and not key.startswith("-"): @@ -1780,10 +1786,16 @@ def select_toa_mask(self, toas): r = np.nonzero(c)[0] else: r = [] - if self.key is not None and self.key_value is not None: - for i, f in enumerate(toas.table["flags"]): - if f.get(self.key[1:], None) == self.key_value: - r.append(i) + if self.key is not None: + if self.key.startswith("-"): + key = self.key[1:] + else: + # only "name" is allowed here + key = self.key + if self.key_value is not None: + for i, f in enumerate(toas.table["flags"]): + if str(f.get(key, "")) == self.key_value: + r.append(i) r = np.array(r, dtype=int) return r diff --git a/src/pint/models/timing_model.py b/src/pint/models/timing_model.py index 94d15c7a6..6e9d04277 100644 --- a/src/pint/models/timing_model.py +++ b/src/pint/models/timing_model.py @@ -1342,76 +1342,51 @@ def jump_flags_to_params(self, toas, flag="jump"): """ from . import jump + new_jumps = [] # check if any TOAs are jumped - jumped = [flag in flag_dict for flag_dict in toas.table["flags"]] - if not any(jumped): + jumped = set( + flag_dict[flag] for flag_dict in toas.table["flags"] if flag in flag_dict + ) + 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") - # FIXME: numbers don't make sense here! - # take jumps in TOA table and add them as parameters to the model - existing_jumps = self.components["PhaseJump"].jump_dict - val = flag_dict[flag] - if (flag, val) not in existing_jumps: - param = maskParameter( - name="JUMP", - index=None, - key="-" + flag, - key_value=val, - value=0.0, - units="second", - uncertainty=0.0, - frozen=False, - ) - self.add_param_from_top(param, "PhaseJump") - 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.values(): + if pm.key == "-" + flag: + if pm.key_value in jumped: + jumped.remove(pm.key_value) + if not jumped: + log.info("All JUMPs appear to already be present.") + return new_jumps + used_indices = set() + for pm in self.jumps.values(): + 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, + key="-" + flag, + key_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. 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/tests/test_jump.py b/tests/test_jump.py index e1ec9489b..d466d27ea 100644 --- a/tests/test_jump.py +++ b/tests/test_jump.py @@ -47,7 +47,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.key == "-gui_jump" + assert jp2.key_value == "2" # check previous jump flags unaltered for d in setup_NGC6440E.t.table["flags"][selected_toa_ind]: assert d["gui_jump"] == "1" @@ -55,17 +58,17 @@ def test_add_jumps_and_flags(setup_NGC6440E): for d in setup_NGC6440E.t.table["flags"][selected_toa_ind2]: assert d["gui_jump"] == "2" - def test_add_overlapping_jump(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]) + 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" diff --git a/tests/test_mask_parameter.py b/tests/test_mask_parameter.py index e68298b76..0926b068d 100644 --- a/tests/test_mask_parameter.py +++ b/tests/test_mask_parameter.py @@ -65,43 +65,45 @@ def test_mjd_mask(toas): 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", key="freq", key_value=[1400 * u.MHz, 1430 * u.MHz]) + assert mp.key_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", key="freq", key_value=["1400", "2000"]) + mp_switch = maskParameter( + "test2", key="freq", key_value=[2000 * u.MHz, 1400 * u.MHz] + ) + assert mp_switch.key_value == (1400 * u.MHz, 2000 * u.MHz) mp_quantity = maskParameter( "test2", key="freq", key_value=[2000 * u.MHz, 1400 * u.MHz] ) - assert mp_quantity.key_value == [1400 * u.MHz, 2000 * u.MHz] + assert mp_quantity.key_value == (1400 * u.MHz, 2000 * u.MHz) with pytest.raises(ValueError): - mp_wrong_keyvalue = maskParameter("test2", key="freq", key_value=[1400]) + maskParameter("test2", key="freq", key_value=[1400 * u.MHz]) def test_tel_mask(toas): mp_ao = maskParameter("test2", key="tel", key_value="ao") - assert mp_ao.key_value == ["arecibo"] + assert mp_ao.key_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", key="tel", key_value="gbt") + assert mp_gbt.key_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"] + assert mp_special_obs1.key_value == "barycenter" + mp_special_obs2 = maskParameter("test2", key="tel", key_value="0") + assert mp_special_obs2.key_value == "geocenter" with pytest.raises(ValueError): - mp_wrong_keyvalue = maskParameter("test2", key="tel", key_value=["gbt", "ao"]) + maskParameter("test2", key="tel", key_value=["gbt", "ao"]) def test_name_mask(toas): @@ -109,30 +111,34 @@ def test_name_mask(toas): mp_name = maskParameter( "test2", key="name", key_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.key_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", key="name", key_value=["name1", "name2"]) def test_flag_mask(toas): - mp_flag = maskParameter("test2", key="-fe", key_value=430) - assert mp_flag.key_value == ["430"] + with pytest.raises(ValueError): + maskParameter("test2", key="-fe", key_value=430) mp_flag2 = maskParameter("test2", key="-fe", key_value="430") - assert mp_flag2.key_value == ["430"] + assert mp_flag2.key_value == "430" with pytest.raises(ValueError): - mp_wrong_key = maskParameter("test2", key="fe", key_value="430") + maskParameter("test2", key="fe", key_value="430") mp_flag3 = maskParameter("test2", key="-fe", key_value="L-wide") - assert mp_flag3.key_value == ["L-wide"] + assert mp_flag3.key_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_timing_model.py b/tests/test_timing_model.py index 04b0be131..f219c879c 100644 --- a/tests/test_timing_model.py +++ b/tests/test_timing_model.py @@ -8,6 +8,9 @@ import pytest from numpy.testing import assert_allclose from pinttestdata import datadir +from pint.simulation import make_fake_toas_uniform +from pint.toa import get_TOAs +from astropy.time import Time from pint.models import ( DEFAULT_ORDER, @@ -102,8 +105,22 @@ def test_add_component(self): 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.1, + "key": "mjd", + "key_value": [Time(55000, format="mjd"), Time(56000, format="mjd")], + }, + ), + ( + "JUMP", + { + "value": 0.2, + "key": "freq", + "key_value": [1440 * u.MHz, 2000 * u.MHz], + }, + ), ("JUMP", {"value": 0.3, "key": "tel", "key_value": "ao"}), ] @@ -139,9 +156,9 @@ def test_add_component(self): 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 jump1.key_value == (Time(55000, format="mjd"), Time(56000, format="mjd")) + assert jump2.key_value == (1440 * u.MHz, 2000 * u.MHz) + assert jump3.key_value == "arecibo" assert tm.jumps == ["JUMP1", "JUMP2", "JUMP3"] def test_remove_component(self): @@ -153,7 +170,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 +180,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,6 +350,7 @@ 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 +<<<<<<< HEAD assert "JUMP1" in m.components["PhaseJump"].jumps assert "JUMP2" in m.components["PhaseJump"].jumps @@ -349,3 +367,7 @@ def test_assumes_dmepoch_equals_pepoch(): t = make_fake_toas_uniform(57000, 59000, 10, m_assume) assert_allclose(m_assume.dm_value(t), m_given.dm_value(t)) +======= + assert ("-jump", "1") in m.components["PhaseJump"].jumps + assert ("-jump", "2") in m.components["PhaseJump"].jumps +>>>>>>> Clean up handling of unset values diff --git a/tests/test_wideband_dm_data.py b/tests/test_wideband_dm_data.py index 63c5dd797..20b398a80 100644 --- a/tests/test_wideband_dm_data.py +++ b/tests/test_wideband_dm_data.py @@ -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.key_value] = dmj for be in all_backends: assert all( dm_jump_value[self.toa_backends == be] == -dm_jump_map[be].quantity From 6d6145b46cd061f8a113cb69f7e28aecb6729c46 Mon Sep 17 00:00:00 2001 From: Anne Archibald Date: Tue, 20 Jul 2021 18:12:01 +0100 Subject: [PATCH 06/17] We support multiple JUMPs with the same criteria --- src/pint/models/jump.py | 33 +++++--- src/pint/models/parameter.py | 1 - src/pint/models/solar_wind_dispersion.py | 1 - src/pint/models/timing_model.py | 4 +- src/pint/models/troposphere_delay.py | 1 - src/pint/pintk/pulsar.py | 103 +++++++++-------------- tests/test_jump.py | 6 +- tests/test_timing_model.py | 11 +-- 8 files changed, 73 insertions(+), 87 deletions(-) diff --git a/src/pint/models/jump.py b/src/pint/models/jump.py index c93aed3d4..862d68809 100644 --- a/src/pint/models/jump.py +++ b/src/pint/models/jump.py @@ -99,7 +99,15 @@ class PhaseJump(PhaseComponent): >>> toa_index_list = [1,3,5] >>> for i in toa_index_list: ... toas.table['flags'][i]['fish'] = 'carp' - >>> model + >>> np = m.JUMP1.new_param(100) + >>> np.key = '-fish' + >>> np.key_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], key='-fish', key_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 @@ -155,15 +163,18 @@ def __init__(self): ) self.phase_funcs_component += [self.jump_phase] + @property + def jumps(self): + r = [] + for mask_par in self.get_params_of_type("maskParameter"): + if mask_par.startswith("JUMP"): + r.append(getattr(self, mask_par)) + return r + def setup(self): """Set up support data structures to reflect parameters as set.""" super().setup() - self.jumps = {} - for mask_par in self.get_params_of_type("maskParameter"): - if mask_par.startswith("JUMP"): - mp = getattr(self, mask_par) - self.jumps[mp.key, mp.key_value] = mp - for pm in self.jumps.values(): + for pm in self.jumps: j = pm.name # prevents duplicates from being added to phase_deriv_funcs if j in self.deriv_funcs: @@ -191,7 +202,7 @@ def jump_phase(self, toas, delay): """ tbl = toas.table jphase = numpy.zeros(len(tbl)) * (self.JUMP1.units * self._parent.F0.units) - for jump, jump_par in self.jumps.items(): + for jump_par in self.jumps: mask = jump_par.select_toa_mask(toas) # NOTE: Currently parfile jump value has opposite sign with our # phase calculation. @@ -225,7 +236,7 @@ def d_phase_d_jump(self, toas, jump_param, delay): def print_par(self): """Return a string representation of all JUMP parameters appropriate for a par file.""" result = "" - for jump, jump_par in self.jumps.items(): + for jump_par in self.jumps: result += jump_par.as_parfile_line() return result @@ -266,7 +277,7 @@ def add_jump_and_flags(self, toa_table, key="gui_jump", key_value=None): The name of the new JUMP parameter. """ in_use = set() - for pm in self.jumps.values(): + for pm in self.jumps: if pm.key == "-" + key: in_use.add(pm.key_value) if key_value is None: @@ -280,7 +291,7 @@ def add_jump_and_flags(self, toa_table, key="gui_jump", key_value=None): raise ValueError(f"A JUMP -{key} {key_value} is already present.") used_indices = set() - for pm in self.jumps.values(): + for pm in self.jumps: used_indices.add(pm.index) i = 1 while i in used_indices: diff --git a/src/pint/models/parameter.py b/src/pint/models/parameter.py index fda78aaea..05ea099c5 100644 --- a/src/pint/models/parameter.py +++ b/src/pint/models/parameter.py @@ -42,7 +42,6 @@ time_to_longdouble, time_to_mjd_string, ) -from pint.toa_select import TOASelect from pint.utils import split_prefixed_name log = logging.getLogger(__name__) 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 6e9d04277..48b094d40 100644 --- a/src/pint/models/timing_model.py +++ b/src/pint/models/timing_model.py @@ -1356,7 +1356,7 @@ def jump_flags_to_params(self, toas, flag="jump"): self.add_component(a, setup=False) self.remove_param("JUMP1") a.setup() - for pm in self.jumps.values(): + for pm in self.jumps: if pm.key == "-" + flag: if pm.key_value in jumped: jumped.remove(pm.key_value) @@ -1364,7 +1364,7 @@ def jump_flags_to_params(self, toas, flag="jump"): log.info("All JUMPs appear to already be present.") return new_jumps used_indices = set() - for pm in self.jumps.values(): + for pm in self.jumps: used_indices.add(pm.index) next_free_index = 1 while next_free_index in used_indices: 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/pintk/pulsar.py b/src/pint/pintk/pulsar.py index fdaf9a38a..825874e7a 100644 --- a/src/pint/pintk/pulsar.py +++ b/src/pint/pintk/pulsar.py @@ -88,11 +88,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()) @@ -284,67 +280,48 @@ 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( - 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. " - ) - 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 - ) - 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 + + gui_jump_flags = set() + for f in self.all_toas.table["flags"][selected]: + gui_jump_flags.add(f.get("gui_jump", None)) + if None in gui_jump_flags: + if len(gui_jump_flags) > 1: + raise ValueError("Some TOAs are already jumped") + # No jumps, add some! + self.prefit_model.add_jump_and_flags(self.all_toas.table["flags"][selected]) + else: + if len(gui_jump_flags) > 1: + raise ValueError("TOAs are from different GUI jumps") + (the_value,) = gui_jump_flags + # All jumped, remove them. + 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 + if len(param_to_zap.select_toa_mask(self.all_toas)) == 0: + # Parameter no longer selects any TOAs + self.prefit_model.remove_param(param_to_zap.name) def fit(self, selected, iters=1): """ diff --git a/tests/test_jump.py b/tests/test_jump.py index d466d27ea..8700a7663 100644 --- a/tests/test_jump.py +++ b/tests/test_jump.py @@ -326,8 +326,7 @@ def test_multiple_jumps_add(): """ ) ) - for j in m.jumps: - jmp = getattr(m, j) + for jmp in m.jumps: if jmp.key == "mjd": start, end = jmp.key_value if start.mjd < 58500: @@ -348,7 +347,8 @@ def test_multiple_jumps_add(): second_jump.quantity = 75 * u.us r_sum = pint.residuals.Residuals(toas, m) - assert_allclose(r_first.resids + r_second.resids, r_sum.resids, atol=1e-3 * u.us) + 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( diff --git a/tests/test_timing_model.py b/tests/test_timing_model.py index f219c879c..7913d27b4 100644 --- a/tests/test_timing_model.py +++ b/tests/test_timing_model.py @@ -350,7 +350,6 @@ 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 -<<<<<<< HEAD assert "JUMP1" in m.components["PhaseJump"].jumps assert "JUMP2" in m.components["PhaseJump"].jumps @@ -367,7 +366,9 @@ def test_assumes_dmepoch_equals_pepoch(): t = make_fake_toas_uniform(57000, 59000, 10, m_assume) assert_allclose(m_assume.dm_value(t), m_given.dm_value(t)) -======= - assert ("-jump", "1") in m.components["PhaseJump"].jumps - assert ("-jump", "2") in m.components["PhaseJump"].jumps ->>>>>>> Clean up handling of unset values + assert ("-jump", "1") in [ + (j.key, j.key_value) for j in m.components["PhaseJump"].jumps + ] + assert ("-jump", "2") in [ + (j.key, j.key_value) for j in m.components["PhaseJump"].jumps + ] From 19e246bf6b1f8f075f6342b9ca11cec7bd94c6cc Mon Sep 17 00:00:00 2001 From: Anne Archibald Date: Tue, 20 Jul 2021 22:21:56 +0100 Subject: [PATCH 07/17] Attempt to un-break pintk --- src/pint/models/jump.py | 68 ++++++++++++++++++++---- src/pint/pintk/plk.py | 109 ++++++++++++++++++++------------------- src/pint/pintk/pulsar.py | 31 ++++++++--- 3 files changed, 137 insertions(+), 71 deletions(-) diff --git a/src/pint/models/jump.py b/src/pint/models/jump.py index 862d68809..af55a3bd5 100644 --- a/src/pint/models/jump.py +++ b/src/pint/models/jump.py @@ -2,7 +2,7 @@ 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 @@ -54,7 +54,7 @@ def jump_delay(self, toas, acc_delay=None): in the unit of seconds. """ tbl = toas.table - jdelay = numpy.zeros(len(tbl)) + jdelay = np.zeros(len(tbl)) for jump in self.jumps: jump_par = getattr(self, jump) mask = jump_par.select_toa_mask(toas) @@ -65,7 +65,7 @@ def jump_delay(self, toas, acc_delay=None): def d_delay_d_jump(self, toas, jump_param, acc_delay=None): tbl = toas.table - d_delay_d_j = numpy.zeros(len(tbl)) + d_delay_d_j = np.zeros(len(tbl)) jpar = getattr(self, jump_param) mask = jpar.select_toa_mask(toas) d_delay_d_j[mask] = -1.0 @@ -201,7 +201,7 @@ def jump_phase(self, toas, delay): The phase shift for each TOA. """ tbl = toas.table - jphase = numpy.zeros(len(tbl)) * (self.JUMP1.units * self._parent.F0.units) + jphase = np.zeros(len(tbl)) * (self.JUMP1.units * self._parent.F0.units) for jump_par in self.jumps: mask = jump_par.select_toa_mask(toas) # NOTE: Currently parfile jump value has opposite sign with our @@ -228,7 +228,7 @@ def d_phase_d_jump(self, toas, jump_param, delay): """ 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) @@ -245,7 +245,7 @@ def get_jump_param_objects(self): jump_obs = list(self.jump.values()) return jump_obs - def add_jump_and_flags(self, toa_table, key="gui_jump", key_value=None): + def add_jump_and_flags(self, toa_flags, key="gui_jump", key_value=None): """Add jump object to PhaseJump and appropriate flags to TOA tables. Given a subset of TOAs (specified by a reference to their flags objects), @@ -262,8 +262,8 @@ def add_jump_and_flags(self, toa_table, key="gui_jump", key_value=None): Parameters ---------- - toa_table: list - 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] key: str The name of the flag to use for the JUMP. @@ -307,7 +307,7 @@ def add_jump_and_flags(self, toa_table, key="gui_jump", key_value=None): frozen=False, ) name = param.name - for d in toa_table: + for d in toa_flags: if key in d: raise ValueError( "The selected toa(s) overlap an existing jump. Remove all " @@ -316,6 +316,54 @@ def add_jump_and_flags(self, toa_table, key="gui_jump", key_value=None): self.add_param(param) self.setup() # add appropriate flags to TOA table to link jump with appropriate TOA - for d in toa_table: + for d in toa_flags: d[key] = key_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 n in self.free_params: + if not n.startswith("JUMP"): + continue + c = np.zeros(len(toas), dtype=bool) + pm = getattr(self, n) + if not np.any(c): + pm.frozen = True + else: + c[pm.select_toa_mask(toas)] = True + masks[n] = 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: + 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/pintk/plk.py b/src/pint/pintk/plk.py index 59cd16eef..461b5d6c3 100644 --- a/src/pint/pintk/plk.py +++ b/src/pint/pintk/plk.py @@ -1201,64 +1201,65 @@ 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 + # 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." + ) + 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 + 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 + self.jumped = self.jumped[~toas_to_delete] + print(self.jumped) + 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): """ diff --git a/src/pint/pintk/pulsar.py b/src/pint/pintk/pulsar.py index 825874e7a..872b76b79 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: @@ -299,19 +297,29 @@ def add_jump(self, selected): Which TOAs to apply to. """ + if "PhaseJump" not in self.prefit_model.components: + log.info("PhaseJump component added") + a = pint.models.jump.PhaseJump() + a.setup() + self.prefit_model.add_component(a) + + 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! - self.prefit_model.add_jump_and_flags(self.all_toas.table["flags"][selected]) + log.info("Adding a GUI jump for these TOAs") + new_name = self.prefit_model.add_jump_and_flags(self.all_toas.table["flags"][selected]) + log.info(f"Added new GUI jump parameter {getattr(self.prefit_model, new_name)}") else: if len(gui_jump_flags) > 1: raise ValueError("TOAs are from different GUI jumps") (the_value,) = gui_jump_flags - # All jumped, remove them. + 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 @@ -319,18 +327,27 @@ def add_jump(self, selected): 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 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 From 09bae6090ed55098bdc1dd16ec26eb7e5a7ef8ed Mon Sep 17 00:00:00 2001 From: Anne Archibald Date: Wed, 21 Jul 2021 18:47:02 +0100 Subject: [PATCH 08/17] Simplify pintk JUMP handling Highlight JUMPed TOAs (if JUMP unfrozen). Document pintk module (include pointers to plk and pulsar). Label JUMPs and test new code --- src/pint/models/jump.py | 23 ++-- src/pint/models/parameter.py | 8 -- src/pint/pintk/__init__.py | 5 + src/pint/pintk/colormodes.py | 8 +- src/pint/pintk/plk.py | 196 +++++++++-------------------------- src/pint/pintk/pulsar.py | 16 ++- tests/test_jump.py | 86 +++++++++++++-- 7 files changed, 159 insertions(+), 183 deletions(-) diff --git a/src/pint/models/jump.py b/src/pint/models/jump.py index af55a3bd5..11d664349 100644 --- a/src/pint/models/jump.py +++ b/src/pint/models/jump.py @@ -203,6 +203,8 @@ def jump_phase(self, toas, delay): tbl = toas.table 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. @@ -240,11 +242,6 @@ def print_par(self): result += jump_par.as_parfile_line() return result - def get_jump_param_objects(self): - """Returns a list of the maskParameter objects for all JUMPs.""" - jump_obs = list(self.jump.values()) - return jump_obs - def add_jump_and_flags(self, toa_flags, key="gui_jump", key_value=None): """Add jump object to PhaseJump and appropriate flags to TOA tables. @@ -336,22 +333,22 @@ def tidy_jumps_for_fit(self, toas): The TOAs that this model is to be used with. """ masks = {} - for n in self.free_params: - if not n.startswith("JUMP"): + for pm in self.jumps: + if pm.frozen: continue c = np.zeros(len(toas), dtype=bool) - pm = getattr(self, n) + 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: - c[pm.select_toa_mask(toas)] = True - masks[n] = pm, c + 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: + for n, (pm, c) in masks.items(): affected |= c if c.sum() > most_count: most_count = c.sum() @@ -363,7 +360,3 @@ def tidy_jumps_for_fit(self, toas): del masks[most_n] else: break - - - - diff --git a/src/pint/models/parameter.py b/src/pint/models/parameter.py index 05ea099c5..2da21eed1 100644 --- a/src/pint/models/parameter.py +++ b/src/pint/models/parameter.py @@ -1464,14 +1464,6 @@ 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: astropy.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), - } self.key = key self.key_value = key_value 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..eca543424 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.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 461b5d6c3..4709ee868 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) @@ -659,7 +635,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 +644,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 +659,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 +687,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 +706,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 +722,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 +858,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...") @@ -937,6 +899,18 @@ def determine_yaxis_units(self, miny, maxy): miny = miny.to(u.us) return miny, maxy + @property + def jumped(self): + jumped = np.zeros(len(self.psr.all_toas), dtype=bool) + # FIXME: prefit or postfit? + model = self.psr.prefit_model + if hasattr(model, "jumps"): + for pm in model.jumps: + if not pm.frozen: + jumped[pm.select_toa_mask(self.psr.all_toas)] = True + log.info(f"Model jumps {jumped.sum()} of {len(jumped)} TOAs.") + return jumped + def print_info(self): """ Write information about the current selection, or all points @@ -973,8 +947,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 +962,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 +977,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 +1002,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 +1016,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] @@ -1140,25 +1116,6 @@ def check_jump_invalid(self): ) 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 @@ -1206,27 +1163,11 @@ def stationaryClick(self, event): # 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." - ) - 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 + # 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") @@ -1242,8 +1183,7 @@ def stationaryClick(self, event): "obs" ) # update jumps and rest of graph - self.jumped = self.jumped[~toas_to_delete] - print(self.jumped) + # 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) @@ -1288,7 +1228,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"): @@ -1326,22 +1267,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" ) @@ -1353,7 +1280,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) @@ -1361,39 +1287,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 @@ -1407,7 +1310,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 872b76b79..c2044bf35 100644 --- a/src/pint/pintk/pulsar.py +++ b/src/pint/pintk/pulsar.py @@ -313,8 +313,12 @@ def add_jump(self, selected): 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]) - log.info(f"Added new GUI jump parameter {getattr(self.prefit_model, new_name)}") + new_name = self.prefit_model.add_jump_and_flags( + self.all_toas.table["flags"][selected] + ) + log.info( + f"Added new GUI jump parameter {getattr(self.prefit_model, new_name)}" + ) else: if len(gui_jump_flags) > 1: raise ValueError("TOAs are from different GUI jumps") @@ -328,10 +332,14 @@ def add_jump(self, selected): param_to_zap = pm break else: - raise ValueError(f"Unable to find JUMP parameter for -gui_jump {the_value}") + raise ValueError( + f"Unable to find JUMP parameter for -gui_jump {the_value}" + ) 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}") + 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): diff --git a/tests/test_jump.py b/tests/test_jump.py index 8700a7663..2818fb515 100644 --- a/tests/test_jump.py +++ b/tests/test_jump.py @@ -12,10 +12,9 @@ from numpy.testing import assert_allclose from pinttestdata import datadir -import pint.models.model_builder as mb import pint.models.parameter import pint.toa -from pint.models import PhaseJump, parameter as p +from pint.models import PhaseJump, get_model, parameter as p from pint.residuals import Residuals @@ -23,7 +22,7 @@ class SimpleSetup: def __init__(self, par, tim): self.par = par self.tim = tim - self.m = mb.get_model(self.par) + self.m = get_model(self.par) self.t = pint.toa.get_TOAs( self.tim, ephem="DE405", planets=False, include_bipm=False ) @@ -187,7 +186,7 @@ class TestJUMP(unittest.TestCase): def setUpClass(cls): 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 = mb.get_model(cls.parf) + cls.JUMPm = get_model(cls.parf) cls.toas = pint.toa.get_TOAs( cls.timf, ephem="DE405", planets=False, include_bipm=False ) @@ -312,7 +311,7 @@ def test_tim_file_gets_jump_flags(tim, flag_ranges): def test_multiple_jumps_add(): - m = mb.get_model( + m = get_model( StringIO( """ PSR J1234+5678 @@ -347,8 +346,11 @@ def test_multiple_jumps_add(): 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) + 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( @@ -391,3 +393,73 @@ def test_jump_parfile_roundtrip(j): 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 = pint.toa.make_fake_toas(58000, 60000, 20, m) + + class R: + pass + + r = R() + r.m = m + r.t = t + for j in m.jumps: + if j.key == "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.key_value = ( + astropy.time.Time(56000, format="mjd"), + astropy.time.Time(70000, format="mjd"), + ) + 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.key_value = ( + astropy.time.Time(50000, format="mjd"), + astropy.time.Time(55000, format="mjd"), + ) + 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.key_value = ( + astropy.time.Time(50000, format="mjd"), + astropy.time.Time(59000, format="mjd"), + ) + small.m.components["PhaseJump"].add_param(j2) + small.m.tidy_jumps_for_fit(small.t) + assert small.j.frozen != j2.frozen From f6eac8ebbf4b9d6fc69e234823f67a4fd34d3cef Mon Sep 17 00:00:00 2001 From: Anne Archibald Date: Thu, 22 Jul 2021 15:33:18 +0100 Subject: [PATCH 09/17] Remove DelayJump. --- src/pint/models/__init__.py | 2 +- src/pint/models/jump.py | 68 +----------------------------------- src/pint/pintk/colormodes.py | 2 +- src/pint/pintk/plk.py | 35 ------------------- src/pint/pintk/pulsar.py | 18 ++++++++++ tests/test_timing_model.py | 5 ++- 6 files changed, 23 insertions(+), 107 deletions(-) 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 11d664349..3dfbb04e7 100644 --- a/src/pint/models/jump.py +++ b/src/pint/models/jump.py @@ -12,73 +12,6 @@ __all__ = ["PhaseJump"] -class DelayJump(DelayComponent): - """Delay jumps - - This is a different kind of JUMP, conceptually affecting the TOAs directly, - so that a 60s jump would advance the pulsar around its orbit by 60 extra - seconds. The conventional JUMP affects only the observed phase, as you - would expect if they are due to a redefinition of phase zero in a template. - - 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 = np.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 = np.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 - - class PhaseJump(PhaseComponent): """Arbitrary jumps in pulse phase. @@ -165,6 +98,7 @@ def __init__(self): @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"): diff --git a/src/pint/pintk/colormodes.py b/src/pint/pintk/colormodes.py index eca543424..5d06d0b5a 100644 --- a/src/pint/pintk/colormodes.py +++ b/src/pint/pintk/colormodes.py @@ -40,7 +40,7 @@ def plotColorMode(self): """ Plots application's residuals in proper color scheme. """ - jumped = self.application.jumped + jumped = self.application.psr.jumped if self.application.yerrs is None: self.application.plkAxes.scatter( diff --git a/src/pint/pintk/plk.py b/src/pint/pintk/plk.py index 4709ee868..caa13ac84 100644 --- a/src/pint/pintk/plk.py +++ b/src/pint/pintk/plk.py @@ -623,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) @@ -899,18 +896,6 @@ def determine_yaxis_units(self, miny, maxy): miny = miny.to(u.us) return miny, maxy - @property - def jumped(self): - jumped = np.zeros(len(self.psr.all_toas), dtype=bool) - # FIXME: prefit or postfit? - model = self.psr.prefit_model - if hasattr(model, "jumps"): - for pm in model.jumps: - if not pm.frozen: - jumped[pm.select_toa_mask(self.psr.all_toas)] = True - log.info(f"Model jumps {jumped.sum()} of {len(jumped)} TOAs.") - return jumped - def print_info(self): """ Write information about the current selection, or all points @@ -1096,26 +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 canvasClickEvent(self, event): """ Call this function when the figure/canvas is clicked diff --git a/src/pint/pintk/pulsar.py b/src/pint/pintk/pulsar.py index c2044bf35..c5fb0dbcb 100644 --- a/src/pint/pintk/pulsar.py +++ b/src/pint/pintk/pulsar.py @@ -106,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: diff --git a/tests/test_timing_model.py b/tests/test_timing_model.py index 7913d27b4..6d7105c6a 100644 --- a/tests/test_timing_model.py +++ b/tests/test_timing_model.py @@ -16,7 +16,6 @@ DEFAULT_ORDER, AstrometryEquatorial, BinaryELL1, - DelayJump, Spindown, TimingModel, Wave, @@ -91,10 +90,10 @@ 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 From 368d056f5526788767d67d4e65c8c02cd240fea2 Mon Sep 17 00:00:00 2001 From: Anne Archibald Date: Thu, 22 Jul 2021 16:52:03 +0100 Subject: [PATCH 10/17] Clean up PhaseJump --- tests/test_timing_model.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/test_timing_model.py b/tests/test_timing_model.py index 6d7105c6a..5da0fdd02 100644 --- a/tests/test_timing_model.py +++ b/tests/test_timing_model.py @@ -18,6 +18,7 @@ BinaryELL1, Spindown, TimingModel, + PhaseJump, Wave, get_model, parameter as p, @@ -97,8 +98,9 @@ def test_add_component(self): 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")) @@ -158,7 +160,7 @@ def test_add_component(self): assert jump1.key_value == (Time(55000, format="mjd"), Time(56000, format="mjd")) assert jump2.key_value == (1440 * u.MHz, 2000 * u.MHz) assert jump3.key_value == "arecibo" - assert tm.jumps == ["JUMP1", "JUMP2", "JUMP3"] + assert len(tm.jumps) == 3 def test_remove_component(self): tm = TimingModel( From bcd59e653a79851733edf003bc8086ca9954f884 Mon Sep 17 00:00:00 2001 From: Anne Archibald Date: Wed, 28 Jul 2021 13:24:53 +0100 Subject: [PATCH 11/17] Update tests for new JUMP criteria --- tests/datafile/test.tim | 21 +++++++++++++++++++++ tests/test_flagging_clustering.py | 5 +++-- 2 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 tests/datafile/test.tim diff --git a/tests/datafile/test.tim b/tests/datafile/test.tim new file mode 100644 index 000000000..3b785c6ff --- /dev/null +++ b/tests/datafile/test.tim @@ -0,0 +1,21 @@ +FORMAT 1 +C Created: 2021-07-28T12:48:38.549524 +C PINT_version: 0.8.2+518.g8045953d.dirty +C User: Anne Archibald (naa280) +C Host: halley +C OS: Linux-5.4.0-77-generic-x86_64-with-glibc2.27 +unk 999999.000000 54999.9999999190350347 1.000 arecibo -pn 35081.0 +unk 999999.000000 55250.0000000421224653 1.000 arecibo -pn 1328078308.0 +unk 999999.000000 55499.9999999678046876 1.000 arecibo -pn 2656157102.0 +unk 999999.000000 55750.0000000257422338 1.000 arecibo -pn 3984292587.0 +unk 999999.000000 56000.0000000452955787 1.000 arecibo -pn 5312347290.0 +unk 999999.000000 54999.9999999566489615 1.000 bat -pn 0.0 +unk 999999.000000 55250.0000000440191276 1.000 bat -pn 1328086291.0 +unk 999999.000000 55500.0000000468705689 1.000 bat -pn 2656172581.0 +unk 999999.000000 55749.9999999652032852 1.000 bat -pn 3984258870.0 +unk 999999.000000 55999.9999999872580569 1.000 bat -pn 5312345159.0 +unk 999999.000000 55000.0000000064168981 1.000 gbt -pn 35081.0 +unk 999999.000000 55250.0000000315666088 1.000 gbt -pn 1328078308.0 +unk 999999.000000 55499.9999999818982986 1.000 gbt -pn 2656157102.0 +unk 999999.000000 55749.9999999297936112 1.000 gbt -pn 3984292586.0 +unk 999999.000000 56000.0000000503245833 1.000 gbt -pn 5312347290.0 diff --git a/tests/test_flagging_clustering.py b/tests/test_flagging_clustering.py index ae11c54a3..814b53a71 100644 --- a/tests/test_flagging_clustering.py +++ b/tests/test_flagging_clustering.py @@ -7,6 +7,7 @@ import astropy.units as u import numpy as np +from astropy.time import Time import pint.models.model_builder as mb import pint.toa as toa @@ -42,7 +43,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", key="mjd", value=0.2, key_value=[Time(54099*u.d, format="mjd"), Time(54100*u.d, format="mjd")], units=u.s ) # this should be the last group of any TOAs cp.add_param(par, setup=True) @@ -55,7 +56,7 @@ 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", key="-toacluster", value=0.2, key_value="41", units=u.s ) # this should be identical to the cluster above cp_copy.add_param(par_copy, setup=True) From acfba76a1950e1bad94a25850fe5a61187a9d0dc Mon Sep 17 00:00:00 2001 From: Anne Archibald Date: Wed, 28 Jul 2021 22:37:41 +0100 Subject: [PATCH 12/17] Use floats for MJD ranges --- src/pint/models/parameter.py | 26 +++++++++++------------ tests/datafile/test.tim | 4 ++-- tests/test_flagging_clustering.py | 3 +-- tests/test_jump.py | 20 +++++++----------- tests/test_mask_parameter.py | 35 +++++++------------------------ tests/test_parameters.py | 24 ++++++++++++++++----- tests/test_timing_model.py | 11 ++-------- 7 files changed, 53 insertions(+), 70 deletions(-) diff --git a/src/pint/models/parameter.py b/src/pint/models/parameter.py index 2da21eed1..66675c129 100644 --- a/src/pint/models/parameter.py +++ b/src/pint/models/parameter.py @@ -1507,17 +1507,17 @@ def validate(key, key_value): """ if key is None: if key_value is not None: - raise ValueError(f"Cannot associate a value with no flag") + raise ValueError("Cannot associate a value with no flag") if key == "mjd": start, end = sorted(key_value) - if not isinstance(start, astropy.time.Time) or not isinstance( - end, astropy.time.Time + 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 Times not {key_value}" + f"pair of numbers not {key_value}" ) - return start, end + return float(start), float(end) elif key == "freq": low, high = sorted(key_value) if not isinstance(low, u.Quantity) or not isinstance(high, u.Quantity): @@ -1534,18 +1534,19 @@ def validate(key, key_value): f"When selecting by {key} one must supply a " f"string not {key_value}" ) + # FlagDict.validate() once #1074 is merged. if len(key_value.split()) != 1: raise ValueError(f"Key value {repr(key_value)} cannot occur in TOAs.") return key_value @staticmethod def convert(key, key_value): - """Convert strings to the appropriate form for the key.""" + """Convert key_value strings to the appropriate form for the key.""" if key == "mjd": start, end = key_value return ( - astropy.time.Time(start, format="mjd"), - astropy.time.Time(end, format="mjd"), + float(start), + float(end), ) elif key == "freq": start, end = key_value @@ -1707,8 +1708,8 @@ def as_parfile_line(self): line += self.key_value else: start, end = self.key_value - if isinstance(start, astropy.time.Time): - line += f"{time_to_mjd_string(start)} {time_to_mjd_string(end)} " + 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: @@ -1761,11 +1762,10 @@ def select_toa_mask(self, toas): array An array of TOA indices selected by the mask. """ - column_match = {"mjd": "mjd_float", "freq": "freq", "tel": "obs"} if self.key == "mjd": start, end = self.key_value - c = toas.table["mjd_float"] >= start.mjd - c &= toas.table["mjd_float"] <= end.mjd + c = toas.table["mjd_float"] >= start + c &= toas.table["mjd_float"] <= end r = np.nonzero(c)[0] elif self.key == "freq": low, high = self.key_value diff --git a/tests/datafile/test.tim b/tests/datafile/test.tim index 3b785c6ff..79c4b43e9 100644 --- a/tests/datafile/test.tim +++ b/tests/datafile/test.tim @@ -1,6 +1,6 @@ FORMAT 1 -C Created: 2021-07-28T12:48:38.549524 -C PINT_version: 0.8.2+518.g8045953d.dirty +C Created: 2021-07-28T22:32:26.227045 +C PINT_version: 0.8.2+519.g653e6785.dirty C User: Anne Archibald (naa280) C Host: halley C OS: Linux-5.4.0-77-generic-x86_64-with-glibc2.27 diff --git a/tests/test_flagging_clustering.py b/tests/test_flagging_clustering.py index 814b53a71..988c94e7b 100644 --- a/tests/test_flagging_clustering.py +++ b/tests/test_flagging_clustering.py @@ -7,7 +7,6 @@ import astropy.units as u import numpy as np -from astropy.time import Time import pint.models.model_builder as mb import pint.toa as toa @@ -43,7 +42,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=[Time(54099*u.d, format="mjd"), Time(54100*u.d, format="mjd")], units=u.s + name="JUMP", key="mjd", value=0.2, key_value=[54099, 54100], units=u.s ) # this should be the last group of any TOAs cp.add_param(par, setup=True) diff --git a/tests/test_jump.py b/tests/test_jump.py index 2818fb515..cac5e0300 100644 --- a/tests/test_jump.py +++ b/tests/test_jump.py @@ -5,7 +5,6 @@ import unittest from io import StringIO -import astropy.time import astropy.units as u import numpy as np import pytest @@ -328,7 +327,7 @@ def test_multiple_jumps_add(): for jmp in m.jumps: if jmp.key == "mjd": start, end = jmp.key_value - if start.mjd < 58500: + if start < 58500: first_jump = jmp else: second_jump = jmp @@ -371,10 +370,7 @@ def test_multiple_jumps_add(): pint.models.parameter.maskParameter( name="JUMP", key="MJD", - key_value=( - astropy.time.Time(57000, format="mjd"), - astropy.time.Time(58000, format="mjd"), - ), + key_value=(57000, 58000,), units=u.s, value=7, frozen=False, @@ -433,8 +429,8 @@ def test_tidy_jumps_all_ok(small): def test_tidy_jumps_all_jumped(small): small.j.frozen = False small.j.key_value = ( - astropy.time.Time(56000, format="mjd"), - astropy.time.Time(70000, format="mjd"), + 56000, + 70000, ) small.m.tidy_jumps_for_fit(small.t) assert small.j.frozen @@ -444,8 +440,8 @@ def test_tidy_jumps_irrelevant(small): small.j.frozen = False j2 = small.j.new_param(index=100, copy_all=True) j2.key_value = ( - astropy.time.Time(50000, format="mjd"), - astropy.time.Time(55000, format="mjd"), + 50000, + 55000, ) small.m.components["PhaseJump"].add_param(j2) small.m.tidy_jumps_for_fit(small.t) @@ -457,8 +453,8 @@ def test_tidy_jumps_cover_all_freeze_one(small): small.j.frozen = False j2 = small.j.new_param(index=100, copy_all=True) j2.key_value = ( - astropy.time.Time(50000, format="mjd"), - astropy.time.Time(59000, format="mjd"), + 50000, + 59000, ) small.m.components["PhaseJump"].add_param(j2) small.m.tidy_jumps_for_fit(small.t) diff --git a/tests/test_mask_parameter.py b/tests/test_mask_parameter.py index 0926b068d..b145d1859 100644 --- a/tests/test_mask_parameter.py +++ b/tests/test_mask_parameter.py @@ -8,7 +8,6 @@ import astropy.units as u import numpy as np import pytest -from astropy.time import Time from pinttestdata import datadir from pint.models.model_builder import get_model @@ -24,13 +23,9 @@ def toas(): def test_mjd_mask(toas): - mp = maskParameter( - "test1", - key="mjd", - key_value=[Time(54000, format="mjd"), Time(54100, format="mjd")], - ) + mp = maskParameter("test1", key="mjd", key_value=[54000, 54100],) assert mp.key == "mjd" - assert mp.key_value == (Time(54000, format="mjd"), Time(54100, format="mjd")) + assert mp.key_value == (54000, 54100) assert mp.value == None select_toas = mp.select_toa_mask(toas) assert len(select_toas) > 0 @@ -40,28 +35,14 @@ 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=[Time("54000", format="mjd"), Time("54100", format="mjd")], - ) - assert mp_str_keyval.key_value == ( - Time(54000, format="mjd"), - Time(54100, format="mjd"), - ) - mp_value_switch = maskParameter( - "test1", - key="mjd", - key_value=[Time(54100, format="mjd"), Time(54000, format="mjd")], - ) - assert mp_value_switch.key_value == ( - Time(54000, format="mjd"), - Time(54100, format="mjd"), - ) with pytest.raises(ValueError): - mp_str_keyval = maskParameter( - "test2", key="mjd", key_value=[Time("54000", format="mjd")] + maskParameter( + "test2", key="mjd", 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]) def test_freq_mask(toas): diff --git a/tests/test_parameters.py b/tests/test_parameters.py index ef90cf12e..6f30dbe68 100644 --- a/tests/test_parameters.py +++ b/tests/test_parameters.py @@ -176,8 +176,8 @@ def test_read_par_line(self): self.assertTrue(np.isclose(test_m.F8.uncertainty_value, 10)) self.assertEqual(test_m.JUMP1.frozen, True) self.assertEqual(test_m.JUMP1.key, "mjd") - self.assertTrue(np.isclose(test_m.JUMP1.key_value[0].mjd, 52742.0, atol=1e-10)) - self.assertTrue(np.isclose(test_m.JUMP1.key_value[1].mjd, 52745.0, atol=1e-10)) + self.assertTrue(np.isclose(test_m.JUMP1.key_value[0], 52742.0, atol=1e-10)) + self.assertTrue(np.isclose(test_m.JUMP1.key_value[1], 52745.0, atol=1e-10)) self.assertTrue(np.isclose(test_m.JUMP1.value, 0.2)) self.assertEqual(test_m.JUMP2.frozen, True) @@ -609,8 +609,8 @@ def test_parameter_can_be_pickled(p): valid_settings = [ ("tel", "ao"), ("freq", (1000.0 * u.MHz, 2000.0 * u.MHz)), - ("mjd", (Time(57000.0, format="mjd"), Time(58000.0, format="mjd"))), - ("mjd", [Time(57000.0, format="mjd"), Time(58000.0, format="mjd")]), + ("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), @@ -626,7 +626,7 @@ def test_parameter_can_be_pickled(p): @pytest.mark.parametrize("key, key_value", valid_settings) def test_maskParameter_construction_valid(key, key_value): - j = maskParameter( + maskParameter( name="JUMP", index=467, key=key, @@ -635,3 +635,17 @@ def test_maskParameter_construction_valid(key, key_value): units=u.s, description="Generic description of a JUMP", ) + + +@pytest.mark.parametrize("key, key_value", invalid_settings) +def test_maskParameter_construction_invalid(key, key_value): + with pytest.raises(ValueError): + maskParameter( + name="JUMP", + index=467, + key=key, + key_value=key_value, + value=0, + units=u.s, + description="Generic description of a JUMP", + ) diff --git a/tests/test_timing_model.py b/tests/test_timing_model.py index 5da0fdd02..38c7eb915 100644 --- a/tests/test_timing_model.py +++ b/tests/test_timing_model.py @@ -106,14 +106,7 @@ def test_add_component(self): print(cp.get_prefix_mapping_component("JUMP")) print(id(cp), "test") add_jumps = [ - ( - "JUMP", - { - "value": 0.1, - "key": "mjd", - "key_value": [Time(55000, format="mjd"), Time(56000, format="mjd")], - }, - ), + ("JUMP", {"value": 0.1, "key": "mjd", "key_value": [55000, 56000],},), ( "JUMP", { @@ -157,7 +150,7 @@ def test_add_component(self): assert jump2.value == 0.2 assert jump3.value == 0.3 # Check jump key value - assert jump1.key_value == (Time(55000, format="mjd"), Time(56000, format="mjd")) + assert jump1.key_value == (55000, 56000) assert jump2.key_value == (1440 * u.MHz, 2000 * u.MHz) assert jump3.key_value == "arecibo" assert len(tm.jumps) == 3 From 402b136f4d0dfc2874862e10d2cca07f45e7eb95 Mon Sep 17 00:00:00 2001 From: Anne Archibald Date: Wed, 28 Jul 2021 23:45:41 +0100 Subject: [PATCH 13/17] change key, key_value to flag, flag_value --- src/pint/models/jump.py | 34 +++--- src/pint/models/noise_model.py | 40 +++---- src/pint/models/parameter.py | 192 +++++++++++++++--------------- src/pint/models/timing_model.py | 18 +-- tests/datafile/test.tim | 21 ---- tests/test_dmefac_dmequad.py | 24 ++-- tests/test_flagging_clustering.py | 4 +- tests/test_jump.py | 127 ++++---------------- tests/test_mask_parameter.py | 66 +++++----- tests/test_parameters.py | 67 +++++------ tests/test_timing_model.py | 30 +++-- tests/test_toa_writer.py | 5 +- tests/test_wideband_dm_data.py | 4 +- 13 files changed, 258 insertions(+), 374 deletions(-) delete mode 100644 tests/datafile/test.tim diff --git a/src/pint/models/jump.py b/src/pint/models/jump.py index 3dfbb04e7..2602e7c55 100644 --- a/src/pint/models/jump.py +++ b/src/pint/models/jump.py @@ -33,12 +33,12 @@ class PhaseJump(PhaseComponent): >>> for i in toa_index_list: ... toas.table['flags'][i]['fish'] = 'carp' >>> np = m.JUMP1.new_param(100) - >>> np.key = '-fish' - >>> np.key_value = 'carp' + >>> 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], key='-fish', key_value='carp')``, + ``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. @@ -176,7 +176,7 @@ def print_par(self): result += jump_par.as_parfile_line() return result - def add_jump_and_flags(self, toa_flags, key="gui_jump", key_value=None): + 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. Given a subset of TOAs (specified by a reference to their flags objects), @@ -196,9 +196,9 @@ def add_jump_and_flags(self, toa_flags, key="gui_jump", key_value=None): toa_flags: array of dict The TOA flags which must be modified. In pintk (pulsar.py), this will be all_toas.table["flags"][selected] - key: str + flag: str The name of the flag to use for the JUMP. - key_value: str or None + 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. @@ -209,17 +209,17 @@ def add_jump_and_flags(self, toa_flags, key="gui_jump", key_value=None): """ in_use = set() for pm in self.jumps: - if pm.key == "-" + key: - in_use.add(pm.key_value) - if key_value is None: + if pm.flag == "-" + flag: + in_use.add(pm.flag_value) + if flag_value is None: i = 1 while True: - key_value = str(i) - if key_value not in in_use: + flag_value = str(i) + if flag_value not in in_use: break i += 1 - elif key_value in in_use: - raise ValueError(f"A JUMP -{key} {key_value} is already present.") + 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: @@ -231,15 +231,15 @@ def add_jump_and_flags(self, toa_flags, key="gui_jump", key_value=None): param = maskParameter( name="JUMP", index=i, - key="-" + key, - key_value=key_value, + flag="-" + flag, + flag_value=flag_value, value=0.0, units="second", frozen=False, ) name = param.name for d in toa_flags: - if key in d: + if flag in d: raise ValueError( "The selected toa(s) overlap an existing jump. Remove all " "interfering jumps before attempting to jump these toas." @@ -248,7 +248,7 @@ def add_jump_and_flags(self, toa_flags, key="gui_jump", key_value=None): self.setup() # add appropriate flags to TOA table to link jump with appropriate TOA for d in toa_flags: - d[key] = key_value + d[flag] = flag_value return name def tidy_jumps_for_fit(self, toas): 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 66675c129..861f59a55 100644 --- a/src/pint/models/parameter.py +++ b/src/pint/models/parameter.py @@ -1404,7 +1404,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' @@ -1452,8 +1452,8 @@ def __init__( self, name, index=1, - key=None, - key_value=None, + flag=None, + flag_value=None, value=None, long_double=False, units=None, @@ -1465,8 +1465,8 @@ def __init__( ): self.is_mask = True - self.key = key - self.key_value = key_value + self.flag = flag + self.flag_value = flag_value self.index = index name_param = name + str(index) self.origin_name = name @@ -1498,103 +1498,103 @@ def __init__( wants_two_values = {"mjd", "freq"} @staticmethod - def validate(key, key_value): + 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 key is None: - if key_value is not None: + if flag is None: + if flag_value is not None: raise ValueError("Cannot associate a value with no flag") - if key == "mjd": - start, end = sorted(key_value) + 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 {key_value}" + f"pair of numbers not {flag_value}" ) return float(start), float(end) - elif key == "freq": - low, high = sorted(key_value) + 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 {key_value}" + f"pair of Quantities not {flag_value}" ) return low.to(u.MHz), high.to(u.MHz) - elif key == "tel": - return get_observatory(key_value).name + elif flag == "tel": + return get_observatory(flag_value).name else: - if not isinstance(key_value, str): + if not isinstance(flag_value, str): raise ValueError( - f"When selecting by {key} one must supply a " - f"string not {key_value}" + f"When selecting by {flag} one must supply a " + f"string not {flag_value}" ) # FlagDict.validate() once #1074 is merged. - if len(key_value.split()) != 1: - raise ValueError(f"Key value {repr(key_value)} cannot occur in TOAs.") - return key_value + if len(flag_value.split()) != 1: + raise ValueError(f"Flag value {repr(flag_value)} cannot occur in TOAs.") + return flag_value @staticmethod - def convert(key, key_value): - """Convert key_value strings to the appropriate form for the key.""" - if key == "mjd": - start, end = key_value + 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 key == "freq": - start, end = key_value + elif flag == "freq": + start, end = flag_value return float(start) * u.MHz, float(end) * u.MHz else: - return key_value + return flag_value @property - def key(self): - return self._key - - @key.setter - def key(self, key): - if key is None: - self._key = None - elif not isinstance(key, str): + 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(key)}" + f"maskParameters can only select on strings not {repr(flag)}" ) else: - key = key.lower() - if key not in self.allowed_non_flags and not key.startswith("-"): + flag = flag.lower() + if flag not in self.allowed_non_flags and not flag.startswith("-"): raise ValueError( f"Flags must be indicated with -, and the only " f"non-flags allowed are {maskParameter.allowed_non_flags}." ) - self._key = key + self._flag = flag @property - def key_value(self): - return self._key_value + def flag_value(self): + return self._flag_value - @key_value.setter - def key_value(self, key_value): - if key_value is None: - self._key_value = None + @flag_value.setter + def flag_value(self, flag_value): + if flag_value is None: + self._flag_value = None else: - self._key_value = maskParameter.validate(self.key, key_value) + 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: - if isinstance(self.key_value, str): - out += " " + self.key_value + 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.key_value: + for kv in self.flag_value: out += " " + str(kv) if self.quantity is not None: out += " " + self.str_quantity(self.quantity) @@ -1631,19 +1631,19 @@ def from_parfile_line(self, line): Notes ----- - The accepted formats for most keys:: + 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 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 key is one of MJD or FREQ then:: + If the flag is one of MJD or FREQ then:: - 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_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``. @@ -1659,28 +1659,28 @@ def from_parfile_line(self, line): return False try: - self.key = k[1] + self.flag = k[1] 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 self.key in self.wants_two_values: - key_value_str = k[2], k[3] - len_key_v = 2 + if self.flag in self.wants_two_values: + flag_value_str = k[2], k[3] + len_flag_v = 2 else: - key_value_str = k[2] - len_key_v = 1 - self.key_value = maskParameter.convert(self.key, key_value_str) - if len(k) < 3 + len_key_v: + 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_key_v]) + self.value = str2longdouble(k[2 + len_flag_v]) - k_left = k[3 + len_key_v :] + k_left = k[3 + len_flag_v :] if not k_left: pass elif len(k_left) == 1: @@ -1703,20 +1703,20 @@ def as_parfile_line(self): name = self.origin_name else: name = self.use_alias - line = "%-15s %s " % (name, self.key) - if isinstance(self.key_value, str): - line += self.key_value + line = "%-15s %s " % (name, self.flag) + if isinstance(self.flag_value, str): + line += self.flag_value else: - start, end = self.key_value + 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: raise ValueError( - f"Unexpected format in self.key_value: {self.key_value}" + f"Unexpected format in self.flag_value: {self.flag_value}" ) - line += "%25s" % self.print_quantity(self.quantity) + 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)) elif not self.frozen: @@ -1737,8 +1737,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, @@ -1762,30 +1762,30 @@ def select_toa_mask(self, toas): array An array of TOA indices selected by the mask. """ - if self.key == "mjd": - start, end = self.key_value + 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.key == "freq": - low, high = self.key_value + 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.key == "tel": - c = toas.table["obs"] == self.key_value + elif self.flag == "tel": + c = toas.table["obs"] == self.flag_value r = np.nonzero(c)[0] else: r = [] - if self.key is not None: - if self.key.startswith("-"): - key = self.key[1:] + if self.flag is not None: + if self.flag.startswith("-"): + flag = self.flag[1:] else: # only "name" is allowed here - key = self.key - if self.key_value is not None: + flag = self.flag + if self.flag_value is not None: for i, f in enumerate(toas.table["flags"]): - if str(f.get(key, "")) == self.key_value: + if str(f.get(flag, "")) == self.flag_value: r.append(i) r = np.array(r, dtype=int) return r diff --git a/src/pint/models/timing_model.py b/src/pint/models/timing_model.py index 48b094d40..42a40ae6d 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__ ) @@ -1357,9 +1357,9 @@ def jump_flags_to_params(self, toas, flag="jump"): self.remove_param("JUMP1") a.setup() for pm in self.jumps: - if pm.key == "-" + flag: - if pm.key_value in jumped: - jumped.remove(pm.key_value) + 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 @@ -1373,8 +1373,8 @@ def jump_flags_to_params(self, toas, flag="jump"): param = maskParameter( name="JUMP", index=next_free_index, - key="-" + flag, - key_value=j, + flag="-" + flag, + flag_value=j, value=0.0, units="second", uncertainty=0.0, @@ -2245,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) @@ -2568,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/tests/datafile/test.tim b/tests/datafile/test.tim deleted file mode 100644 index 79c4b43e9..000000000 --- a/tests/datafile/test.tim +++ /dev/null @@ -1,21 +0,0 @@ -FORMAT 1 -C Created: 2021-07-28T22:32:26.227045 -C PINT_version: 0.8.2+519.g653e6785.dirty -C User: Anne Archibald (naa280) -C Host: halley -C OS: Linux-5.4.0-77-generic-x86_64-with-glibc2.27 -unk 999999.000000 54999.9999999190350347 1.000 arecibo -pn 35081.0 -unk 999999.000000 55250.0000000421224653 1.000 arecibo -pn 1328078308.0 -unk 999999.000000 55499.9999999678046876 1.000 arecibo -pn 2656157102.0 -unk 999999.000000 55750.0000000257422338 1.000 arecibo -pn 3984292587.0 -unk 999999.000000 56000.0000000452955787 1.000 arecibo -pn 5312347290.0 -unk 999999.000000 54999.9999999566489615 1.000 bat -pn 0.0 -unk 999999.000000 55250.0000000440191276 1.000 bat -pn 1328086291.0 -unk 999999.000000 55500.0000000468705689 1.000 bat -pn 2656172581.0 -unk 999999.000000 55749.9999999652032852 1.000 bat -pn 3984258870.0 -unk 999999.000000 55999.9999999872580569 1.000 bat -pn 5312345159.0 -unk 999999.000000 55000.0000000064168981 1.000 gbt -pn 35081.0 -unk 999999.000000 55250.0000000315666088 1.000 gbt -pn 1328078308.0 -unk 999999.000000 55499.9999999818982986 1.000 gbt -pn 2656157102.0 -unk 999999.000000 55749.9999999297936112 1.000 gbt -pn 3984292586.0 -unk 999999.000000 56000.0000000503245833 1.000 gbt -pn 5312347290.0 diff --git a/tests/test_dmefac_dmequad.py b/tests/test_dmefac_dmequad.py index 44dc1191f..8d2d667a6 100644 --- a/tests/test_dmefac_dmequad.py +++ b/tests/test_dmefac_dmequad.py @@ -38,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) @@ -50,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) @@ -65,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) @@ -88,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_flagging_clustering.py b/tests/test_flagging_clustering.py index 988c94e7b..c9962e05b 100644 --- a/tests/test_flagging_clustering.py +++ b/tests/test_flagging_clustering.py @@ -42,7 +42,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,7 +55,7 @@ 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) diff --git a/tests/test_jump.py b/tests/test_jump.py index cac5e0300..9d33cea3c 100644 --- a/tests/test_jump.py +++ b/tests/test_jump.py @@ -8,7 +8,7 @@ import astropy.units as u import numpy as np import pytest -from numpy.testing import assert_allclose +from numpy.testing import assert_allclose, assert_array_equal from pinttestdata import datadir import pint.models.parameter @@ -47,8 +47,8 @@ def test_add_jumps_and_flags(setup_NGC6440E): selected_toa_ind2 = [10, 11, 12] j2 = cp.add_jump_and_flags(setup_NGC6440E.t.table["flags"][selected_toa_ind2]) jp2 = getattr(cp, j2) - assert jp2.key == "-gui_jump" - assert jp2.key_value == "2" + 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" @@ -56,6 +56,7 @@ def test_add_jumps_and_flags(setup_NGC6440E): for d in setup_NGC6440E.t.table["flags"][selected_toa_ind2]: assert d["gui_jump"] == "2" + def test_add_overlapping_jump(setup_NGC6440E): setup_NGC6440E.m.add_component(PhaseJump(), validate=False) cp = setup_NGC6440E.m.components["PhaseJump"] @@ -77,88 +78,14 @@ 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 * u.MHz, 1700 * u.MHz], - 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 * u.MHz, 1900 * u.MHz], - 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", + flag="freq", value=0.2, - key_value=[1440 * u.MHz, 1700 * u.MHz], + 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] @@ -166,19 +93,9 @@ def test_multijump_toa(setup_NGC6440E): # 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 @@ -207,10 +124,10 @@ def test_jump_selects_toas(self): if not p.startswith("JUMP"): continue pm = getattr(self.JUMPm, p) - if pm.key != "-chanid": + if pm.flag != "-chanid": continue assert len( - [l for l in open(self.timf).readlines() if re.search(pm.key_value, l)] + [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): @@ -325,8 +242,8 @@ def test_multiple_jumps_add(): ) ) for jmp in m.jumps: - if jmp.key == "mjd": - start, end = jmp.key_value + if jmp.flag == "mjd": + start, end = jmp.flag_value if start < 58500: first_jump = jmp else: @@ -357,20 +274,20 @@ def test_multiple_jumps_add(): [ pint.models.parameter.maskParameter( name="JUMP", - key="-fish", - key_value="carp", + flag="-fish", + flag_value="carp", units=u.s, value=7, frozen=False, uncertainty=0.1, ), pint.models.parameter.maskParameter( - name="JUMP", key="tel", key_value="ao", units=u.s, value=7, frozen=False, + name="JUMP", flag="tel", flag_value="ao", units=u.s, value=7, frozen=False, ), pint.models.parameter.maskParameter( name="JUMP", - key="MJD", - key_value=(57000, 58000,), + flag="MJD", + flag_value=(57000, 58000,), units=u.s, value=7, frozen=False, @@ -382,8 +299,8 @@ def test_jump_parfile_roundtrip(j): nj = pint.models.parameter.maskParameter(name="JUMP", units=u.s) nj.from_parfile_line(l) - assert nj.key == j.key - assert nj.key_value == j.key_value + 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 @@ -415,7 +332,7 @@ class R: r.m = m r.t = t for j in m.jumps: - if j.key == "mjd": + if j.flag == "mjd": r.j = j return r @@ -428,7 +345,7 @@ def test_tidy_jumps_all_ok(small): def test_tidy_jumps_all_jumped(small): small.j.frozen = False - small.j.key_value = ( + small.j.flag_value = ( 56000, 70000, ) @@ -439,7 +356,7 @@ def test_tidy_jumps_all_jumped(small): def test_tidy_jumps_irrelevant(small): small.j.frozen = False j2 = small.j.new_param(index=100, copy_all=True) - j2.key_value = ( + j2.flag_value = ( 50000, 55000, ) @@ -452,7 +369,7 @@ def test_tidy_jumps_irrelevant(small): def test_tidy_jumps_cover_all_freeze_one(small): small.j.frozen = False j2 = small.j.new_param(index=100, copy_all=True) - j2.key_value = ( + j2.flag_value = ( 50000, 59000, ) diff --git a/tests/test_mask_parameter.py b/tests/test_mask_parameter.py index b145d1859..fcf6d8a11 100644 --- a/tests/test_mask_parameter.py +++ b/tests/test_mask_parameter.py @@ -23,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 @@ -37,17 +37,17 @@ def test_mjd_mask(toas): assert np.all(toas.table["mjd_float"][select_toas] >= 54000) with pytest.raises(ValueError): maskParameter( - "test2", key="mjd", key_value=["54000", "54100"], + "test2", flag="mjd", flag_value=["54000", "54100"], ) - mp_value_switch = maskParameter("test1", key="mjd", key_value=[54100, 54000],) - assert mp_value_switch.key_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): - mp_str_keyval = maskParameter("test2", key="mjd", key_value=[54000]) + maskParameter("test2", flag="mjd", flag_value=[54000]) def test_freq_mask(toas): - mp = maskParameter("test2", key="freq", key_value=[1400 * u.MHz, 1430 * u.MHz]) - 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( @@ -55,44 +55,44 @@ def test_freq_mask(toas): ) assert np.all(select_toas == raw_selection[0]) with pytest.raises(ValueError): - maskParameter("test2", key="freq", key_value=["1400", "2000"]) + maskParameter("test2", flag="freq", flag_value=["1400", "2000"]) mp_switch = 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_switch.key_value == (1400 * u.MHz, 2000 * 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): - maskParameter("test2", key="freq", key_value=[1400 * u.MHz]) + 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): - 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 = [] @@ -101,18 +101,18 @@ def test_name_mask(toas): raw_selection.append(i) assert np.all(select_toas == raw_selection) with pytest.raises(ValueError): - maskParameter("test2", key="name", key_value=["name1", "name2"]) + maskParameter("test2", flag="name", flag_value=["name1", "name2"]) def test_flag_mask(toas): with pytest.raises(ValueError): - maskParameter("test2", key="-fe", key_value=430) - mp_flag2 = maskParameter("test2", key="-fe", key_value="430") - assert mp_flag2.key_value == "430" + 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", 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_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 = [] diff --git a/tests/test_parameters.py b/tests/test_parameters.py index 6f30dbe68..71a15aef2 100644 --- a/tests/test_parameters.py +++ b/tests/test_parameters.py @@ -7,11 +7,8 @@ import astropy.units as u import numpy as np import pytest -<<<<<<< HEAD -from numpy.testing import assert_allclose -======= from astropy.time import Time ->>>>>>> Clean up maskParameter so jumps make more sense +from numpy.testing import assert_allclose from pinttestdata import datadir import pint.fitter @@ -24,11 +21,8 @@ floatParameter, intParameter, maskParameter, -<<<<<<< HEAD pairParameter, prefixParameter, -======= ->>>>>>> Clean up maskParameter so jumps make more sense strParameter, ) from pint.toa import get_TOAs @@ -85,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( @@ -151,8 +145,6 @@ def setUpClass(cls): cls.m = get_model("B1855+09_NANOGrav_dfg+12_modified.par") cls.mp = get_model("prefixtest.par") -<<<<<<< HEAD -======= def test_read_par_line(self): test_m = get_model("test_par_read.par") self.assertEqual(test_m.F2.frozen, True) @@ -175,9 +167,9 @@ def test_read_par_line(self): 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.key, "mjd") - self.assertTrue(np.isclose(test_m.JUMP1.key_value[0], 52742.0, atol=1e-10)) - self.assertTrue(np.isclose(test_m.JUMP1.key_value[1], 52745.0, atol=1e-10)) + 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) @@ -187,25 +179,21 @@ def test_read_par_line(self): 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.key, "-testflag") + self.assertEqual(test_m.JUMP12.flag, "-testflag") self.assertEqual(test_m.JUMP12.frozen, False) - self.assertEqual(test_m.JUMP12.key_value, "flagvalue") + 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)) - self.assertTrue( - np.isclose(test_m.RAJ.uncertainty_value, 476.94611148516092061223) + assert_allclose( + test_m.RAJ.uncertainty, + 476.94611148516092061223 * pint_units["hourangle_second"], ) - self.assertTrue( - np.isclose( - test_m.DECJ.uncertainty_value, - 190996312986311097848351.00000000000000000000, - ) + assert_allclose( + test_m.DECJ.uncertainty, + 190996312986311097848351.00000000000000000000 * u.arcsec, ) - self.assertTrue(test_m.RAJ.uncertainty.unit, pint_units["hourangle_second"]) - self.assertTrue(test_m.RAJ.uncertainty.unit, u.arcsecond) ->>>>>>> Clean up maskParameter so jumps make more sense def test_RAJ(self): """Check whether the value and units of RAJ parameter are ok""" units = u.hourangle @@ -603,8 +591,9 @@ def test_set_uncertainty_bogus_raises(p): def test_parameter_can_be_pickled(p): pickle.dumps(p) + # Testing maskParameters: -# Their key and key_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. +# 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"), @@ -624,27 +613,27 @@ def test_parameter_can_be_pickled(p): ] -@pytest.mark.parametrize("key, key_value", valid_settings) -def test_maskParameter_construction_valid(key, key_value): +@pytest.mark.parametrize("flag, flag_value", valid_settings) +def test_maskParameter_construction_valid(flag, flag_value): maskParameter( name="JUMP", index=467, - key=key, - key_value=key_value, + flag=flag, + flag_value=flag_value, value=0, units=u.s, description="Generic description of a JUMP", ) -@pytest.mark.parametrize("key, key_value", invalid_settings) -def test_maskParameter_construction_invalid(key, key_value): +@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, - key=key, - key_value=key_value, + flag=flag, + flag_value=flag_value, value=0, units=u.s, description="Generic description of a JUMP", diff --git a/tests/test_timing_model.py b/tests/test_timing_model.py index 38c7eb915..7b0941dcb 100644 --- a/tests/test_timing_model.py +++ b/tests/test_timing_model.py @@ -106,16 +106,16 @@ def test_add_component(self): 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.1, "flag": "mjd", "flag_value": [55000, 56000],},), ( "JUMP", { "value": 0.2, - "key": "freq", - "key_value": [1440 * u.MHz, 2000 * u.MHz], + "flag": "freq", + "flag_value": [1440 * u.MHz, 2000 * u.MHz], }, ), - ("JUMP", {"value": 0.3, "key": "tel", "key_value": "ao"}), + ("JUMP", {"value": 0.3, "flag": "tel", "flag_value": "ao"}), ] for jp in add_jumps: @@ -124,9 +124,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) @@ -142,17 +142,15 @@ 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 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): @@ -361,8 +359,8 @@ def test_assumes_dmepoch_equals_pepoch(): assert_allclose(m_assume.dm_value(t), m_given.dm_value(t)) assert ("-jump", "1") in [ - (j.key, j.key_value) for j in m.components["PhaseJump"].jumps + (j.flag, j.flag_value) for j in m.components["PhaseJump"].jumps ] assert ("-jump", "2") in [ - (j.key, j.key_value) for j in m.components["PhaseJump"].jumps + (j.flag, j.flag_value) for j in m.components["PhaseJump"].jumps ] diff --git a/tests/test_toa_writer.py b/tests/test_toa_writer.py index 3e0104b21..33a17f78d 100644 --- a/tests/test_toa_writer.py +++ b/tests/test_toa_writer.py @@ -146,7 +146,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"), @@ -158,7 +158,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_wideband_dm_data.py b/tests/test_wideband_dm_data.py index 20b398a80..083205797 100644 --- a/tests/test_wideband_dm_data.py +++ b/tests/test_wideband_dm_data.py @@ -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] = 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("")) From 4244276a335e343f6a9dcd815105a08fc929215d Mon Sep 17 00:00:00 2001 From: Anne Archibald Date: Mon, 2 Aug 2021 12:44:34 +0100 Subject: [PATCH 14/17] Get rid of the `-` on flags in API --- src/pint/models/jump.py | 4 +-- src/pint/models/parameter.py | 44 +++++++++++++++++-------------- src/pint/models/timing_model.py | 8 +++--- tests/test_dmefac_dmequad.py | 12 ++++----- tests/test_flagging_clustering.py | 6 +---- tests/test_jump.py | 4 +-- tests/test_mask_parameter.py | 8 +++--- tests/test_parameters.py | 11 ++++---- tests/test_timing_model.py | 6 ++--- 9 files changed, 51 insertions(+), 52 deletions(-) diff --git a/src/pint/models/jump.py b/src/pint/models/jump.py index 2602e7c55..0089d26dc 100644 --- a/src/pint/models/jump.py +++ b/src/pint/models/jump.py @@ -209,7 +209,7 @@ def add_jump_and_flags(self, toa_flags, flag="gui_jump", flag_value=None): """ in_use = set() for pm in self.jumps: - if pm.flag == "-" + flag: + if pm.flag == flag: in_use.add(pm.flag_value) if flag_value is None: i = 1 @@ -231,7 +231,7 @@ def add_jump_and_flags(self, toa_flags, flag="gui_jump", flag_value=None): param = maskParameter( name="JUMP", index=i, - flag="-" + flag, + flag=flag, flag_value=flag_value, value=0.0, units="second", diff --git a/src/pint/models/parameter.py b/src/pint/models/parameter.py index 861f59a55..ce9202bf4 100644 --- a/src/pint/models/parameter.py +++ b/src/pint/models/parameter.py @@ -42,6 +42,7 @@ time_to_longdouble, time_to_mjd_string, ) +from pint.toa import FlagDict from pint.utils import split_prefixed_name log = logging.getLogger(__name__) @@ -1529,14 +1530,7 @@ def validate(flag, flag_value): elif flag == "tel": return get_observatory(flag_value).name else: - if not isinstance(flag_value, str): - raise ValueError( - f"When selecting by {flag} one must supply a " - f"string not {flag_value}" - ) - # FlagDict.validate() once #1074 is merged. - if len(flag_value.split()) != 1: - raise ValueError(f"Flag value {repr(flag_value)} cannot occur in TOAs.") + FlagDict.check_allowed_value(flag, flag_value) return flag_value @staticmethod @@ -1568,11 +1562,7 @@ def flag(self, flag): ) else: flag = flag.lower() - if flag not in self.allowed_non_flags and not flag.startswith("-"): - raise ValueError( - f"Flags must be indicated with -, and the only " - f"non-flags allowed are {maskParameter.allowed_non_flags}." - ) + FlagDict.check_allowed_key(flag) self._flag = flag @property @@ -1633,10 +1623,10 @@ def from_parfile_line(self, line): ----- The accepted formats for most flags:: - 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 + 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:: @@ -1659,13 +1649,23 @@ def from_parfile_line(self, line): return False try: - self.flag = k[1] + flag = k[1].lower() except IndexError: raise ValueError( "{}: 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:] - if self.flag in self.wants_two_values: + if flag in self.wants_two_values: flag_value_str = k[2], k[3] len_flag_v = 2 else: @@ -1703,7 +1703,11 @@ def as_parfile_line(self): name = self.origin_name else: name = self.use_alias - line = "%-15s %s " % (name, self.flag) + 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: diff --git a/src/pint/models/timing_model.py b/src/pint/models/timing_model.py index 42a40ae6d..f6c0beef7 100644 --- a/src/pint/models/timing_model.py +++ b/src/pint/models/timing_model.py @@ -1344,9 +1344,9 @@ def jump_flags_to_params(self, toas, flag="jump"): new_jumps = [] # check if any TOAs are jumped - jumped = set( - flag_dict[flag] for flag_dict in toas.table["flags"] if flag in flag_dict - ) + jumped = set(toas[flag]) + if "" in jumped: + jumped.remove("") if not jumped: log.info("No jump flags to process from .tim file") return new_jumps @@ -1373,7 +1373,7 @@ def jump_flags_to_params(self, toas, flag="jump"): param = maskParameter( name="JUMP", index=next_free_index, - flag="-" + flag, + flag=flag, flag_value=j, value=0.0, units="second", diff --git a/tests/test_dmefac_dmequad.py b/tests/test_dmefac_dmequad.py index 8d2d667a6..9b0ca5658 100644 --- a/tests/test_dmefac_dmequad.py +++ b/tests/test_dmefac_dmequad.py @@ -38,7 +38,7 @@ def test_no_efact_noequad(test_toas, test_model): def test_only_one_efact(test_toas, test_model): - test_model.DMEFAC1.flag = "-fe" + test_model.DMEFAC1.flag = "fe" test_model.DMEFAC1.flag_value = "Rcvr_800" test_model.DMEFAC1.value = 10 test_model.setup() @@ -50,7 +50,7 @@ def test_only_one_efact(test_toas, test_model): def test_only_one_equad(test_toas, test_model): - test_model.DMEQUAD1.flag = "-fe" + test_model.DMEQUAD1.flag = "fe" test_model.DMEQUAD1.flag_value = "YUPPI" test_model.DMEQUAD1.value = 10 test_model.setup() @@ -65,10 +65,10 @@ 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.flag = "-fe" + test_model.DMEQUAD1.flag = "fe" test_model.DMEQUAD1.flag_value = "Rcvr_800" test_model.DMEQUAD1.value = 10 - test_model.DMEFAC1.flag = "-fe" + test_model.DMEFAC1.flag = "fe" test_model.DMEFAC1.flag_value = "Rcvr_800" test_model.DMEFAC1.value = 10 test_model.setup() @@ -88,10 +88,10 @@ 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.flag = "-fe" + test_model.DMEQUAD1.flag = "fe" test_model.DMEQUAD1.flag_value = "Rcvr_800" test_model.DMEQUAD1.value = 10 - test_model.DMEFAC1.flag = "-fe" + test_model.DMEFAC1.flag = "fe" test_model.DMEFAC1.flag_value = "YUPPI" test_model.DMEFAC1.value = 20 test_model.setup() diff --git a/tests/test_flagging_clustering.py b/tests/test_flagging_clustering.py index c9962e05b..cf4b46eff 100644 --- a/tests/test_flagging_clustering.py +++ b/tests/test_flagging_clustering.py @@ -55,14 +55,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", flag="-toacluster", value=0.2, flag_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_jump.py b/tests/test_jump.py index 9d33cea3c..be222c285 100644 --- a/tests/test_jump.py +++ b/tests/test_jump.py @@ -47,7 +47,7 @@ def test_add_jumps_and_flags(setup_NGC6440E): selected_toa_ind2 = [10, 11, 12] 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 == "gui_jump" assert jp2.flag_value == "2" # check previous jump flags unaltered for d in setup_NGC6440E.t.table["flags"][selected_toa_ind]: @@ -274,7 +274,7 @@ def test_multiple_jumps_add(): [ pint.models.parameter.maskParameter( name="JUMP", - flag="-fish", + flag="fish", flag_value="carp", units=u.s, value=7, diff --git a/tests/test_mask_parameter.py b/tests/test_mask_parameter.py index fcf6d8a11..d93969221 100644 --- a/tests/test_mask_parameter.py +++ b/tests/test_mask_parameter.py @@ -106,12 +106,12 @@ def test_name_mask(toas): def test_flag_mask(toas): with pytest.raises(ValueError): - maskParameter("test2", flag="-fe", flag_value=430) - mp_flag2 = maskParameter("test2", flag="-fe", flag_value="430") + 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") + 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 diff --git a/tests/test_parameters.py b/tests/test_parameters.py index 71a15aef2..3f1d0b375 100644 --- a/tests/test_parameters.py +++ b/tests/test_parameters.py @@ -91,7 +91,7 @@ def test_read_par_line_expected_values(): 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.flag == "-testflag" + assert test_m.JUMP12.flag == "testflag" assert not test_m.JUMP12.frozen assert test_m.JUMP12.flag_value == "flagvalue" assert_allclose(test_m.JUMP12.value, 0.1) @@ -179,7 +179,7 @@ def test_read_par_line(self): 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.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)) @@ -600,7 +600,7 @@ def test_parameter_can_be_pickled(p): ("freq", (1000.0 * u.MHz, 2000.0 * u.MHz)), ("mjd", (57000.0, 58000.0)), ("mjd", [57000.0, 58000.0]), - ("-fish", "carp"), + ("fish", "carp"), ("freq", (2000.0 * u.MHz, np.inf * u.MHz)), ("freq", np.array([1000, 2000], dtype=np.longdouble) * u.MHz), ] @@ -608,8 +608,9 @@ def test_parameter_can_be_pickled(p): ("tel", (10, 20)), ("freq", "ao"), ("mjd", ([], [])), - ("-fish", (1, 2)), - ("-fish", ["c", "a", "r", "p"]), + ("-fish", "carp"), + ("fish", (1, 2)), + ("fish", ["c", "a", "r", "p"]), ] diff --git a/tests/test_timing_model.py b/tests/test_timing_model.py index 7b0941dcb..5146bc75c 100644 --- a/tests/test_timing_model.py +++ b/tests/test_timing_model.py @@ -342,8 +342,6 @@ 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 def test_supports_rm(): @@ -358,9 +356,9 @@ def test_assumes_dmepoch_equals_pepoch(): t = make_fake_toas_uniform(57000, 59000, 10, m_assume) assert_allclose(m_assume.dm_value(t), m_given.dm_value(t)) - assert ("-jump", "1") in [ + assert ("jump", "1") in [ (j.flag, j.flag_value) for j in m.components["PhaseJump"].jumps ] - assert ("-jump", "2") in [ + assert ("jump", "2") in [ (j.flag, j.flag_value) for j in m.components["PhaseJump"].jumps ] From 932ba5419b96b3facc8fea96371ca193607d1642 Mon Sep 17 00:00:00 2001 From: Anne Archibald Date: Mon, 2 Aug 2021 13:01:51 +0100 Subject: [PATCH 15/17] Test pintk fitting freezes jumps --- tests/test_pintk_pulsar.py | 45 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) 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 From b644dc5de3d5630b06210ba0ea56ffc538316a06 Mon Sep 17 00:00:00 2001 From: Anne Archibald Date: Mon, 23 Aug 2021 17:54:40 +0100 Subject: [PATCH 16/17] Fix up conflicts and isort again --- tests/test_datafiles.py | 4 +++- tests/test_density.py | 2 +- tests/test_derived_quantities.py | 8 ++++---- tests/test_downhill_fitter.py | 2 +- tests/test_fake_toas.py | 18 ++++++++++-------- tests/test_fitter.py | 4 +--- tests/test_fitter_compare.py | 2 +- tests/test_flagging_clustering.py | 9 ++++----- tests/test_infostrings.py | 12 ++++++------ tests/test_jump.py | 5 +++-- tests/test_model_derivatives.py | 4 ++-- tests/test_parametercovariancematrix.py | 2 +- tests/test_plk_widget.py | 4 +++- tests/test_pulse_number.py | 4 ++-- tests/test_random_models.py | 6 +----- tests/test_residuals.py | 2 +- tests/test_solar_wind.py | 4 ++-- tests/test_stigma.py | 8 +++----- tests/test_timing_model.py | 3 ++- tests/test_toa.py | 2 +- tests/test_toa_pickle.py | 3 +-- tests/test_toa_reader.py | 5 +---- tests/test_toa_writer.py | 6 +----- 23 files changed, 55 insertions(+), 64 deletions(-) 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_density.py b/tests/test_density.py index 169b582d3..097873941 100644 --- a/tests/test_density.py +++ b/tests/test_density.py @@ -8,8 +8,8 @@ from astropy.time import Time from pylab import * -import pint.toa as toa import pint.simulation as simulation +import pint.toa as toa from pint.models import get_model 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_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_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_fitter.py b/tests/test_fitter.py index b306738be..a3a6e55c9 100644 --- a/tests/test_fitter.py +++ b/tests/test_fitter.py @@ -11,10 +11,8 @@ 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 bf6664e53..d4aba4d26 100644 --- a/tests/test_fitter_compare.py +++ b/tests/test_fitter_compare.py @@ -19,8 +19,8 @@ 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_flagging_clustering.py b/tests/test_flagging_clustering.py index cf4b46eff..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: 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 be222c285..fecfe0306 100644 --- a/tests/test_jump.py +++ b/tests/test_jump.py @@ -15,6 +15,7 @@ import pint.toa from pint.models import PhaseJump, get_model, parameter as p from pint.residuals import Residuals +from pint.simulation import make_fake_toas_uniform class SimpleSetup: @@ -248,7 +249,7 @@ def test_multiple_jumps_add(): first_jump = jmp else: second_jump = jmp - toas = pint.toa.make_fake_toas(57000, 60000 - 1, 10, m) + toas = make_fake_toas_uniform(57000, 60000 - 1, 10, m) first_jump.quantity = 100 * u.us second_jump.quantity = 0 * u.us @@ -323,7 +324,7 @@ def small(): """ ) ) - t = pint.toa.make_fake_toas(58000, 60000, 20, m) + t = make_fake_toas_uniform(58000, 60000, 20, m) class R: pass 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_parametercovariancematrix.py b/tests/test_parametercovariancematrix.py index 536b9183c..b91417e38 100644 --- a/tests/test_parametercovariancematrix.py +++ b/tests/test_parametercovariancematrix.py @@ -17,8 +17,8 @@ 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_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_pulse_number.py b/tests/test_pulse_number.py index 9c880f5f3..e47d42c56 100644 --- a/tests/test_pulse_number.py +++ b/tests/test_pulse_number.py @@ -11,9 +11,9 @@ import pint.fitter from pint.models import get_model -from pint.toa import get_TOAs -from pint.simulation import make_fake_toas_uniform 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 5a167f181..4cb73eb43 100644 --- a/tests/test_random_models.py +++ b/tests/test_random_models.py @@ -7,15 +7,11 @@ # matplotlib.use('TKAgg') import matplotlib.pyplot as plt 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 fitter, ls, toa, utils +from pint import fitter, ls, simulation, toa, utils from pint.models import get_model, get_model_and_toas 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_solar_wind.py b/tests/test_solar_wind.py index 2e2cf08a0..c8db21849 100644 --- a/tests/test_solar_wind.py +++ b/tests/test_solar_wind.py @@ -14,9 +14,9 @@ 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 686a46d6d..512da742a 100644 --- a/tests/test_stigma.py +++ b/tests/test_stigma.py @@ -6,14 +6,12 @@ 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 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 = """ diff --git a/tests/test_timing_model.py b/tests/test_timing_model.py index 5146bc75c..20724c4b7 100644 --- a/tests/test_timing_model.py +++ b/tests/test_timing_model.py @@ -11,14 +11,15 @@ from pint.simulation import make_fake_toas_uniform from pint.toa import get_TOAs from astropy.time import Time +from pinttestdata import datadir from pint.models import ( DEFAULT_ORDER, AstrometryEquatorial, BinaryELL1, + PhaseJump, Spindown, TimingModel, - PhaseJump, Wave, get_model, parameter as p, 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_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 09211001a..58dbc38e3 100644 --- a/tests/test_toa_reader.py +++ b/tests/test_toa_reader.py @@ -11,13 +11,10 @@ from astropy.utils.iers import conf from hypothesis import given 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 -from pint import toa +from pint import simulation, toa from pint.models import get_model, get_model_and_toas from pint.observatory import bipm_default diff --git a/tests/test_toa_writer.py b/tests/test_toa_writer.py index 33a17f78d..5062bd5c4 100644 --- a/tests/test_toa_writer.py +++ b/tests/test_toa_writer.py @@ -8,12 +8,8 @@ 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): From 4552ef40f4263ebc3ca6f343bcf9fdd56758021f Mon Sep 17 00:00:00 2001 From: Anne Archibald Date: Mon, 23 Aug 2021 19:55:06 +0100 Subject: [PATCH 17/17] Clean up merge mess --- tests/test_astrometry.py | 1 - tests/test_timing_model.py | 17 +++++++---------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/tests/test_astrometry.py b/tests/test_astrometry.py index c95a8045a..4c2e3da4d 100644 --- a/tests/test_astrometry.py +++ b/tests/test_astrometry.py @@ -8,7 +8,6 @@ from pinttestdata import datadir from pint.models import get_model -from pinttestdata import datadir @pytest.fixture diff --git a/tests/test_timing_model.py b/tests/test_timing_model.py index 20724c4b7..bf563146b 100644 --- a/tests/test_timing_model.py +++ b/tests/test_timing_model.py @@ -6,11 +6,8 @@ import astropy.units as u import numpy as np import pytest -from numpy.testing import assert_allclose -from pinttestdata import datadir -from pint.simulation import make_fake_toas_uniform -from pint.toa import get_TOAs from astropy.time import Time +from numpy.testing import assert_allclose from pinttestdata import datadir from pint.models import ( @@ -343,6 +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 ("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(): @@ -357,9 +360,3 @@ def test_assumes_dmepoch_equals_pepoch(): t = make_fake_toas_uniform(57000, 59000, 10, m_assume) assert_allclose(m_assume.dm_value(t), m_given.dm_value(t)) - 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 - ]