diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f6fdcf..49a4813 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file. ## [v0.13.0] unreleased +- **CHANGED** Refresh the default CelesTrak space-weather cache only when a + requested time is outside the available data range. Custom space-weather + files are never refreshed automatically. - **FIXED** MSIS 2.0 and 2.1 support parameter directories longer than 128 characters, avoiding premature process termination in deeply nested installs. - **ADDED** `interpolate_indices` option. diff --git a/pymsis/utils.py b/pymsis/utils.py index 88541b1..9e6b7ee 100644 --- a/pymsis/utils.py +++ b/pymsis/utils.py @@ -306,6 +306,23 @@ def get_f107_ap( _SPACE_WEATHER.data = _load_f107_ap_data() data = _SPACE_WEATHER.data + # Each ap value covers the three-hour interval beginning at its + # timestamp. Refresh only when the requested data is outside the file. + data_end = data["dates"][-1] + np.timedelta64(3, "h") + needs_refresh = _SPACE_WEATHER.path == _F107_AP_DEFAULT_FILE and np.any( + dates >= data_end + ) + if needs_refresh: + try: + download_f107_ap() + except OSError: + # Leave the old file and cache intact. The range check below + # reports the normal unavailable-data error for the request. + pass + if _SPACE_WEATHER.data is None: + _SPACE_WEATHER.data = _load_f107_ap_data() + data = _SPACE_WEATHER.data + data_start = data["dates"][0] data_end = data["dates"][-1] # atleast_1d keeps output shapes consistent for scalar and array inputs diff --git a/pyproject.toml b/pyproject.toml index 202e615..e33ff99 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -74,6 +74,10 @@ testpaths = [ addopts = [ "--import-mode=importlib", ] +filterwarnings = [ +# Ignore warnings loading from file specifically + 'ignore:Downloading ap and F10.7 data from file:UserWarning', +] [tool.cibuildwheel] environment.PIP_ONLY_BINARY = "numpy" diff --git a/tests/test_utils.py b/tests/test_utils.py index 980cebd..bf1a45c 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -154,6 +154,68 @@ def test_download_refreshes_cache(monkeypatch, tmp_path, local_path): assert_array_equal(utils.get_f107_ap("2000-07-01T12:00")[0], [159.6]) +def test_historical_request_does_not_refresh(monkeypatch, tmp_path, local_path): + target = tmp_path / "weather.csv" + target.write_text(local_path.read_text().replace("159.6", "199.6")) + monkeypatch.setattr(utils._SPACE_WEATHER, "path", target) + monkeypatch.setattr(utils, "_F107_AP_DEFAULT_FILE", target) + + with patch.object(utils, "download_f107_ap") as download: + assert_array_equal(utils.get_f107_ap("2000-07-01T12:00")[0], [199.6]) + download.assert_not_called() + + +def test_out_of_range_request_refreshes(monkeypatch, tmp_path, local_path): + target = tmp_path / "weather.csv" + lines = local_path.read_text().splitlines() + target.write_text("\n".join(lines[:100]) + "\n") + monkeypatch.setattr(utils._SPACE_WEATHER, "path", target) + monkeypatch.setattr(utils, "_F107_AP_DEFAULT_FILE", target) + + def refresh_from_fixture(): + target.write_bytes(local_path.read_bytes()) + utils._SPACE_WEATHER.data = None + + with patch.object( + utils, "download_f107_ap", side_effect=refresh_from_fixture + ) as download: + assert_array_equal(utils.get_f107_ap("2000-07-01T12:00")[0], [159.6]) + download.assert_called_once_with() + + +def test_custom_file_is_not_refreshed(monkeypatch, tmp_path, local_path): + target = tmp_path / "weather.csv" + lines = local_path.read_text().splitlines() + target.write_text("\n".join(lines[:100]) + "\n") + utils.use_space_weather_file(target) + + with ( + patch.object(utils, "download_f107_ap") as download, + pytest.raises(ValueError, match="not available"), + ): + utils.get_f107_ap("2000-07-01T12:00") + download.assert_not_called() + + +def test_out_of_range_request_raises_when_refresh_fails( + monkeypatch, tmp_path, local_path +): + target = tmp_path / "weather.csv" + lines = local_path.read_text().splitlines() + target.write_text("\n".join(lines[:100]) + "\n") + monkeypatch.setattr(utils._SPACE_WEATHER, "path", target) + monkeypatch.setattr(utils, "_F107_AP_DEFAULT_FILE", target) + + with ( + patch.object( + utils, "download_f107_ap", side_effect=OSError("offline") + ) as download, + pytest.raises(ValueError, match="not available"), + ): + utils.get_f107_ap("2000-07-01T12:00") + download.assert_called_once_with() + + @pytest.mark.parametrize( ("dates", "expected_f107", "expected_f107a", "expected_ap"), [