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

DEP: deprecate NOAA's RuC sounding #706

Merged
merged 6 commits into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Attention: The newest changes should be on top -->

### Changed

-
- DEP: deprecate NOAA's RuC sounding [#706](https://github.com/RocketPy-Team/RocketPy/pull/706)

### Fixed

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ RocketPy is the next-generation trajectory simulation solution for High-Power Ro

2. **Accurate Weather Modeling**
- Supports International Standard Atmosphere (1976)
- Custom atmospheric profiles and Soundings (Wyoming, NOAARuc)
- Custom atmospheric profiles and Soundings (Wyoming)
- Weather forecasts, reanalysis, and ensembles for realistic scenarios

3. **Aerodynamic Models**
Expand Down
11 changes: 11 additions & 0 deletions docs/user/environment/1-atm-models/soundings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,17 @@ Initialize a new Environment instance:
NOAA's Ruc Soundings
--------------------

.. important::

From September 30th, 2024, this model is no longer available since NOAA has \
discontinued the Ruc Soundings public service. The following message is \
displayed on the website: \
"On Monday, September 30, a number of legacy websites were permanently removed. \
These sites were no longer being maintained and did not meet security and \
design requirements mandated by NOAA. They were intended for research \
purposes and are not designed for operational use, such as for commercial \
purposes or the safety of life and property."

Another option for upper air soundings is `NOAA's Ruc Soundings <https://rucsoundings.noaa.gov/>`_.
This service allows users to download virtual soundings from numerical weather
prediction models such as GFS, RAP, and NAM, and also real soundings from the
Expand Down
126 changes: 8 additions & 118 deletions rocketpy/environment/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
fetch_gfs_file_return_dataset,
fetch_hiresw_file_return_dataset,
fetch_nam_file_return_dataset,
fetch_noaaruc_sounding,
fetch_open_elevation,
fetch_rap_file_return_dataset,
fetch_wyoming_sounding,
Expand Down Expand Up @@ -142,11 +141,11 @@
Environment.atmospheric_model_type : string
Describes the atmospheric model which is being used. Can only assume the
following values: ``standard_atmosphere``, ``custom_atmosphere``,
``wyoming_sounding``, ``NOAARucSounding``, ``Forecast``, ``Reanalysis``,
``wyoming_sounding``, ``Forecast``, ``Reanalysis``,
``Ensemble``.
Environment.atmospheric_model_file : string
Address of the file used for the atmospheric model being used. Only
defined for ``wyoming_sounding``, ``NOAARucSounding``, ``Forecast``,
defined for ``wyoming_sounding``, ``Forecast``,
``Reanalysis``, ``Ensemble``
Environment.atmospheric_model_dict : dictionary
Dictionary used to properly interpret ``netCDF`` and ``OPeNDAP`` files.
Expand Down Expand Up @@ -1053,24 +1052,6 @@

.. _weather.uwyo: http://weather.uwyo.edu/upperair/sounding.html

- ``NOAARucSounding``: sets pressure, temperature, wind-u
and wind-v profiles and surface elevation obtained from
an upper air sounding given by the file parameter through
an URL. This URL should point to a data webpage obtained
through NOAA's Ruc Sounding servers, which can be accessed
in `rucsoundings`_. Selecting ROABs as the
initial data source, specifying the station through it's
WMO-ID and opting for the ASCII (GSD format) button, the
following example URL opens up:

https://rucsoundings.noaa.gov/get_raobs.cgi?data_source=RAOB&latest=latest&start_year=2019&start_month_name=Feb&start_mday=5&start_hour=12&start_min=0&n_hrs=1.0&fcst_len=shortest&airport=83779&text=Ascii%20text%20%28GSD%20format%29&hydrometeors=false&start=latest

Any ASCII GSD format page from this server can be read,
so information from virtual soundings such as GFS and NAM
can also be imported.

.. _rucsoundings: https://rucsoundings.noaa.gov/

- ``windy_atmosphere``: sets pressure, temperature, wind-u and
wind-v profiles and surface elevation obtained from the Windy API.
See file argument to specify the model as either ``ECMWF``,
Expand Down Expand Up @@ -1279,8 +1260,6 @@
self.process_standard_atmosphere()
elif type == "wyoming_sounding":
self.process_wyoming_sounding(file)
elif type == "noaarucsounding":
self.process_noaaruc_sounding(file)
elif type == "custom_atmosphere":
self.process_custom_atmosphere(pressure, temperature, wind_u, wind_v)
elif type == "windy":
Expand Down Expand Up @@ -1689,107 +1668,18 @@

See also
--------
More details can be found at: https://rucsoundings.noaa.gov/.
This method is deprecated and will be fully deleted in version 1.8.0.

Returns
-------
None
"""
# Request NOAA Ruc Sounding from file url
response = fetch_noaaruc_sounding(file)

# Split response into lines
lines = response.text.split("\n")

# Process GSD format (https://rucsoundings.noaa.gov/raob_format.html)

# Extract elevation data
for line in lines:
# Split line into columns
columns = re.split(" +", line)[1:]
if len(columns) > 0:
if columns[0] == "1" and columns[5] != "99999":
# Save elevation
self.elevation = float(columns[5])
else:
# No elevation data available
pass

pressure_array = []
barometric_height_array = []
temperature_array = []
wind_speed_array = []
wind_direction_array = []

for line in lines:
# Split line into columns
columns = re.split(" +", line)[1:]
if len(columns) < 6:
# skip lines with less than 6 columns
continue
if columns[0] in ["4", "5", "6", "7", "8", "9"]:
# Convert columns to floats
columns = np.array(columns, dtype=float)
# Select relevant columns
altitude, pressure, temperature, wind_direction, wind_speed = columns[
[2, 1, 3, 5, 6]
]
# Check for missing values
if altitude == 99999:
continue
# Save values only if they are not missing
if pressure != 99999:
pressure_array.append([altitude, pressure])
barometric_height_array.append([pressure, altitude])
if temperature != 99999:
temperature_array.append([altitude, temperature])
if wind_direction != 99999:
wind_direction_array.append([altitude, wind_direction])
if wind_speed != 99999:
wind_speed_array.append([altitude, wind_speed])

# Convert lists to arrays
pressure_array = np.array(pressure_array)
barometric_height_array = np.array(barometric_height_array)
temperature_array = np.array(temperature_array)
wind_speed_array = np.array(wind_speed_array)
wind_direction_array = np.array(wind_direction_array)

# Converts 10*hPa to Pa and save values
pressure_array[:, 1] = 10 * pressure_array[:, 1]
self.__set_pressure_function(pressure_array)
# Converts 10*hPa to Pa and save values
barometric_height_array[:, 0] = 10 * barometric_height_array[:, 0]
self.__set_barometric_height_function(barometric_height_array)

# Convert C to K and save values
temperature_array[:, 1] = temperature_array[:, 1] / 10 + 273.15
self.__set_temperature_function(temperature_array)

# Process wind-u and wind-v
# Converts Knots to m/s
wind_speed_array[:, 1] = wind_speed_array[:, 1] * 1.852 / 3.6
wind_heading_array = wind_direction_array[:, :] * 1
# Convert wind direction to wind heading
wind_heading_array[:, 1] = (wind_direction_array[:, 1] + 180) % 360
wind_u = wind_speed_array[:, :] * 1
wind_v = wind_speed_array[:, :] * 1
wind_u[:, 1] = wind_speed_array[:, 1] * np.sin(
np.deg2rad(wind_heading_array[:, 1])
)
wind_v[:, 1] = wind_speed_array[:, 1] * np.cos(
np.deg2rad(wind_heading_array[:, 1])
warnings.warn(
"NOAA RUC models are no longer available. "
"This method is deprecated and will be fully deleted in version 1.8.0.",
DeprecationWarning,
)

# Save wind data
self.__set_wind_direction_function(wind_direction_array)
self.__set_wind_heading_function(wind_heading_array)
self.__set_wind_speed_function(wind_speed_array)
self.__set_wind_velocity_x_function(wind_u)
self.__set_wind_velocity_y_function(wind_v)

# Save maximum expected height
self.max_expected_height = pressure_array[-1, 0]
return file

Check warning on line 1682 in rocketpy/environment/environment.py

View check run for this annotation

Codecov / codecov/patch

rocketpy/environment/environment.py#L1682

Added line #L1682 was not covered by tests

def process_forecast_reanalysis(
self, file, dictionary
Expand Down
11 changes: 7 additions & 4 deletions rocketpy/environment/fetchers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import re
import time
import warnings
from datetime import datetime, timedelta, timezone

import netCDF4
Expand Down Expand Up @@ -347,10 +348,12 @@
ImportError
If unable to load the specified file or the file content is too short.
"""
response = requests.get(file)
if response.status_code != 200 or len(response.text) < 10:
raise ImportError("Unable to load " + file + ".")
return response
warnings.warn(
"The NOAA RUC soundings are deprecated since September 30th, 2024. "
"This method will be removed in version 1.8.0.",
DeprecationWarning,
)
return file

Check warning on line 356 in rocketpy/environment/fetchers.py

View check run for this annotation

Codecov / codecov/patch

rocketpy/environment/fetchers.py#L356

Added line #L356 was not covered by tests


@exponential_backoff(max_attempts=5, base_delay=2, max_delay=60)
Expand Down
14 changes: 0 additions & 14 deletions tests/integration/test_environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,20 +98,6 @@ def test_standard_atmosphere(
assert example_plain_env.prints.print_earth_details() is None


@patch("matplotlib.pyplot.show")
def test_noaaruc_atmosphere(
mock_show, example_spaceport_env
): # pylint: disable=unused-argument
url = (
r"https://rucsoundings.noaa.gov/get_raobs.cgi?data_source=RAOB&latest="
r"latest&start_year=2019&start_month_name=Feb&start_mday=5&start_hour=12"
r"&start_min=0&n_hrs=1.0&fcst_len=shortest&airport=83779&text=Ascii"
r"%20text%20%28GSD%20format%29&hydrometeors=false&start=latest"
)
example_spaceport_env.set_atmospheric_model(type="NOAARucSounding", file=url)
assert example_spaceport_env.all_info() is None


@pytest.mark.parametrize(
"model_name",
[
Expand Down
Loading