Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enterprise falls back to PINT if no TEMPO2 #340

Open
wants to merge 30 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
29cc262
Make test suite work without tempo2
Feb 10, 2023
1eabe47
Tidy code
Feb 10, 2023
3302266
Separate T2-model tests so others can be run
Feb 10, 2023
ea0b698
Parametrize tests so they run on PINT and tempo2
Feb 10, 2023
8ca41be
Make code analysis tools happier
Feb 11, 2023
85aa6d2
Arrange for no-tempo test run
Feb 11, 2023
a23d08a
Try to work around absence of tempo2 for no-tempo
Feb 11, 2023
a66f7d0
Undo attempt to avoid installing libstempo
Feb 11, 2023
c097248
Try just uninstallibg libstempo
Feb 11, 2023
dc24e64
Add -y to pip uninstall
Feb 11, 2023
d62f136
Add tests to cover whole patch
Feb 13, 2023
9bec1d8
Tidy and test PINT-interfacing code
Feb 13, 2023
16edb95
Fix wrong input test
Feb 13, 2023
c309d35
Note about over-specific test
Feb 13, 2023
61cf782
Less specific test
Feb 13, 2023
25874aa
Includr no_pint test
Feb 13, 2023
a5f0ec3
Try to make no_pint run pass
Feb 13, 2023
ce45bcf
Handle missing and old PINT
Feb 13, 2023
8658f17
Lower required coverage percentage for local tests
Feb 13, 2023
d9bdc32
Fix timing_package defaulting
Feb 13, 2023
393d12a
Skip more tests if PINT not available
Feb 13, 2023
8bb1294
Better message when arguments missing
Feb 13, 2023
3411b35
Tidy argument processing
Feb 13, 2023
b774ad6
Improve error handling in Pulsar
Feb 14, 2023
97a9c6e
Ensure all PINT tests are conditional on PINT
Feb 14, 2023
f630468
Delete leftover file
Feb 14, 2023
189a32e
Skip pulsar tests if no PINT
Feb 14, 2023
177b3c5
Ensure .pkl file is removed
Feb 14, 2023
31ddf15
Explain extra requirements.
Feb 20, 2023
bcd3892
Blacken
Feb 20, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions .github/workflows/ci_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,71 @@ jobs:
#with:
# fail_ci_if_error: true

test_no_tempo2:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Set up Python 3.10
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install non-python dependencies
run: |
sudo apt-get install libsuitesparse-dev
curl -sSL https://raw.githubusercontent.com/vallis/libstempo/master/install_tempo2.sh | sh
- name: Install dependencies and package
run: |
python -m pip install --upgrade pip setuptools wheel
python -m pip install -r requirements_dev.txt
python -m pip install -e .
python -m pip uninstall -y libstempo
- name: Display Python, pip, setuptools, and all installed versions
run: |
python -c "import sys; print(f'Python {sys.version}')"
python -c "import pip; print(f'pip {pip.__version__}')"
python -c "import setuptools; print(f'setuptools {setuptools.__version__}')"
python -m pip freeze
- name: Run lint
run: make lint
- name: Test with pytest
run: make test
- name: Codecov
uses: codecov/codecov-action@v3

test_no_pint:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Set up Python 3.10
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install non-python dependencies
run: |
sudo apt-get install libsuitesparse-dev
curl -sSL https://raw.githubusercontent.com/vallis/libstempo/master/install_tempo2.sh | sh
- name: Install dependencies and package
run: |
python -m pip install --upgrade pip setuptools wheel
python -m pip install -r requirements_dev.txt
python -m pip install -e .
python -m pip uninstall -y pint-pulsar
- name: Display Python, pip, setuptools, and all installed versions
run: |
python -c "import sys; print(f'Python {sys.version}')"
python -c "import pip; print(f'pip {pip.__version__}')"
python -c "import setuptools; print(f'setuptools {setuptools.__version__}')"
python -m pip freeze
- name: Run lint
run: make lint
- name: Test with pytest
run: make test
- name: Codecov
uses: codecov/codecov-action@v3

build:
needs: [tests]
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ clean-test: ## remove test and coverage artifacts
rm -fr htmlcov/
rm -rf coverage.xml

COV_COVERAGE_PERCENT ?= 85
COV_COVERAGE_PERCENT ?= 75
test: lint ## run tests quickly with the default Python
pytest -v --durations=10 --full-trace --cov-report html --cov-report xml \
--cov-config .coveragerc --cov-fail-under=$(COV_COVERAGE_PERCENT) \
Expand Down
88 changes: 51 additions & 37 deletions enterprise/pulsar.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@

try:
import libstempo as t2
except ImportError:
logger.warning("libstempo not installed. Will use PINT instead.") # pragma: no cover
except (ImportError, RuntimeError) as e:
# ImportError happens if libstempo isn't installed
# RuntimeError happens if libstempo is installed but can't find TEMPO2
logger.warning(f"Unable to use TEMPO2: {e} Will use PINT instead.")
t2 = None

try:
Expand All @@ -31,7 +33,7 @@
from pint.residuals import Residuals as resids
from pint.toa import TOAs
except ImportError:
logger.warning("PINT not installed. Will use libstempo instead.") # pragma: no cover
logger.warning("PINT not installed. Will use libstempo instead.")
pint = None

if pint is None and t2 is None:
Expand Down Expand Up @@ -327,20 +329,17 @@ def __init__(self, toas, model, sort=True, drop_pintpsr=True, planets=True):
self._stoas = np.array(toas.get_mjds().value, dtype="float64") * 86400
self._residuals = np.array(resids(toas, model).time_resids.to(u.s), dtype="float64")
self._toaerrs = np.array(toas.get_errors().to(u.s), dtype="float64")
self._designmatrix = model.designmatrix(toas)[0]
self._designmatrix, self.fitpars, units = model.designmatrix(toas)
self._ssbfreqs = np.array(model.barycentric_radio_freq(toas), dtype="float64")
self._telescope = np.array(toas.get_obss())

# fitted parameters
self.fitpars = ["Offset"] + [par for par in model.params if not getattr(model, par).frozen]

# gather DM/DMX information if available
self._set_dm(model)

# set parameters
spars = [par for par in model.params]
self.setpars = [sp for sp in spars if sp not in self.fitpars]
self.setpars = [sp for sp in model.params if sp not in self.fitpars]

# FIXME: this can be done more cleanly using PINT
self._flags = {}
for ii, obsflags in enumerate(toas.get_flags()):
for jj, flag in enumerate(obsflags):
Expand All @@ -351,6 +350,7 @@ def __init__(self, toas, model, sort=True, drop_pintpsr=True, planets=True):

# convert flags to arrays
# TODO probably better way to do this
# in fact PINT always stores flags as strings
for key, val in self._flags.items():
if isinstance(val[0], u.quantity.Quantity):
self._flags[key] = np.array([v.value for v in val])
Expand Down Expand Up @@ -557,8 +557,8 @@ def _get_sunssb(self, t2pulsar):
sunssb = None
if self.planets:
# for ii in range(1, 10):
# tag = 'DMASSPLANET' + str(ii)
# self.t2pulsar[tag].val = 0.0
# tag = 'DMASSPLANET' + str(ii)@pytest.mark.skipif(t2 is None, reason="TEMPO2/libstempo not available")

self.t2pulsar.formbats()
sunssb = np.zeros((len(self._toas), 6))
sunssb[:, :] = self.t2pulsar.sun_ssb
Expand All @@ -570,6 +570,7 @@ def _get_sunssb(self, t2pulsar):

# infrastructure for sharing Pulsar objects among processes
# (currently Tempo2Pulsar only)
# FIXME: why?
# the Pulsar deflater will copy select numpy arrays to SharedMemory,
# then replace them with pickleable objects that can be inflated
# to numpy arrays with SharedMemory storage
Expand Down Expand Up @@ -603,14 +604,16 @@ def destroy(psr): # pragma: py-lt-38


def Pulsar(*args, **kwargs):
ephem = kwargs.get("ephem", None)
clk = kwargs.get("clk", None)
bipm_version = kwargs.get("bipm_version", None)
ephem = kwargs.get("ephem")
clk = kwargs.get("clk")
bipm_version = kwargs.get("bipm_version")
planets = kwargs.get("planets", True)
sort = kwargs.get("sort", True)
drop_t2pulsar = kwargs.get("drop_t2pulsar", True)
drop_pintpsr = kwargs.get("drop_pintpsr", True)
timing_package = kwargs.get("timing_package", "tempo2")
timing_package = kwargs.get("timing_package")
if timing_package is not None:
timing_package = timing_package.lower()

if pint is not None:
toas = [x for x in args if isinstance(x, TOAs)]
Expand Down Expand Up @@ -638,28 +641,39 @@ def Pulsar(*args, **kwargs):
reltimfile = timfiletup[-1]
relparfile = os.path.relpath(parfile[0], dirname)

if timing_package is None:
if t2 is not None:
timing_package = "tempo2"
elif pint is not None:
timing_package = "pint"
else:
raise ValueError("No timing package available with which to load a pulsar")

# get current directory
cwd = os.getcwd()

# Change directory to the base directory of the tim-file to deal with
# INCLUDE statements in the tim-file
os.chdir(dirname)

if timing_package.lower() == "pint":
if (clk is not None) and (bipm_version is None):
bipm_version = clk.split("(")[1][:-1]
model, toas = get_model_and_toas(
relparfile, reltimfile, ephem=ephem, bipm_version=bipm_version, planets=planets
)
os.chdir(cwd)
return PintPulsar(toas, model, sort=sort, drop_pintpsr=drop_pintpsr, planets=planets)

elif timing_package.lower() == "tempo2":

# hack to set maxobs
maxobs = get_maxobs(reltimfile) + 100
t2pulsar = t2.tempopulsar(relparfile, reltimfile, maxobs=maxobs, ephem=ephem, clk=clk)
try:
# Change directory to the base directory of the tim-file to deal with
# INCLUDE statements in the tim-file
os.chdir(dirname)
if timing_package == "tempo2":
if t2 is None:
raise ValueError("tempo2 requested but tempo2 is not available")
# hack to set maxobs
maxobs = get_maxobs(reltimfile) + 100
t2pulsar = t2.tempopulsar(relparfile, reltimfile, maxobs=maxobs, ephem=ephem, clk=clk)
return Tempo2Pulsar(t2pulsar, sort=sort, drop_t2pulsar=drop_t2pulsar, planets=planets)
elif timing_package.lower() == "pint":
if pint is None:
raise ValueError("PINT requested but PINT is not available")
if (clk is not None) and (bipm_version is None):
bipm_version = clk.split("(")[1][:-1]
model, toas = get_model_and_toas(
relparfile, reltimfile, ephem=ephem, bipm_version=bipm_version, planets=planets
)
os.chdir(cwd)
return PintPulsar(toas, model, sort=sort, drop_pintpsr=drop_pintpsr, planets=planets)
else:
raise ValueError(f"Unknown timing package {timing_package}")
finally:
os.chdir(cwd)
return Tempo2Pulsar(t2pulsar, sort=sort, drop_t2pulsar=drop_t2pulsar, planets=planets)

raise ValueError("Unknown arguments {}".format(args))
raise ValueError("Pulsar (par/tim) not specified in {args} or {kwargs}")
3 changes: 3 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
"libstempo>=2.4.4",
]

# We could add optional requirements here - pip install enterprise[pint,tempo2]
# Sadly you can't have default-on extra requirements.

test_requirements = []

setup(
Expand Down
4 changes: 3 additions & 1 deletion tests/test_deterministic_signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@
import unittest

import numpy as np
import pytest

import enterprise
from enterprise.pulsar import Pulsar
from enterprise.pulsar import Pulsar, pint
from enterprise.signals import deterministic_signals, parameter, selections, utils
from enterprise.signals.parameter import function
from enterprise.signals.selections import Selection
Expand Down Expand Up @@ -259,6 +260,7 @@ def test_physical_ephem_model_setIII(self):
assert np.allclose(d1, d2, rtol=1e-10), msg2


@pytest.mark.skipif(pint is None, reason="PINT not available")
class TestDeterministicSignalsPint(TestDeterministicSignals):
"""Tests deterministic signals with a PINT Pulsar object."""

Expand Down
4 changes: 3 additions & 1 deletion tests/test_gp_coefficients.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@
import unittest

import numpy as np
import pytest
import scipy.sparse as sps
from sksparse.cholmod import cholesky

from enterprise.pulsar import Pulsar
from enterprise.pulsar import Pulsar, pint
from enterprise.signals import (
deterministic_signals,
gp_signals,
Expand Down Expand Up @@ -379,6 +380,7 @@ def test_conditional_gp(self):
assert np.allclose(mn[idx[c_name]], v)


@pytest.mark.skipif(pint is None, reason="PINT not available")
class TestGPCoefficientsPint(TestGPCoefficients):
@classmethod
def setUpClass(cls):
Expand Down
2 changes: 1 addition & 1 deletion tests/test_gp_priors.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
Tests for GP priors and bases.
"""


import unittest

import numpy as np

from tests.enterprise_test_data import datadir
Expand Down
4 changes: 3 additions & 1 deletion tests/test_gp_signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@
import unittest

import numpy as np
import pytest
import scipy.linalg as sl

from enterprise.pulsar import Pulsar
from enterprise.pulsar import Pulsar, pint
from enterprise.signals import gp_signals, parameter, selections, signal_base, utils
from enterprise.signals.selections import Selection
from tests.enterprise_test_data import datadir
Expand Down Expand Up @@ -711,6 +712,7 @@ def test_combine_signals(self):
assert m.get_basis(params).shape == T.shape, msg


@pytest.mark.skipif(pint is None, reason="PINT not available")
class TestGPSignalsPint(TestGPSignals):
@classmethod
def setUpClass(cls):
Expand Down
4 changes: 3 additions & 1 deletion tests/test_gp_wideband.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@
import unittest

import numpy as np
import pytest

from enterprise.pulsar import Pulsar
from enterprise.pulsar import Pulsar, pint
from enterprise.signals import white_signals, gp_signals, parameter, selections, signal_base
from enterprise.signals.selections import Selection
from tests.enterprise_test_data import datadir
Expand Down Expand Up @@ -145,6 +146,7 @@ def test_wideband(self):
assert np.allclose(dl1, delays), msg


@pytest.mark.skipif(pint is None, reason="PINT not available")
class TestGPSignalsPint(TestWidebandTimingModel):
@classmethod
def setUpClass(cls):
Expand Down
4 changes: 3 additions & 1 deletion tests/test_likelihood.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@
import unittest

import numpy as np
import pytest
import scipy.linalg as sl

from enterprise.pulsar import Pulsar
from enterprise.pulsar import Pulsar, pint
from enterprise.signals import gp_signals, parameter, selections, signal_base, utils, white_signals
from enterprise.signals.selections import Selection
from tests.enterprise_test_data import datadir
Expand Down Expand Up @@ -327,6 +328,7 @@ def test_compare_ecorr_likelihood(self):
assert np.allclose(l1, l2), msg


@pytest.mark.skipif(pint is None, reason="PINT not available")
class TestLikelihoodPint(TestLikelihood):
@classmethod
def setUpClass(cls):
Expand Down
4 changes: 3 additions & 1 deletion tests/test_pta.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@
import unittest

import numpy as np
import pytest

from enterprise.pulsar import Pulsar
from enterprise.pulsar import Pulsar, pint
from enterprise.signals import gp_signals, parameter, signal_base, utils, white_signals

from .enterprise_test_data import datadir
Expand Down Expand Up @@ -329,6 +330,7 @@ def test_summary(self):
assert pta["B1855+09"]["red_noise"] == pta.pulsarmodels[0].signals[0], msg


@pytest.mark.skipif(pint is None, reason="PINT not available")
class TestPTASignalsPint(TestPTASignals):
@classmethod
def setUpClass(cls):
Expand Down
Loading