ENH: Support for Open-Meteo API in the Environment class - #1119
Open
Gui-FernandesBR wants to merge 7 commits into
Open
ENH: Support for Open-Meteo API in the Environment class#1119Gui-FernandesBR wants to merge 7 commits into
Environment class#1119Gui-FernandesBR wants to merge 7 commits into
Conversation
Wraps the three Open-Meteo endpoints RocketPy needs to build atmospheric profiles: the forecast API, the historical-forecast API (for past launch dates) and the ensemble API. All of them serve pressure-level data as plain JSON over HTTPS, with no API key and no netCDF/OPeNDAP dependency. Note that Open-Meteo's ERA5 archive endpoint is deliberately not used: it serves surface variables only and answers with nulls at every pressure level, so the historical-forecast API (available from 2021 onwards) is the only archive that can feed a vertical profile. Ensemble models are restricted to the ones that actually publish pressure-level data (gfs05, ecmwf_ifs025, gem_global); the others return HTTP 200 with null values, which would otherwise surface as an opaque failure much later in the parsing step. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds two new atmospheric model types to set_atmospheric_model:
env.set_atmospheric_model("open_meteo") # best_match
env.set_atmospheric_model("open_meteo", file="ecmwf_ifs025")
env.set_atmospheric_model("open_meteo_ensemble", file="gfs05")
Both build the usual pressure, temperature and wind profiles from
Open-Meteo pressure-level data, so no external files and no netCDF/OPeNDAP
libraries are involved. When the launch date is in the past, "open_meteo"
transparently queries Open-Meteo's historical-forecast archive instead of
the live forecast, which is what makes past-launch reconstruction work
without downloading reanalysis files by hand.
The ensemble processor stores every member, so select_ensemble_member() and
plots.ensemble_member_comparison() work exactly as they do for GEFS. The
unsuffixed control run is kept as member 0, matching the documented
convention that member 0 is the unperturbed control.
Open-Meteo reports wind as speed/direction rather than u/v components, so
convert_wind_speed_direction_to_components is added to environment.tools;
it converts the meteorological blows-from convention into RocketPy's
East/North components. Temperatures are converted from Celsius to Kelvin
and pressure levels from hPa to Pa.
The model-type gates in the prints and plots classes were comparing
capitalised literals ("Ensemble"), which never matched a lower-case type
even though set_atmospheric_model documents the argument as
case-insensitive. They now compare case-insensitively, so both the new
Open-Meteo types and a lower-case "ensemble" report their time period and
member count.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds 44 offline unit tests (tests/unit/environment/test_open_meteo.py) and 6 live integration tests marked slow. The unit tests patch the fetchers, so the whole module runs without network access: verified by re-running the suite with socket.connect blocked, where all 44 still pass. They cover the unit conversions (hPa to Pa, Celsius to Kelvin, speed/direction to u/v), the nearest-hour selection, skipping levels a model does not resolve, altitude sorting, the ensemble member layout with the control run as member 0, the endpoint routing for past versus future dates, error payload handling, and to_dict/from_dict round trips. The wind-component conversion is tested against the four cardinal directions and round-tripped through calculate_wind_heading, since getting that convention wrong would silently flip the wind by 180 degrees. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Verifying the ensemble models against the live API showed gem_global cannot feed a RocketPy profile: it publishes temperature and geopotential height at pressure levels but no pressure-level winds at all (168/168 hours null for wind_speed and wind_direction at every level, at three different launch sites). The earlier check only probed temperature, which is why it looked usable. Accepting it meant "Open-Meteo returned fewer than two usable pressure levels" at profile-build time instead of an actionable message naming the model, so it is now rejected up front alongside gfs025, icon_global and bom_access_global_ensemble. gfs05 (31 members) and ecmwf_ifs025 (51 members) are the two that publish the full set; both member counts are confirmed against the API. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds docs/user/environment/1-atm-models/open_meteo.rst, covering the forecast, past-launch and ensemble workflows, the model tables, and the caveats worth knowing: coverage of pressure levels varies per model, the historical archive only reaches back to 2021, and Open-Meteo's ERA5 endpoint cannot be used because it serves no pressure-level data. Cross-references were added from the forecast, reanalysis and ensemble pages, since Open-Meteo is the lighter alternative in each of those cases -- notably for ensembles, where the GEFS shortcut is currently unavailable. Every code block in the new page runs as part of the docs build; all five were executed against the live API to confirm they work. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Open-Meteo answers historical requests for unsupported dates with HTTP 200 and null values at every pressure level, so a pre-archive launch date used to surface only as a generic "fewer than two usable pressure levels" error, with no hint that the date itself was the problem. Such dates now raise a warning naming the archive start and pointing at the reanalysis and sounding models. The cutoff was probed against the live API rather than assumed: 2021-03-15 comes back empty while 2021-03-23 is complete, so the archive starts in March 2021 and not in January as the previous constant implied. The constant is now a date instead of a year, and the docs state March 2021. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
|
Failed to generate code suggestions for PR |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## develop #1119 +/- ##
===========================================
+ Coverage 82.18% 82.80% +0.62%
===========================================
Files 122 129 +7
Lines 16355 16812 +457
===========================================
+ Hits 13441 13922 +481
+ Misses 2914 2890 -24 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
The CI lint job runs pylint, not just ruff, and flagged the new code: - process_open_meteo_atmosphere and process_open_meteo_ensemble exceeded the statement limit. Rather than suppress it, the profile-storing and member-stacking blocks were extracted into helpers, mirroring the existing _store_meteomatics_* pattern. The two processors now read as a sequence of named steps. - set_atmospheric_model exceeded the branch limit, since the two new model cases added to an already long match. The self-contained pressure_conversion_factor validation moved to a private validator next to the other validators, which also flattens its nested ifs. - Unused-argument and missing-docstring warnings in the new tests, from fakes that deliberately accept the real signature. Behaviour is unchanged: the pressure_conversion_factor error messages and the Open-Meteo profiles were re-verified against the original, and the full unit suite still passes (1954 passed, 16 skipped). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #520
Pull request type
Checklist
ruff check/ruff format) has passed locallypytest tests -m slow --runslow) have passed locallyCHANGELOG.md— entries added manually as wellCurrent behavior
Reaching for reanalysis data means downloading files by hand from the Copernicus
Climate Data Store, and pulling a forecast means an OPeNDAP request through
netCDF4, which is heavy and slow. On top of that, theGEFSensemble shortcutis currently disabled because NOMADS OPeNDAP was deactivated, so there is no
working out-of-the-box ensemble source.
New behavior
Adds Open-Meteo as an atmospheric data source. It
serves pressure-level data as plain JSON over HTTPS, with no API key and no
netCDF4/OPeNDAP dependency (requestsis already a RocketPy dependency, sothis adds none).
Two new model types:
Past launches need no extra argument — a past date is transparently routed to
Open-Meteo's historical-forecast archive:
The ensemble model stores every member, so
select_ensemble_member()andplots.ensemble_member_comparison()work exactly as they do for GEFS, with theunperturbed control run exposed as member
0. Open-Meteo also reports theelevation of the grid cell it answered for, so the launch site elevation is set
automatically.
Notable API findings
Each of the following returns HTTP 200, so none of them fail loudly. They were
found by probing the live API and they shaped the implementation:
nulls at every level and date tested. It is therefore not used; the
historical-forecast API is the only archive that can feed a vertical profile.
2021-03-15comes backempty,
2021-03-23is complete). Earlier dates now raise a warning naming thecutoff and pointing at the reanalysis and sounding models, instead of failing
with a generic "not enough pressure levels" error.
gfs05(31 members) andecmwf_ifs025(51) publish completepressure-level data.
gem_globalis the subtle one: it serves temperatureand geopotential height but no pressure-level winds, so probing temperature
alone makes it look usable.
gfs025,icon_globalandbom_access_global_ensemblereturn nulls everywhere. All of these are nowrejected up front with an actionable message.
wind_speed_unit=msis requested explicitly;dropping it would silently inflate wind speeds by 3.6x.
Coverage of pressure levels varies per model (
gfs_seamlessreaches 30 hPa,ecmwf_ifs025stops at 50 hPa), so levels a model does not resolve are droppedwhile parsing.
Drive-by bug fix
The model-type gates in
_EnvironmentPrintsand_EnvironmentPlotscomparedagainst capitalised literals (
"Ensemble","Forecast"), butset_atmospheric_modeldocumentstypeas case-insensitive and stores it as theuser spelled it. So
type="ensemble"silently printed no time period and nomember count, and skipped the ensemble comparison plot. They now compare
case-insensitively.
Breaking change
Additional information
Implementation notes
convert_wind_speed_direction_to_componentswas added toenvironment/tools.py. It converts the meteorological blows-from conventioninto RocketPy's East/North components, and is round-trip tested against
calculate_wind_heading— getting that convention wrong would flip the windby 180°.
Pa, and geopotential heights to geometric altitude.
to_dict/from_dictround-trip both new types, including every ensemblemember.
Verification
re-run with
socket.connectblocked to confirm no network access.slow, exercising the live API (forecast across4 models, the historical archive, and the ensemble with member selection).
tests/integration/environmentsuite passes with--runslow: 29passed, 1 skipped.
were executed against the live API.
open_meteoat SpaceportAmerica gives 3421 m AGL apogee at Mach 0.82, with the 1405 m site elevation
picked up automatically from the API.
Docs
New page at
docs/user/environment/1-atm-models/open_meteo.rst, cross-referencedfrom the forecast, reanalysis and ensemble pages, since Open-Meteo is the lighter
alternative in each of those cases — and currently the only working ensemble
source while the
GEFSshortcut is down.🤖 Generated with Claude Code