From 18baf6bc4334dca76f5fa2c9e586743e02a5e5f0 Mon Sep 17 00:00:00 2001 From: David Kaplan Date: Mon, 5 Feb 2024 15:49:32 -0600 Subject: [PATCH 01/13] turn off subtract_mean from fake toas --- src/pint/simulation.py | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/src/pint/simulation.py b/src/pint/simulation.py index d66128dcb..3dc136a07 100644 --- a/src/pint/simulation.py +++ b/src/pint/simulation.py @@ -22,7 +22,7 @@ ] -def zero_residuals(ts, model, maxiter=10, tolerance=None): +def zero_residuals(ts, model, subtract_mean=True, maxiter=10, tolerance=None): """Use a model to adjust a TOAs object, setting residuals to 0 iteratively. Parameters @@ -42,7 +42,9 @@ def zero_residuals(ts, model, maxiter=10, tolerance=None): if tolerance is None: tolerance = 1 * u.ns if pint.utils.check_longdouble_precision() else 5 * u.us for i in range(maxiter): - r = pint.residuals.Residuals(ts, model, track_mode="use_pulse_numbers") + r = pint.residuals.Residuals( + ts, model, subtract_mean=subtract_mean, track_mode="use_pulse_numbers" + ) resids = r.calc_time_resids(calctype="taylor") if maxresid is not None and (np.abs(resids).max() > maxresid): log.warning( @@ -104,7 +106,14 @@ def get_fake_toa_clock_versions(model, include_bipm=False, include_gps=True): } -def make_fake_toas(ts, model, add_noise=False, add_correlated_noise=False, name="fake"): +def make_fake_toas( + ts, + model, + add_noise=False, + add_correlated_noise=False, + name="fake", + subtract_mean=True, +): """Make toas from an array of times Can include alternating frequencies if fed an array of frequencies, @@ -122,6 +131,8 @@ def make_fake_toas(ts, model, add_noise=False, add_correlated_noise=False, name= Add correlated noise to the TOAs if it's present in the timing mode. name : str, optional Name for the TOAs (goes into the flags) + subtract_mean : bool, optional + Controls whether mean will be subtracted from the residuals when making fake TOAs Returns ------- @@ -133,7 +144,7 @@ def make_fake_toas(ts, model, add_noise=False, add_correlated_noise=False, name= `add_noise` respects any ``EFAC`` or ``EQUAD`` present in the `model` """ tsim = deepcopy(ts) - zero_residuals(tsim, model) + zero_residuals(tsim, model, subtract_mean=subtract_mean) if add_correlated_noise: U = model.noise_model_designmatrix(tsim) @@ -191,6 +202,7 @@ def make_fake_toas_uniform( include_gps=True, multi_freqs_in_epoch=False, flags=None, + subtract_mean=True, ): """Simulate uniformly spaced TOAs. @@ -236,6 +248,8 @@ def make_fake_toas_uniform( Whether to generate multiple frequency TOAs for the same epoch. flags: None or dict Dictionary of flags to be added to all simulated TOAs. + subtract_mean : bool, optional + Controls whether mean will be subtracted from the residuals when making fake TOAs Returns ------- @@ -308,6 +322,7 @@ def make_fake_toas_uniform( add_noise=add_noise, add_correlated_noise=add_correlated_noise, name=name, + subtract_mean=subtract_mean, ) @@ -326,6 +341,7 @@ def make_fake_toas_fromMJDs( include_gps=True, multi_freqs_in_epoch=False, flags=None, + subtract_mean=True, ): """Simulate TOAs from a list of MJDs @@ -363,6 +379,8 @@ def make_fake_toas_fromMJDs( Whether to generate multiple frequency TOAs for the same epoch. flags: None or dict Dictionary of flags to be added to all simulated TOAs. + subtract_mean : bool, optional + Controls whether mean will be subtracted from the residuals when making fake TOAs Returns ------- @@ -439,11 +457,17 @@ def make_fake_toas_fromMJDs( add_noise=add_noise, add_correlated_noise=add_correlated_noise, name=name, + subtract_mean=subtract_mean, ) def make_fake_toas_fromtim( - timfile, model, add_noise=False, add_correlated_noise=False, name="fake" + timfile, + model, + add_noise=False, + add_correlated_noise=False, + name="fake", + subtract_mean=True, ): """Simulate fake TOAs with the same times as an input tim file @@ -459,6 +483,8 @@ def make_fake_toas_fromtim( Add correlated noise to the TOAs if it's present in the timing mode. name : str, optional Name for the TOAs (goes into the flags) + subtract_mean : bool, optional + Controls whether mean will be subtracted from the residuals when making fake TOAs Returns ------- @@ -493,6 +519,7 @@ def make_fake_toas_fromtim( add_noise=add_noise, add_correlated_noise=add_correlated_noise, name=name, + subtract_mean=subtract_mean, ) From de5ec155bcc6d550da0fd4f72506b7194c5f662e Mon Sep 17 00:00:00 2001 From: David Kaplan Date: Mon, 5 Feb 2024 17:13:57 -0600 Subject: [PATCH 02/13] test & changelog --- CHANGELOG-unreleased.md | 1 + tests/test_fake_toas.py | 54 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/CHANGELOG-unreleased.md b/CHANGELOG-unreleased.md index 5ac8acfd0..0b40ab524 100644 --- a/CHANGELOG-unreleased.md +++ b/CHANGELOG-unreleased.md @@ -32,6 +32,7 @@ the released changes. - Documentation: Noise fitting example notebook. - `freeze_params` option in `wavex_setup` and `dmwavex_setup` - `plrednoise_from_wavex`, `pldmnoise_from_dmwavex`, and `find_optimal_nharms` functions +- fake TOAs can be created with `subtract_mean=False`, to maintain phase coherence between different data sets ### Fixed - `MCMC_walkthrough` notebook now runs - Fixed runtime data README diff --git a/tests/test_fake_toas.py b/tests/test_fake_toas.py index d1678085e..16c95e46a 100644 --- a/tests/test_fake_toas.py +++ b/tests/test_fake_toas.py @@ -236,6 +236,60 @@ def test_fake_fromMJDs(MJDs): assert np.isclose(r.calc_time_resids().std(), 1 * u.us, rtol=0.2) +def test_fake_fromMJDs_keepmean(): + # basic model, no EFAC or EQUAD + model = get_model( + io.StringIO( + """ + PSRJ J1234+5678 + ELAT 0 + ELONG 0 + DM 10 + F0 1 + F1 -1E-15 + PEPOCH 58000 + """ + ) + ) + t1 = np.linspace(57001, 57500, 100, dtype=np.longdouble) * u.d + t2 = np.linspace(57501, 58000, 100, dtype=np.longdouble) * u.d + toas1 = pint.simulation.make_fake_toas_fromMJDs( + t1, + model=model, + error=1 * u.us, + add_noise=True, + ) + toas2 = pint.simulation.make_fake_toas_fromMJDs( + t2, + model=model, + error=1 * u.us, + add_noise=True, + ) + r = pint.residuals.Residuals(toas1 + toas2, model) + toas1m = pint.simulation.make_fake_toas_fromMJDs( + t1, + model=model, + error=1 * u.us, + add_noise=True, + subtract_mean=False, + ) + toas2m = pint.simulation.make_fake_toas_fromMJDs( + t2, + model=model, + error=1 * u.us, + add_noise=True, + subtract_mean=False, + ) + r = pint.residuals.Residuals(toas1 + toas2, model) + rm = pint.residuals.Residuals(toas1m + toas2m, model) + + # need a generous rtol because of the small statistics + # this first test should fail because the two segments won't have the same mean + assert not np.isclose(r.calc_time_resids().std(), 1 * u.us, rtol=0.2) + # but this should pass because we no longer subtract the mean. they should be coherent + assert np.isclose(rm.calc_time_resids().std(), 1 * u.us, rtol=0.2) + + @pytest.mark.parametrize( "t1,t2", [ From 1f54b5399d5522223fe60dcabc0072ac9fbed490 Mon Sep 17 00:00:00 2001 From: David Kaplan Date: Mon, 12 Feb 2024 10:22:46 -0600 Subject: [PATCH 03/13] made keyword-only --- src/pint/simulation.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pint/simulation.py b/src/pint/simulation.py index 3dc136a07..6d9ca48db 100644 --- a/src/pint/simulation.py +++ b/src/pint/simulation.py @@ -22,7 +22,7 @@ ] -def zero_residuals(ts, model, subtract_mean=True, maxiter=10, tolerance=None): +def zero_residuals(ts, model, *, subtract_mean=True, maxiter=10, tolerance=None): """Use a model to adjust a TOAs object, setting residuals to 0 iteratively. Parameters @@ -31,6 +31,8 @@ def zero_residuals(ts, model, subtract_mean=True, maxiter=10, tolerance=None): Input TOAs (modified in-place) model : pint.models.timing_model.TimingModel current model + subtract_mean : bool, optional + Controls whether mean will be subtracted from the residuals when making fake TOAs maxiter : int, optional maximum number of iterations allowed tolerance : astropy.units.Quantity From 34f0c089790abd82b8aed457270d3a9a1d057f69 Mon Sep 17 00:00:00 2001 From: David Kaplan Date: Mon, 12 Feb 2024 13:28:19 -0600 Subject: [PATCH 04/13] added type annotations --- src/pint/simulation.py | 155 ++++++++++++++++++++++++++--------------- 1 file changed, 99 insertions(+), 56 deletions(-) diff --git a/src/pint/simulation.py b/src/pint/simulation.py index 6d9ca48db..ae5a73cfe 100644 --- a/src/pint/simulation.py +++ b/src/pint/simulation.py @@ -2,6 +2,8 @@ """ from collections import OrderedDict from copy import deepcopy +from typing import Optional, Union, List, Dict +import pathlib import astropy.units as u import numpy as np @@ -22,7 +24,14 @@ ] -def zero_residuals(ts, model, *, subtract_mean=True, maxiter=10, tolerance=None): +def zero_residuals( + ts: pint.toa.TOAs, + model: pint.models.timing_model.TimingModel, + *, + subtract_mean: bool = True, + maxiter: int = 10, + tolerance: Optional[u.Quantity] = None, +): """Use a model to adjust a TOAs object, setting residuals to 0 iteratively. Parameters @@ -62,7 +71,11 @@ def zero_residuals(ts, model, *, subtract_mean=True, maxiter=10, tolerance=None) ) -def get_fake_toa_clock_versions(model, include_bipm=False, include_gps=True): +def get_fake_toa_clock_versions( + model: pint.models.timing_model.TimingModel, + include_bipm: bool = False, + include_gps: bool = True, +) -> dict: """Get the clock settings (corrections, etc) for fake TOAs Parameters @@ -75,6 +88,10 @@ def get_fake_toa_clock_versions(model, include_bipm=False, include_gps=True): include_gps : bool, optional Whether or not to disable UTC(GPS)->UTC clock correction (see :class:`pint.observatory.topo_obs.TopoObs`) + + Returns + ------- + dict """ bipm_version = bipm_default if model["CLOCK"].value is not None: @@ -109,13 +126,13 @@ def get_fake_toa_clock_versions(model, include_bipm=False, include_gps=True): def make_fake_toas( - ts, - model, - add_noise=False, - add_correlated_noise=False, - name="fake", - subtract_mean=True, -): + ts: pint.toa.TOAs, + model: pint.models.timing_model.TimingModel, + add_noise: bool = False, + add_correlated_noise: bool = False, + name: str = "fake", + subtract_mean: bool = True, +) -> pint.toa.TOAs: """Make toas from an array of times Can include alternating frequencies if fed an array of frequencies, @@ -166,8 +183,23 @@ def make_fake_toas( return tsim -def update_fake_dms(model, ts, dm_error, add_noise): - """Update simulated wideband DM information in TOAs.""" +def update_fake_dms( + model: pint.models.timing_model.TimingModel, + ts: pint.toa.TOAs, + dm_error: u.Quantity, + add_noise: bool, +) -> pint.toa.TOAs: + """Update simulated wideband DM information in TOAs. + + Parameters + ---------- + model: pint.models.timing_model.TimingModel + ts : pint.toa.TOAs + Input TOAs + dm_error: u.Quantity + add_noise : bool, optional + Add noise to the DMs (otherwise `dm_error` just populates the column) + """ toas = deepcopy(ts) dm_errors = dm_error * np.ones(len(toas)) @@ -187,25 +219,25 @@ def update_fake_dms(model, ts, dm_error, add_noise): def make_fake_toas_uniform( - startMJD, - endMJD, - ntoas, - model, - fuzz=0, - freq=1400 * u.MHz, - obs="GBT", - error=1 * u.us, - add_noise=False, - add_correlated_noise=False, - wideband=False, - wideband_dm_error=1e-4 * pint.dmu, - name="fake", - include_bipm=False, - include_gps=True, - multi_freqs_in_epoch=False, - flags=None, - subtract_mean=True, -): + startMJD: float | u.Quantity | time.Time, + endMJD: float | u.Quantity | time.Time, + ntoas: int, + model: pint.models.timing_model.TimingModel, + fuzz: u.Quantity = 0, + freq: u.Quantity = 1400 * u.MHz, + obs: str = "GBT", + error: u.Quantity = 1 * u.us, + add_noise: bool = False, + add_correlated_noise: bool = False, + wideband: bool = False, + wideband_dm_error: u.Quantity = 1e-4 * pint.dmu, + name: str = "fake", + include_bipm: bool = False, + include_gps: bool = True, + multi_freqs_in_epoch: bool = False, + flags: Optional[dict] = None, + subtract_mean: bool = True, +) -> pint.toa.TOAs: """Simulate uniformly spaced TOAs. Parameters @@ -329,22 +361,22 @@ def make_fake_toas_uniform( def make_fake_toas_fromMJDs( - MJDs, - model, - freq=1400 * u.MHz, - obs="GBT", - error=1 * u.us, - add_noise=False, - add_correlated_noise=False, - wideband=False, - wideband_dm_error=1e-4 * pint.dmu, - name="fake", - include_bipm=False, - include_gps=True, - multi_freqs_in_epoch=False, - flags=None, - subtract_mean=True, -): + MJDs: u.Quantity | time.Time | np.ndarray, + model: pint.models.timing_model.TimingModel, + freq: u.Quantity = 1400 * u.MHz, + obs: str = "GBT", + error: u.Quantity = 1 * u.us, + add_noise: bool = False, + add_correlated_noise: bool = False, + wideband: bool = False, + wideband_dm_error: u.Quantity = 1e-4 * pint.dmu, + name: str = "fake", + include_bipm: bool = False, + include_gps: bool = True, + multi_freqs_in_epoch: bool = False, + flags: Optional[dict] = None, + subtract_mean: bool = True, +) -> pint.toa.TOAs: """Simulate TOAs from a list of MJDs Parameters @@ -464,13 +496,13 @@ def make_fake_toas_fromMJDs( def make_fake_toas_fromtim( - timfile, - model, - add_noise=False, - add_correlated_noise=False, - name="fake", - subtract_mean=True, -): + timfile: str | List[str] | pathlib.Path, + model: pint.models.timing_model.TimingModel, + add_noise: bool = False, + add_correlated_noise: bool = False, + name: str = "fake", + subtract_mean: bool = True, +) -> pint.toa.TOAs: """Simulate fake TOAs with the same times as an input tim file Parameters @@ -526,8 +558,13 @@ def make_fake_toas_fromtim( def calculate_random_models( - fitter, toas, Nmodels=100, keep_models=True, return_time=False, params="all" -): + fitter: pint.fitter.Fitter, + toas: pint.toa.TOAs, + Nmodels: int = 100, + keep_models: bool = True, + return_time: bool = False, + params: str = "all", +) -> (np.ndarray, Optional[list]): """ Calculates random models based on the covariance matrix of the `fitter` object. @@ -650,7 +687,13 @@ def calculate_random_models( return (dphase, random_models) if keep_models else dphase -def _get_freqs_and_times(start, end, ntoas, freqs, multi_freqs_in_epoch=True): +def _get_freqs_and_times( + start: float | u.Quantity | time.Time, + end: float | u.Quantity | time.Time, + ntoas: int, + freqs: u.Quantity, + multi_freqs_in_epoch: bool = True, +) -> (float | u.Quantity | time.Time, np.ndarray): freqs = np.atleast_1d(freqs) assert ( len(freqs.shape) == 1 and len(freqs) <= ntoas From bdd5eae10cac68b7bacbbaee2a9bba87d98c0d73 Mon Sep 17 00:00:00 2001 From: David Kaplan Date: Mon, 12 Feb 2024 13:45:11 -0600 Subject: [PATCH 05/13] import annotations --- src/pint/simulation.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pint/simulation.py b/src/pint/simulation.py index ae5a73cfe..23e0df768 100644 --- a/src/pint/simulation.py +++ b/src/pint/simulation.py @@ -1,8 +1,9 @@ """Functions related to simulating TOAs and models """ +from __future__ import annotations from collections import OrderedDict from copy import deepcopy -from typing import Optional, Union, List, Dict +from typing import Optional, List import pathlib import astropy.units as u From c8ccf58dcbfdae4f7f30c63d86e1d95c9e7a36e3 Mon Sep 17 00:00:00 2001 From: Anne Archibald Date: Wed, 14 Feb 2024 19:24:46 +0000 Subject: [PATCH 06/13] report out of order entries --- .pre-commit-config.yaml | 4 ++-- src/pint/observatory/clock_file.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 024cb11be..6c2c4b8cc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,11 +1,11 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: check-yaml - id: check-merge-conflict - id: check-symlinks - repo: https://github.com/psf/black - rev: 23.1.0 + rev: 24.2.0 hooks: - id: black diff --git a/src/pint/observatory/clock_file.py b/src/pint/observatory/clock_file.py index 2d3fe0667..8379d9051 100644 --- a/src/pint/observatory/clock_file.py +++ b/src/pint/observatory/clock_file.py @@ -103,8 +103,9 @@ def __init__( raise ValueError(f"MJDs have {len(mjd)} entries but clock has {len(clock)}") self._time = Time(mjd, format="pulsar_mjd", scale="utc") if not np.all(np.diff(self._time.mjd) >= 0): + i = np.where(np.diff(self._time.mjd) < 0)[0][0] raise ValueError( - f"Clock file {self.friendly_name} appears to be out of order" + f"Clock file {self.friendly_name} appears to be out of order: {self._time[i]} > {self._time[i+1]}" ) self._clock = clock.to(u.us) if comments is None: From aae809903f338874d7b1b032162c5e5a2e544a3d Mon Sep 17 00:00:00 2001 From: Anne Archibald Date: Sat, 17 Feb 2024 11:38:41 +0000 Subject: [PATCH 07/13] Report out of order entries --- CHANGELOG-unreleased.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG-unreleased.md b/CHANGELOG-unreleased.md index 5ac8acfd0..7dff6bb67 100644 --- a/CHANGELOG-unreleased.md +++ b/CHANGELOG-unreleased.md @@ -12,6 +12,7 @@ the released changes. - Moved `get_derived_params` to `timing_model` - `check_ephemeris_connection` CI test no longer requires access to static NANOGrav site - `TimingModel.compare()` now calls `change_binary_epoch()`. +- When clock files contain out-of-order entries, the exception now records the first MJDs that are out of order ### Added - Added numdifftools to setup.cfg to match requirements.txt - Documentation: Added `convert_parfile` to list of command-line tools in RTD From 8b7f6c148c3af03424bf38a99437be24bf2c61b4 Mon Sep 17 00:00:00 2001 From: Anne Archibald Date: Sat, 17 Feb 2024 11:51:49 +0000 Subject: [PATCH 08/13] Test out-of-order reporting --- tests/test_clock_file.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/tests/test_clock_file.py b/tests/test_clock_file.py index 525fab054..451d306c0 100644 --- a/tests/test_clock_file.py +++ b/tests/test_clock_file.py @@ -107,7 +107,8 @@ def test_merge_mjds_trims_range(): ca = ClockFile(mjd=a, clock=np.zeros_like(a) * u.s) cb = ClockFile(mjd=b, clock=np.zeros_like(b) * u.s) - m = ClockFile.merge([ca, cb]) + with pytest.warns(UserWarning, match="out of range"): + m = ClockFile.merge([ca, cb]) assert_array_equal(m.time.mjd, np.array([50000, 55000, 60000])) @@ -118,7 +119,8 @@ def test_merge_mjds_trims_range_repeat_beginning(): ca = ClockFile(mjd=a, clock=np.zeros_like(a) * u.s) cb = ClockFile(mjd=b, clock=np.zeros_like(b) * u.s) - m = ClockFile.merge([ca, cb]) + with pytest.warns(UserWarning, match="out of range"): + m = ClockFile.merge([ca, cb]) assert_array_equal(m.time.mjd, np.array([50000, 50000, 55000, 60000])) @@ -129,7 +131,8 @@ def test_merge_mjds_trims_range_repeat_end(): ca = ClockFile(mjd=a, clock=np.zeros_like(a) * u.s) cb = ClockFile(mjd=b, clock=np.zeros_like(b) * u.s) - m = ClockFile.merge([ca, cb]) + with pytest.warns(UserWarning, match="out of range"): + m = ClockFile.merge([ca, cb]) assert_array_equal(m.time.mjd, np.array([50000, 55000, 60000, 60000])) @@ -139,7 +142,8 @@ def test_merge_mjds_trims_range_mixed(): ca = ClockFile(mjd=a, clock=np.zeros_like(a) * u.s) cb = ClockFile(mjd=b, clock=np.zeros_like(b) * u.s) - m = ClockFile.merge([ca, cb]) + with pytest.warns(UserWarning, match="out of range"): + m = ClockFile.merge([ca, cb]) assert_array_equal(m.time.mjd, np.array([50000, 55000, 60000])) @@ -469,3 +473,13 @@ def test_out_of_range_allowed(): valid_beyond_ends=True, ) basic_clock.evaluate(Time(60001, format="mjd"), limits="error") + + +def test_out_of_order_raises_exception(): + with pytest.raises(ValueError) as excinfo: + ClockFile( + mjd=np.array([50000, 55000, 54000, 60000]), + clock=np.array([1.0, 2.0, -1.0, 1.0]) * u.us, + friendly_name="basic_clock", + ) + assert "55000" in str(excinfo.value) From 154478785db754421c68ab9fd97eb9109d38d0e4 Mon Sep 17 00:00:00 2001 From: David Kaplan Date: Sat, 17 Feb 2024 18:42:44 -0600 Subject: [PATCH 09/13] typing | -> Union --- src/pint/simulation.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/pint/simulation.py b/src/pint/simulation.py index 23e0df768..9f3f76627 100644 --- a/src/pint/simulation.py +++ b/src/pint/simulation.py @@ -1,9 +1,9 @@ """Functions related to simulating TOAs and models """ -from __future__ import annotations + from collections import OrderedDict from copy import deepcopy -from typing import Optional, List +from typing import Optional, List, Union import pathlib import astropy.units as u @@ -220,8 +220,8 @@ def update_fake_dms( def make_fake_toas_uniform( - startMJD: float | u.Quantity | time.Time, - endMJD: float | u.Quantity | time.Time, + startMJD: Union[float, u.Quantity, time.Time], + endMJD: Union[float, u.Quantity, time.Time], ntoas: int, model: pint.models.timing_model.TimingModel, fuzz: u.Quantity = 0, @@ -362,7 +362,7 @@ def make_fake_toas_uniform( def make_fake_toas_fromMJDs( - MJDs: u.Quantity | time.Time | np.ndarray, + MJDs: Union[u.Quantity, time.Time, np.ndarray], model: pint.models.timing_model.TimingModel, freq: u.Quantity = 1400 * u.MHz, obs: str = "GBT", @@ -497,7 +497,7 @@ def make_fake_toas_fromMJDs( def make_fake_toas_fromtim( - timfile: str | List[str] | pathlib.Path, + timfile: Union[str, List[str], pathlib.Path], model: pint.models.timing_model.TimingModel, add_noise: bool = False, add_correlated_noise: bool = False, @@ -689,12 +689,12 @@ def calculate_random_models( def _get_freqs_and_times( - start: float | u.Quantity | time.Time, - end: float | u.Quantity | time.Time, + start: Union[float, u.Quantity, time.Time], + end: Union[float, u.Quantity, time.Time], ntoas: int, freqs: u.Quantity, multi_freqs_in_epoch: bool = True, -) -> (float | u.Quantity | time.Time, np.ndarray): +) -> (Union[float, u.Quantity, time.Time], np.ndarray): freqs = np.atleast_1d(freqs) assert ( len(freqs.shape) == 1 and len(freqs) <= ntoas From 83d556424fb6eb8740e2fa312c33dd16ec78c626 Mon Sep 17 00:00:00 2001 From: David Kaplan Date: Mon, 19 Feb 2024 09:42:55 -0600 Subject: [PATCH 10/13] added import of pint.fitter --- src/pint/simulation.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pint/simulation.py b/src/pint/simulation.py index 23e0df768..f45fc06ec 100644 --- a/src/pint/simulation.py +++ b/src/pint/simulation.py @@ -1,5 +1,6 @@ """Functions related to simulating TOAs and models """ + from __future__ import annotations from collections import OrderedDict from copy import deepcopy @@ -13,6 +14,7 @@ import pint.residuals import pint.toa +import pint.fitter from pint.observatory import bipm_default, get_observatory __all__ = [ From 66d2c384b2810b6a47e766d36e8b9a652f52781f Mon Sep 17 00:00:00 2001 From: Scott Ransom Date: Mon, 19 Feb 2024 14:45:07 -0500 Subject: [PATCH 11/13] Added ability to add very old TOAs in Princeton format that use shortened MJD values --- src/pint/toa.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/pint/toa.py b/src/pint/toa.py index 682ca60ce..a8774acbf 100644 --- a/src/pint/toa.py +++ b/src/pint/toa.py @@ -492,7 +492,11 @@ def _parse_TOA_line(line, fmt="Unknown"): d["freq"] = float(line[15:24]) d["error"] = float(line[44:53]) ii, ff = line[24:44].split(".") - MJD = (int(ii), float(f"0.{ff}")) + ii = int(ii) + # For very old TOAs, see https://tempo.sourceforge.net/ref_man_sections/toa.txt + if ii < 40000: + ii += 39126 + MJD = (ii, float(f"0.{ff}")) try: d["ddm"] = str(float(line[68:78])) except ValueError: From 73efb90529673dda74035ddf8f18505c8eeedb2c Mon Sep 17 00:00:00 2001 From: David Kaplan Date: Wed, 21 Feb 2024 10:30:43 -0600 Subject: [PATCH 12/13] added spacecraft as alias for geocenter --- CHANGELOG-unreleased.md | 1 + src/pint/observatory/special_locations.py | 3 ++- tests/test_toa_create.py | 18 ++++++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/CHANGELOG-unreleased.md b/CHANGELOG-unreleased.md index 5ac8acfd0..7a96e7931 100644 --- a/CHANGELOG-unreleased.md +++ b/CHANGELOG-unreleased.md @@ -43,4 +43,5 @@ the released changes. - Better exceptions for unsupported/unimplemented binary models (BTX, MSS, etc.) - Emit warnings when `WaveX`/`DMWaveX` is used together with other representations of red/DM noise - `get_observatory()` no longer overwrites `include_gps` and `include_bipm` of `Observatory` objects unless explicitly stated (BIPM and GPS clock corrections no longer incorrectly applied to BAT TOAs). +- Added back `spacecraft` as an alias for `stl_geo` ### Removed diff --git a/src/pint/observatory/special_locations.py b/src/pint/observatory/special_locations.py index b5b56047c..d5741e9f6 100644 --- a/src/pint/observatory/special_locations.py +++ b/src/pint/observatory/special_locations.py @@ -10,6 +10,7 @@ -------- :mod:`pint.observatory.topo_obs` """ + import astropy.units as u import numpy as np from astropy.coordinates import EarthLocation @@ -258,7 +259,7 @@ def load_special_locations(): # Need to initialize one of each so that it gets added to the list BarycenterObs("barycenter", aliases=["@", "ssb", "bary", "bat"], overwrite=True) GeocenterObs("geocenter", aliases=["0", "o", "coe", "geo"], overwrite=True) - T2SpacecraftObs("stl_geo", aliases=["STL_GEO"], overwrite=True) + T2SpacecraftObs("stl_geo", aliases=["STL_GEO", "spacecraft"], overwrite=True) # TODO -- How to handle user changing bipm_version? diff --git a/tests/test_toa_create.py b/tests/test_toa_create.py index 3c9c63c5d..0cc024910 100644 --- a/tests/test_toa_create.py +++ b/tests/test_toa_create.py @@ -266,6 +266,24 @@ def test_toas_fermi_notoalist(): assert np.all(toas.table == toas2.table) +def test_geocenter(): + toas = toa.get_TOAs( + io.StringIO( + """pint 0.000000 57932.5446286608075925 4.321 stl_geo -format Tempo2 -creator nicer -mjdtt 57932.5454294 -tely -2783.63069101 -telx 6119.42973459 -telz -890.7794845 -nsrc 18.34 -obsid 70020101 -t NICER -vy 4.04867262 -vx 2.69970132 -dphi 0.20496 -nbkg 430.66 -vz 5.92980989 -exposure 1543.0 +pint 0.000000 57933.6326461481560532 2.422 stl_geo -format Tempo2 -creator nicer -mjdtt 57933.6334469 -tely -4545.24145649 -telx 3799.22279181 -telz -3304.56529985 -nsrc 101.96 -obsid 70020102 -t NICER -vy 1.48403228 -vx 5.86477721 -dphi 0.20355 -nbkg 2394.04 -vz 4.70930058 -exposure 8100.71 +""" + ) + ) + toas2 = toa.get_TOAs( + io.StringIO( + """pint 0.000000 57932.5446286608075925 4.321 spacecraft -format Tempo2 -creator nicer -mjdtt 57932.5454294 -tely -2783.63069101 -telx 6119.42973459 -telz -890.7794845 -nsrc 18.34 -obsid 70020101 -t NICER -vy 4.04867262 -vx 2.69970132 -dphi 0.20496 -nbkg 430.66 -vz 5.92980989 -exposure 1543.0 +pint 0.000000 57933.6326461481560532 2.422 spacecraft -format Tempo2 -creator nicer -mjdtt 57933.6334469 -tely -4545.24145649 -telx 3799.22279181 -telz -3304.56529985 -nsrc 101.96 -obsid 70020102 -t NICER -vy 1.48403228 -vx 5.86477721 -dphi 0.20355 -nbkg 2394.04 -vz 4.70930058 -exposure 8100.71 +""" + ) + ) + assert np.all(toas == toas2) + + def test_toa_wb(): t = pulsar_mjd.Time(np.array([55000, 56000]), scale="utc", format="pulsar_mjd") obs = "gbt" From 327d92e08a180014ced48982f06a2832fbf47a80 Mon Sep 17 00:00:00 2001 From: Scott Ransom Date: Wed, 21 Feb 2024 15:17:03 -0500 Subject: [PATCH 13/13] Added tests for reading Princeton-style TOAs, including with offset MJD --- tests/test_toa_reader.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/test_toa_reader.py b/tests/test_toa_reader.py index 77c7e04f2..cf037f555 100644 --- a/tests/test_toa_reader.py +++ b/tests/test_toa_reader.py @@ -73,6 +73,25 @@ def test_read_parkes(self): assert "barycenter" in ts.observatories assert ts.ntoas == 8 + def test_read_princeton(self): + toaline = "6 11 1744-24 1666.0000 50852.5886574493316 25.6 8-FEB-98 0.0 1 1" + mjd, d = toa._parse_TOA_line(toaline) + assert np.isclose(mjd[0] + mjd[1], 50852.5886574493316) + assert d["format"] == "Princeton" + assert d["obs"] == "vla" + assert d["freq"] == 1666.0 + assert d["error"] == 25.6 + + def test_read_princeton_offset1(self): + toaline = "6 26 1744-24 1667.0000 9064.9010416342947 6.2 26-OCT-90 0.1 1 1" + mjd, d = toa._parse_TOA_line(toaline) + assert np.isclose(mjd[0] + mjd[1], 48190.9010416342947) + + def test_read_princeton_offset2(self): + toaline = "6 37 1744-24 1666.000010062.1379629521572 7.8 20-JUL-93 64.6 1 1" + mjd, d = toa._parse_TOA_line(toaline) + assert np.isclose(mjd[0] + mjd[1], 49188.1379629521572) + def test_read_parkes_phaseoffset(self): # Fourth column contains non-zero phase offset toaline = " PUPPI_J2044+28_58852_652 432.3420 58852.7590686063892 1.00 120.75 @"