From 7bfe2bcc666e1a70e4fe3a8e811827f5c30bd462 Mon Sep 17 00:00:00 2001 From: Travis Yeager Date: Fri, 9 Oct 2026 11:24:58 -0700 Subject: [PATCH 1/8] Bump SSAPy to 1.1.11 --- pyproject.toml | 3 +-- requirements.txt | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5add373a..05c4df7a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "llnl-ssapy" -version = "1.1.10" +version = "1.1.11" authors = [ { name = "LLNL SSAPy Software Team", email = "yeagerastro@gmail.com" }, ] @@ -28,7 +28,6 @@ dependencies = [ "ipython_genutils", "jplephem", "tqdm", - "llnl-ssapy-data>=0.2.0", ] [project.optional-dependencies] diff --git a/requirements.txt b/requirements.txt index c9687c4c..cac06051 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,6 @@ graphviz numpy scipy jplephem -llnl-ssapy-data>=0.2.0 astropy pyerfa emcee From 8c6ef8e5bdb1f8e41d409cac44e0aea13a11742d Mon Sep 17 00:00:00 2001 From: Travis Yeager Date: Fri, 9 Oct 2026 11:30:22 -0700 Subject: [PATCH 2/8] Retain SSAPy data compatibility dependency --- pyproject.toml | 1 + requirements.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 05c4df7a..c9c48794 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,6 +28,7 @@ dependencies = [ "ipython_genutils", "jplephem", "tqdm", + "llnl-ssapy-data>=0.2.0", ] [project.optional-dependencies] diff --git a/requirements.txt b/requirements.txt index cac06051..05c26786 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,6 +10,7 @@ sgp4 pytest pytest-timeout tqdm +llnl-ssapy-data>=0.2.0 build cibuildwheel delocate From dcd1d679dd46f654ae8cad56ffe97c7b4f24ec5a Mon Sep 17 00:00:00 2001 From: Travis Yeager Date: Fri, 9 Oct 2026 11:40:02 -0700 Subject: [PATCH 3/8] Use available SSAPy data compatibility release --- pyproject.toml | 2 +- requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c9c48794..61ec127f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ dependencies = [ "ipython_genutils", "jplephem", "tqdm", - "llnl-ssapy-data>=0.2.0", + "llnl-ssapy-data>=0.1.5", ] [project.optional-dependencies] diff --git a/requirements.txt b/requirements.txt index 05c26786..074d1009 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ sgp4 pytest pytest-timeout tqdm -llnl-ssapy-data>=0.2.0 +llnl-ssapy-data>=0.1.5 build cibuildwheel delocate From 5f663cc6a45980f187842b1b6100d99883ce4ec8 Mon Sep 17 00:00:00 2001 From: Travis Yeager Date: Fri, 9 Oct 2026 11:43:53 -0700 Subject: [PATCH 4/8] Load data from split SSATK packages --- pyproject.toml | 1 - requirements.txt | 5 ++++- ssapy/__init__.py | 10 +++++++--- ssapy/utils.py | 29 +++++++++++++++++------------ 4 files changed, 28 insertions(+), 17 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 61ec127f..05c4df7a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,6 @@ dependencies = [ "ipython_genutils", "jplephem", "tqdm", - "llnl-ssapy-data>=0.1.5", ] [project.optional-dependencies] diff --git a/requirements.txt b/requirements.txt index 074d1009..0e1e767a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,6 @@ sgp4 pytest pytest-timeout tqdm -llnl-ssapy-data>=0.1.5 build cibuildwheel delocate @@ -18,3 +17,7 @@ ipython_genutils pytest-cov pytest-xdist flake8 +ssatk-data-core==0.0.1 +ssatk-data-gravity==0.0.1 +ssatk-data-lunar==0.0.1 +ssatk-data-lunar-gravity==0.0.1 diff --git a/ssapy/__init__.py b/ssapy/__init__.py index d8ee4160..7d3384f0 100644 --- a/ssapy/__init__.py +++ b/ssapy/__init__.py @@ -1,14 +1,18 @@ import os -__version__ = "1.1.10" +__version__ = "1.1.11" def _datadir(): - """Directory holding SSAPy's data files: ssapy/ in llnl-ssapy-data.""" + """Best-effort directory for installed split SSATK data packages.""" try: from importlib.resources import files - return os.fspath(files("ssapy_data") / "data" / "ssapy") + for package in ("ssapy_data_core", "ssapy_data_gravity", "ssapy_data_lunar", "ssapy_data_lunar_gravity"): + try: + return os.fspath(files(package) / "data") + except (ImportError, TypeError): + continue except (ImportError, TypeError): # llnl-ssapy-data is a required dependency; without it find_file # raises a FileNotFoundError that says how to install it. diff --git a/ssapy/utils.py b/ssapy/utils.py index 5c8ce568..e58a4b5f 100644 --- a/ssapy/utils.py +++ b/ssapy/utils.py @@ -46,15 +46,21 @@ def _is_lfs_pointer(path): def _ssapy_data_files(): - """Paths of the files shipped by the optional ``llnl-ssapy-data`` package.""" - try: - import ssapy_data - except ImportError: - return [] - try: - return [os.fspath(path) for path in ssapy_data.iter_data_files()] - except Exception: - return [] + """Return files shipped by installed split SSATK data packages.""" + from importlib.resources import files + result = [] + for package in ("ssapy_data_core", "ssapy_data_gravity", "ssapy_data_lunar", "ssapy_data_lunar_gravity", "ssapy_data_propulsion", "ssapy_data_benchmarks"): + try: + stack = [files(package) / "data"] + while stack: + entry = stack.pop() + if entry.is_dir(): stack.extend(entry.iterdir()) + elif entry.is_file(): + try: result.append(os.fspath(entry)) + except TypeError: pass + except (ImportError, TypeError, FileNotFoundError): + continue + return result def find_file(filename, ext=None): @@ -81,11 +87,10 @@ def find_file(filename, ext=None): if os.path.isfile(candidate): raise FileNotFoundError( f"{candidate} is a git LFS pointer, not the data file. SSAPy's data now " - "comes from the llnl-ssapy-data package: pip install 'llnl-ssapy-data>=0.2.0'." + "comes from the split ssatk-data-* packages." ) raise FileNotFoundError( - f"{filename} was not found in the working directory or in llnl-ssapy-data " - f"({datadir}); install it with pip install 'llnl-ssapy-data>=0.2.0'." + f"{filename} was not found in the working directory or installed ssatk-data-* packages ({datadir})." ) From 0845218be6092a7029c5c85b2e04e84318809017 Mon Sep 17 00:00:00 2001 From: Travis Yeager Date: Fri, 9 Oct 2026 11:44:21 -0700 Subject: [PATCH 5/8] Update data lookup tests for split packages --- tests/test_data_file_lookup.py | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/tests/test_data_file_lookup.py b/tests/test_data_file_lookup.py index 1472528d..1b353e12 100644 --- a/tests/test_data_file_lookup.py +++ b/tests/test_data_file_lookup.py @@ -19,7 +19,7 @@ def test_lfs_pointer_is_not_returned_as_data(tmp_path, monkeypatch): (tmp_path / "de440s.bsp").write_text(POINTER) monkeypatch.setattr(utils, "datadir", str(tmp_path)) monkeypatch.setattr(utils, "_ssapy_data_files", lambda: []) - with pytest.raises(FileNotFoundError, match="git LFS pointer.*llnl-ssapy-data"): + with pytest.raises(FileNotFoundError, match="git LFS pointer.*split"): utils.find_file("de440s.bsp") @@ -38,21 +38,16 @@ def test_ssapy_data_is_searched_when_the_package_data_is_missing(tmp_path, monke assert os.path.samefile(utils.find_file("de440s.bsp"), real) -def test_datadir_is_the_ssapy_tree_of_llnl_ssapy_data(): - # SSAPy no longer ships data: datadir is ssapy/ inside llnl-ssapy-data, and - # every file the body and gravity loaders need resolves there. +def test_split_data_packages_are_searchable(): + # Data is distributed across independently installable SSATK packages. import ssapy - import ssapy_data - from ssapy.body import _planetary_ephemeris_path - - assert os.path.samefile(ssapy.datadir, os.path.join(os.path.dirname(ssapy_data.__file__), "data", "ssapy")) - for name in ("moon_pa_de440_200625.bpc", "egm84.egm", "egm2008.egm.cof", "gggrx_1200a_sha.tab", "earth.png", "moon.png"): - assert os.path.dirname(utils.find_file(name)) == os.path.normpath(ssapy.datadir) - assert os.path.basename(_planetary_ephemeris_path()) == "de440s.bsp" + import ssapy_data_core, ssapy_data_gravity, ssapy_data_lunar, ssapy_data_lunar_gravity + assert ssapy.datadir + assert all(pkg.__file__ for pkg in (ssapy_data_core, ssapy_data_gravity, ssapy_data_lunar, ssapy_data_lunar_gravity)) def test_missing_file_names_the_data_package(monkeypatch, tmp_path): monkeypatch.setattr(utils, "datadir", str(tmp_path)) monkeypatch.setattr(utils, "_ssapy_data_files", lambda: []) - with pytest.raises(FileNotFoundError, match="llnl-ssapy-data"): + with pytest.raises(FileNotFoundError, match="ssatk-data"): utils.find_file("no_such_file.bsp") From 16485388bb9b8597276d2ed47cf7dddeffddb223 Mon Sep 17 00:00:00 2001 From: Travis Yeager Date: Fri, 9 Oct 2026 11:45:57 -0700 Subject: [PATCH 6/8] Update documentation for split data packages --- docs/source/installation.rst | 2 +- skills.md | 12 ++++++------ ssapy/__init__.py | 4 ++-- ssapy/ephemeris.py | 8 ++++---- ssapy/utils.py | 6 +++--- tests/test_accel.py | 4 ++-- tests/test_data_file_lookup.py | 2 +- tests/test_orbit.py | 4 ++-- tests/test_tle_drag.py | 6 +++--- 9 files changed, 24 insertions(+), 24 deletions(-) diff --git a/docs/source/installation.rst b/docs/source/installation.rst index a364e85b..ee6755af 100644 --- a/docs/source/installation.rst +++ b/docs/source/installation.rst @@ -46,7 +46,7 @@ As the package has been published on `PyPI =0.2.0'." + f"{kernel.name} is missing; install the appropriate ssatk-data-* package." ) destination = os.path.join(cache_dir(), kernel.name) manual = (f"Download {kernel.url} ({kernel.nbytes / 1e6:.0f} MB) into {cache_dir()} " diff --git a/ssapy/utils.py b/ssapy/utils.py index e58a4b5f..441272ae 100644 --- a/ssapy/utils.py +++ b/ssapy/utils.py @@ -64,12 +64,12 @@ def _ssapy_data_files(): def find_file(filename, ext=None): - """ Find a file in the current directory or the ssapy datadir (the - ``ssapy/`` tree of the ``llnl-ssapy-data`` package). If ext is not None, + """Find a file in the current directory or installed split data packages. + If ext is not None, also try appending ext to the filename. Git LFS pointer files (left over from SSAPy versions that stored data - with Git LFS) are skipped, and the rest of ``llnl-ssapy-data`` is searched + with Git LFS) are skipped, and installed split data packages are searched by file name. """ names = [filename] if ext is None else [filename, filename + ext] diff --git a/tests/test_accel.py b/tests/test_accel.py index 33b19e22..746c18c3 100644 --- a/tests/test_accel.py +++ b/tests/test_accel.py @@ -19,7 +19,7 @@ def _has_data(name): - # SSAPy's data comes from llnl-ssapy-data; skip only if it is missing. + # SSAPy's data comes from split SSATK data packages; skip only if missing. try: ssapy.utils.find_file(name) return True @@ -151,7 +151,7 @@ def _make_body_dependent_objects(): earth = moon = sun = earth_MG = moon_MG = sun_MG = None -@pytest.mark.skipif(not HAS_EGM84, reason="EGM84 gravity file unavailable (llnl-ssapy-data not installed)") +@pytest.mark.skipif(not HAS_EGM84, reason="EGM84 gravity file unavailable (split data package not installed)") @timer def test_MG_3_1(): """Exercise 3.1 from Montenbruck and Gill diff --git a/tests/test_data_file_lookup.py b/tests/test_data_file_lookup.py index 1b353e12..211c1320 100644 --- a/tests/test_data_file_lookup.py +++ b/tests/test_data_file_lookup.py @@ -25,7 +25,7 @@ def test_lfs_pointer_is_not_returned_as_data(tmp_path, monkeypatch): def test_ssapy_data_is_searched_when_the_package_data_is_missing(tmp_path, monkeypatch): # With the package copy only a pointer, a real file of the same name - # shipped by llnl-ssapy-data is returned instead. + # shipped by a split SSATK data package is returned instead. local = tmp_path / "local" shipped = tmp_path / "shipped" / "ephemerides" local.mkdir() diff --git a/tests/test_orbit.py b/tests/test_orbit.py index c0b7c478..3c913525 100755 --- a/tests/test_orbit.py +++ b/tests/test_orbit.py @@ -20,7 +20,7 @@ def _has_ephemeris(): - # SSAPy's ephemeris comes from llnl-ssapy-data; skip only if it is missing. + # SSAPy's ephemeris comes from split SSATK data packages; skip only if missing. try: ssapy.body._planetary_ephemeris_path() return True @@ -1570,7 +1570,7 @@ def test_light_time_correction(): np.testing.assert_allclose(dircos, dc_linear, rtol=0, atol=1e-7) np.testing.assert_allclose(dircos, dc_exact, rtol=0, atol=1e-14) -@pytest.mark.skipif(not _has_ephemeris(), reason="planetary ephemeris unavailable (llnl-ssapy-data not installed)") +@pytest.mark.skipif(not _has_ephemeris(), reason="planetary ephemeris unavailable (split data package not installed)") @pytest.mark.timeout(90) @timer def test_find_passes(): diff --git a/tests/test_tle_drag.py b/tests/test_tle_drag.py index 18e102ca..6f6f396b 100644 --- a/tests/test_tle_drag.py +++ b/tests/test_tle_drag.py @@ -16,9 +16,9 @@ # The numerical drag propagator needs the gravity and ephemeris data from -# llnl-ssapy-data; skip those tests if it is not installed. +# split SSATK data packages; skip those tests if unavailable. def _has_data(name): - # SSAPy's data comes from llnl-ssapy-data; skip only if it is missing. + # SSAPy's data comes from split SSATK data packages; skip only if missing. try: ssapy.utils.find_file(name) return True @@ -39,7 +39,7 @@ def _has_ephemeris(): HAS_MOON_PA = _has_data("moon_pa_de440_200625.bpc") HAS_BODY_DATA = HAS_EGM84 and HAS_EPHEMERIS and HAS_MOON_PA needs_body_data = pytest.mark.skipif( - not HAS_BODY_DATA, reason="body data unavailable (llnl-ssapy-data not installed)") + not HAS_BODY_DATA, reason="body data unavailable (split data package not installed)") ISS = ("1 25544U 98067A 24015.54791435 .00016717 00000-0 30074-3 0 9993", "2 25544 51.6416 247.4627 0006703 130.5360 325.0288 15.49514637123456") From aca844975bcfad3feb4dfb59bdff285ad62b9e4f Mon Sep 17 00:00:00 2001 From: Travis Yeager Date: Fri, 9 Oct 2026 11:56:45 -0700 Subject: [PATCH 7/8] Cache split data resource discovery --- ssapy/utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ssapy/utils.py b/ssapy/utils.py index 441272ae..6ed23952 100644 --- a/ssapy/utils.py +++ b/ssapy/utils.py @@ -6,6 +6,7 @@ import re import numpy as np import warnings +from functools import lru_cache from astropy.time import Time as _Time import astropy.units as u from typing import Union, Tuple @@ -45,6 +46,7 @@ def _is_lfs_pointer(path): return False +@lru_cache(maxsize=1) def _ssapy_data_files(): """Return files shipped by installed split SSATK data packages.""" from importlib.resources import files From a38786629894e57fa6eabb02f3fc242ae0835fc7 Mon Sep 17 00:00:00 2001 From: Travis Yeager Date: Fri, 9 Oct 2026 12:23:19 -0700 Subject: [PATCH 8/8] Limit CI test parallelism on Linux --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c0d56216..f543c4de 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -123,8 +123,8 @@ jobs: - name: Test with pytest if: matrix.os == 'ubuntu-latest' run: | - # Run pytest with coverage, using all available CPUs and verbose output - pytest -v -n auto --cov-report=xml --cov=ssapy --durations=20 + # Limit xdist workers to avoid memory exhaustion on Linux runners. + pytest -v -n 2 --cov-report=xml --cov=ssapy --durations=20 - name: Upload coverage reports to Codecov if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.12' uses: codecov/codecov-action@v4