Skip to content

Commit

Permalink
Merge pull request #708 from RocketPy-Team/develop
Browse files Browse the repository at this point in the history
REL: v1.6.1
  • Loading branch information
phmbressan authored Oct 9, 2024
2 parents 054f893 + dde7ba4 commit 156aaa5
Show file tree
Hide file tree
Showing 14 changed files with 94 additions and 172 deletions.
23 changes: 20 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,37 @@ Attention: The newest changes should be on top -->

### Added

-


### Changed

-


### Fixed

-

## [v1.6.1] - 2024-10-10

### Changed

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

### Fixed

- BUG: Fix Motor Zero Dry Mass Check [#710](https://github.com/RocketPy-Team/RocketPy/pull/710)
- BUG: Fix Environment.max_expected_height for custom atmosphere [#707](https://github.com/RocketPy-Team/RocketPy/pull/707)
- BUG: Initialize _Controller Init Parameters [#703](https://github.com/RocketPy-Team/RocketPy/pull/703)
- BUG: Rail Buttons Not Accepted in Add Surfaces [#701](https://github.com/RocketPy-Team/RocketPy/pull/701)
- BUG: Vector encoding breaks MonteCarlo export. [#704](https://github.com/RocketPy-Team/RocketPy/pull/704)
- BUG: Single Point Functions Can Not Be Defined [#700](https://github.com/RocketPy-Team/RocketPy/pull/700)
- BUG: savetxt Not Accepting lambda Functions [#698](https://github.com/RocketPy-Team/RocketPy/pull/698)

## [v1.6.0] - 2024-09-29

### Added

- REL: v1.6.0 [#697](https://github.com/RocketPy-Team/RocketPy/pull/697)
- ENH: Generic Surfaces and Generic Linear Surfaces [#680](https://github.com/RocketPy-Team/RocketPy/pull/680)
- ENH: Free-Form Fins [#694](https://github.com/RocketPy-Team/RocketPy/pull/694)
- ENH: Expand Polation Options for ND Functions. [#691](https://github.com/RocketPy-Team/RocketPy/pull/691)
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
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
author = "RocketPy Team"

# The full version, including alpha/beta/rc tags
release = "1.6.0"
release = "1.6.1"


# -- General configuration ---------------------------------------------------
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
2 changes: 1 addition & 1 deletion docs/user/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ If you want to choose a specific version to guarantee compatibility, you may ins

.. code-block:: shell
pip install rocketpy==1.6.0
pip install rocketpy==1.6.1
Optional Installation Method: ``conda``
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "rocketpy"
version = "1.6.0"
version = "1.6.1"
description="Advanced 6-DOF trajectory simulation for High-Power Rocketry."
dynamic = ["dependencies"]
readme = "README.md"
Expand Down
2 changes: 2 additions & 0 deletions rocketpy/control/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,10 @@ def __init__(
None
"""
self.interactive_objects = interactive_objects
self.base_controller_function = controller_function
self.controller_function = self.__init_controller_function(controller_function)
self.sampling_rate = sampling_rate
self.initial_observed_variables = initial_observed_variables
self.name = name
self.prints = _ControllerPrints(self)

Expand Down
140 changes: 15 additions & 125 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 @@ class Environment:
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 @@ def set_atmospheric_model( # pylint: disable=too-many-statements
.. _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 @@ def set_atmospheric_model( # pylint: disable=too-many-statements
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 @@ -1334,7 +1313,7 @@ def process_standard_atmosphere(self):
self.__set_wind_speed_function(0)

# 80k meters is the limit of the standard atmosphere
self.max_expected_height = 80000
self._max_expected_height = 80000

def process_custom_atmosphere(
self, pressure=None, temperature=None, wind_u=0, wind_v=0
Expand Down Expand Up @@ -1411,7 +1390,7 @@ def process_custom_atmosphere(
None
"""
# Initialize an estimate of the maximum expected atmospheric model height
max_expected_height = 1000
max_expected_height = self.max_expected_height or 1000

# Save pressure profile
if pressure is None:
Expand Down Expand Up @@ -1455,7 +1434,7 @@ def wind_heading_func(h): # TODO: create another custom reset for heading
self.__reset_wind_direction_function()
self.__reset_wind_speed_function()

self.max_expected_height = max_expected_height
self._max_expected_height = max_expected_height

def process_windy_atmosphere(
self, model="ECMWF"
Expand Down Expand Up @@ -1530,7 +1509,7 @@ def process_windy_atmosphere(
self.__set_wind_speed_function(data_array[:, (1, 7)])

# Save maximum expected height
self.max_expected_height = max(altitude_array[0], altitude_array[-1])
self._max_expected_height = max(altitude_array[0], altitude_array[-1])

# Get elevation data from file
self.elevation = float(response["header"]["elevation"])
Expand Down Expand Up @@ -1668,7 +1647,7 @@ def process_wyoming_sounding(self, file): # pylint: disable=too-many-statements
)

# Save maximum expected height
self.max_expected_height = data_array[-1, 1]
self._max_expected_height = data_array[-1, 1]

def process_noaaruc_sounding(self, file): # pylint: disable=too-many-statements
"""Import and process the upper air sounding data from `NOAA
Expand All @@ -1689,107 +1668,18 @@ def process_noaaruc_sounding(self, file): # pylint: disable=too-many-statements
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

def process_forecast_reanalysis(
self, file, dictionary
Expand Down Expand Up @@ -2009,7 +1899,7 @@ def process_forecast_reanalysis(
self.__set_wind_speed_function(data_array[:, (1, 7)])

# Save maximum expected height
self.max_expected_height = max(height[0], height[-1])
self._max_expected_height = max(height[0], height[-1])

# Get elevation data from file
if dictionary["surface_geopotential_height"] is not None:
Expand Down Expand Up @@ -2354,7 +2244,7 @@ def select_ensemble_member(self, member=0):
self.__set_wind_speed_function(data_array[:, (1, 7)])

# Save other attributes
self.max_expected_height = max(height[0], height[-1])
self._max_expected_height = max(height[0], height[-1])
self.ensemble_member = member

# Update air density, speed of sound and dynamic viscosity
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 @@ def fetch_noaaruc_sounding(file):
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


@exponential_backoff(max_attempts=5, base_delay=2, max_delay=60)
Expand Down
14 changes: 7 additions & 7 deletions rocketpy/mathutils/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -3171,7 +3171,7 @@ def savetxt(
)
# Generate the data points using the callable
x = np.linspace(lower, upper, samples)
data_points = np.column_stack((x, self.source(x)))
data_points = np.column_stack((x, self(x)))
else:
# If the source is already an array, use it as is
data_points = self.source
Expand Down Expand Up @@ -3247,12 +3247,12 @@ def __validate_source(self, source): # pylint: disable=too-many-statements
)

source_len, source_dim = source.shape

if source_len < source_dim:
raise ValueError(
"Too few data points to define a domain. The number of rows "
"must be greater than or equal to the number of columns."
)
if not source_len == 1: # do not check for one point Functions
if source_len < source_dim:
raise ValueError(
"Too few data points to define a domain. The number of rows "
"must be greater than or equal to the number of columns."
)

return source

Expand Down
Loading

0 comments on commit 156aaa5

Please sign in to comment.