Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
fd8de97
TST: adding tests for aerosurfaces methods
Gui-FernandesBR Nov 20, 2022
68e9726
BUG: correct wrong f string tail method
Gui-FernandesBR Nov 20, 2022
ba54749
BUG: fix Environment.exportEnvironment()
Gui-FernandesBR Nov 20, 2022
65fe9d7
MAINT: specifying input types in utmToGeodesic
Gui-FernandesBR Nov 20, 2022
d591d52
MAINT: specifying input and retuning Nones
Gui-FernandesBR Nov 20, 2022
a014dba
TST: Add additional cases to test_environment
Gui-FernandesBR Nov 20, 2022
40baf0b
TST: Create test for environment analysis
Gui-FernandesBR Nov 20, 2022
2199195
TST: Create tests for Function class
Gui-FernandesBR Nov 20, 2022
1218179
MAINT: removing undesired comments
Gui-FernandesBR Nov 20, 2022
a847089
Fix code style issues with Black
lint-action Nov 20, 2022
23d98d4
MAINT: revert type hint and improve docs
Gui-FernandesBR Dec 31, 2022
916cc50
ENH: moving fixtures to the conftest.py
Gui-FernandesBR Dec 31, 2022
49d4b75
MAINT: improve docstrings for tests
Gui-FernandesBR Dec 31, 2022
cc1dc04
TST: Improved tests for info returned
Gui-FernandesBR Dec 31, 2022
bd7d88f
MAINT: closing and cleaning files after tests
Gui-FernandesBR Dec 31, 2022
27422af
Merge branch 'enh/distances-vs-positons-v2' into tst/new_env_func_aer…
Gui-FernandesBR Dec 31, 2022
db990f9
Fix code style issues with Black
lint-action Dec 31, 2022
4bb94f6
maint: removed anotations
MateusStano Jan 4, 2023
b352ac7
enh: added envanalysis import
MateusStano Jan 4, 2023
51c7cbf
maint: sort imports
MateusStano Jan 4, 2023
65a28f7
bug: fix Function np.inf call with int or float defined Functions
MateusStano Jan 4, 2023
8473947
TST: set EnvAnalysis tests as slow option
Gui-FernandesBR Jan 4, 2023
e6cf871
Merge branch 'enh/distances-vs-positons-v2' into tst/new_env_func_aer…
MateusStano Jan 10, 2023
dda3ce5
Fix code style issues with Black
lint-action Jan 10, 2023
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 rocketpy/AeroSurfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -1466,7 +1466,7 @@ def geometricInfo(self):
def aerodynamicInfo(self):

print(f"\nTail name: {self.name}")
print(f"Tail Center of Pressure Position in Local Coordinates: {self.cp:.3f} m")
print(f"Tail Center of Pressure Position in Local Coordinates: {self.cp} m")
print(f"Tail Lift Coefficient Slope: {self.clalpha:.3f} 1/rad")
print("Tail Lift Coefficient as a function of Alpha and Mach:")
self.cl()
Expand Down
78 changes: 65 additions & 13 deletions rocketpy/Environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -3027,7 +3027,54 @@ def info(self):

# Plot graphs
print("\n\nAtmospheric Model Plots")
self.plots.atmospheric_model()
# Create height grid
grid = np.linspace(self.elevation, self.maxExpectedHeight)

# Create figure
plt.figure(figsize=(9, 4.5))

# Create wind speed and wind direction subplot
ax1 = plt.subplot(121)
ax1.plot(
[self.windSpeed(i) for i in grid], grid, "#ff7f0e", label="Speed of Sound"
)
ax1.set_xlabel("Wind Speed (m/s)", color="#ff7f0e")
ax1.tick_params("x", colors="#ff7f0e")
ax1up = ax1.twiny()
ax1up.plot(
[self.windDirection(i) for i in grid],
grid,
color="#1f77b4",
label="Density",
)
ax1up.set_xlabel("Wind Direction (°)", color="#1f77b4")
ax1up.tick_params("x", colors="#1f77b4")
ax1up.set_xlim(0, 360)
ax1.set_ylabel("Height Above Sea Level (m)")
ax1.grid(True)

# Create density and speed of sound subplot
ax2 = plt.subplot(122)
ax2.plot(
[self.speedOfSound(i) for i in grid],
grid,
"#ff7f0e",
label="Speed of Sound",
)
ax2.set_xlabel("Speed of Sound (m/s)", color="#ff7f0e")
ax2.tick_params("x", colors="#ff7f0e")
ax2up = ax2.twiny()
ax2up.plot(
[self.density(i) for i in grid], grid, color="#1f77b4", label="Density"
)
ax2up.set_xlabel("Density (kg/m³)", color="#1f77b4")
ax2up.tick_params("x", colors="#1f77b4")
ax2.set_ylabel("Height Above Sea Level (m)")
ax2.grid(True)

plt.subplots_adjust(wspace=0.5)
plt.show()
return None

def allInfo(self):
"""Prints out all data and graphs available about the Environment.
Expand All @@ -3046,7 +3093,7 @@ def allInfo(self):

return None

def allPlotInfoReturned(self):
def allPlotInfoReturned(self) -> dict:
"""Returns a dictionary with all plot information available about the Environment.

Parameters
Expand Down Expand Up @@ -3158,9 +3205,10 @@ def allInfoReturned(self):
return info

def exportEnvironment(self, filename="environment"):
"""Export important attributes of Environment class so it can be used
again in further siulations by using the customAtmosphere atmospheric
model.
"""Export important attributes of Environment class to a .json file,
saving all the information needed to recreate the same environment using
customAtmosphere.

Parameters
----------
filename
Expand All @@ -3170,9 +3218,13 @@ def exportEnvironment(self, filename="environment"):
None
"""

# TODO: in the future, allow the user to select which format will be used (json, csv, etc.). Default must be JSON.
# TODO: add self.exportEnvDictionary to the documentation
# TODO: find a way to documennt the workaround I've used on ma.getdata(self...
try:
atmosphericModelFile = self.atmosphericModelFile
atmosphericModelDict = self.atmosphericModelDict
except AttributeError:
atmosphericModelFile = ""
atmosphericModelDict = ""

self.exportEnvDictionary = {
"railLength": self.rL,
"gravity": self.g,
Expand All @@ -3184,8 +3236,8 @@ def exportEnvironment(self, filename="environment"):
"timeZone": self.timeZone,
"maxExpectedHeight": float(self.maxExpectedHeight),
"atmosphericModelType": self.atmosphericModelType,
"atmosphericModelFile": self.atmosphericModelFile,
"atmosphericModelDict": self.atmosphericModelDict,
"atmosphericModelFile": atmosphericModelFile,
"atmosphericModelDict": atmosphericModelDict,
"atmosphericModelPressureProfile": ma.getdata(
self.pressure.getSource()
).tolist(),
Expand Down Expand Up @@ -3365,17 +3417,17 @@ def utmToGeodesic(self, x, y, utmZone, hemis, datum):
hemis : string
Equals to "S" for southern hemisphere and "N" for Northern hemisphere
datum : string
The desired reference ellipsoide model, the following options are
The desired reference ellipsoid model, the following options are
available: "SAD69", "WGS84", "NAD83", and "SIRGAS2000". The default
is "SIRGAS2000", then this model will be used if the user make some
typing mistake

Returns
-------
lat: float
latitude of the analysed point
latitude of the analyzed point
lon: float
latitude of the analysed point
latitude of the analyzed point
"""

if hemis == "N":
Expand Down
31 changes: 30 additions & 1 deletion rocketpy/EnvironmentAnalysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -1410,6 +1410,7 @@ def plot_average_temperature_along_day(self):
plt.grid(alpha=0.25)
plt.legend()
plt.show()
return None

def calculate_average_sustained_surface10m_wind_along_day(self):
"""Computes average sustained wind speed progression throughout the
Expand Down Expand Up @@ -1509,6 +1510,7 @@ def plot_average_surface10m_wind_speed_along_day(self, windSpeedLimit=False):
plt.grid(alpha=0.25)
plt.legend()
plt.show()
return None

def calculate_average_sustained_surface100m_wind_along_day(self):
"""Computes average sustained wind speed progression throughout the
Expand Down Expand Up @@ -1672,6 +1674,7 @@ def plot_average_wind_speed_profile(self, clear_range_limits=False):
plt.legend()
plt.xlim(0, max(np.percentile(wind_speed_profiles, 50 + 49.85, axis=0)))
plt.show()
return None

def plot_average_wind_heading_profile(self, clear_range_limits=False):
"""Average wind heading for all datetimes available."""
Expand Down Expand Up @@ -1764,6 +1767,7 @@ def plot_average_wind_heading_profile(self, clear_range_limits=False):
plt.title("Average Wind heading Profile")
plt.legend()
plt.show()
return None

def process_wind_speed_and_direction_data_for_average_day(self):
"""Process the wind_speed and wind_direction data to generate lists of all the wind_speeds recorded
Expand Down Expand Up @@ -1892,6 +1896,7 @@ def plot_average_pressure_profile(self, clear_range_limits=False):
plt.legend()
plt.xlim(0, max(np.percentile(pressure_profiles, 50 + 49.85, axis=0)))
plt.show()
return None

@staticmethod
def plot_wind_rose(
Expand Down Expand Up @@ -1958,6 +1963,8 @@ def plot_average_day_wind_rose_specific_hour(self, hour, fig=None):
)
plt.show()

return None

def plot_average_day_wind_rose_all_hours(self):
"""Plot wind roses for all hours of a day, in a grid like plot."""
# Get days and hours
Expand Down Expand Up @@ -2032,6 +2039,7 @@ def plot_average_day_wind_rose_all_hours(self):
f"Wind Roses ({self.unit_system['wind_speed']})", fontsize=20, x=0.5, y=1
)
plt.show()
return None

def animate_average_wind_rose(self, figsize=(8, 8), filename="wind_rose.gif"):
"""Animates the wind_rose of an average day. The inputs of a wind_rose
Expand Down Expand Up @@ -2169,6 +2177,8 @@ def plot_wind_gust_distribution_over_average_day(self):
fig.supylabel("Probability")
plt.show()

return None

def animate_wind_gust_distribution_over_average_day(self):
"""Animation of how the wind gust distribution varies throughout the day."""
# Gather animation data
Expand Down Expand Up @@ -2347,9 +2357,12 @@ def plot_sustained_surface_wind_speed_distribution_over_average_day(
fig.supylabel("Probability")
plt.show()

return None

def animate_sustained_surface_wind_speed_distribution_over_average_day(
self, windSpeedLimit=False
): # TODO: getting weird results since the 0.3 on y axis is not parametrized
):
# TODO: getting weird results since the 0.3 on y axis is not parametrized
"""Animation of how the sustained surface wind speed distribution varies throughout the day."""
# Gather animation data
surface_wind_speeds_at_given_hour = {}
Expand Down Expand Up @@ -2503,6 +2516,8 @@ def process_temperature_profile_over_average_day(self):
average_temperature_profile_at_given_hour
)

return None

def process_pressure_profile_over_average_day(self):
"""Compute the average pressure profile for each available hour of a day, over all
days in the dataset."""
Expand Down Expand Up @@ -2536,6 +2551,8 @@ def process_pressure_profile_over_average_day(self):
average_pressure_profile_at_given_hour
)

return None

def process_wind_speed_profile_over_average_day(self):
"""Compute the average wind profile for each available hour of a day, over all
days in the dataset."""
Expand Down Expand Up @@ -2570,6 +2587,8 @@ def process_wind_speed_profile_over_average_day(self):
self.max_average_wind_at_altitude = max_wind
self.average_wind_profile_at_given_hour = average_wind_profile_at_given_hour

return None

def process_wind_velocity_x_profile_over_average_day(self):
"""Compute the average windVelocityX profile for each available hour of a day, over all
days in the dataset."""
Expand Down Expand Up @@ -2602,6 +2621,7 @@ def process_wind_velocity_x_profile_over_average_day(self):
self.average_windVelocityX_profile_at_given_hour = (
average_windVelocityX_profile_at_given_hour
)
return None

def process_wind_velocity_y_profile_over_average_day(self):
"""Compute the average windVelocityY profile for each available hour of a day, over all
Expand Down Expand Up @@ -2635,6 +2655,7 @@ def process_wind_velocity_y_profile_over_average_day(self):
self.average_windVelocityY_profile_at_given_hour = (
average_windVelocityY_profile_at_given_hour
)
return None

def plot_wind_profile_over_average_day(self, clear_range_limits=False):
"""Creates a grid of plots with the wind profile over the average day."""
Expand Down Expand Up @@ -2706,6 +2727,8 @@ def plot_wind_profile_over_average_day(self, clear_range_limits=False):
fig.supylabel(f"Altitude AGL ({self.unit_system['length']})")
plt.show()

return None

def process_wind_heading_profile_over_average_day(self):
"""Compute the average wind velocities (both X and Y components) profile for each available hour of a day, over all days in the dataset."""
altitude_list = np.linspace(*self.altitude_AGL_range, 100)
Expand Down Expand Up @@ -2774,6 +2797,8 @@ def process_wind_heading_profile_over_average_day(self):
average_wind_heading_profile_at_given_hour
)

return None

def plot_wind_heading_profile_over_average_day(self, clear_range_limits=False):
"""Creates a grid of plots with the wind heading profile over the average day."""
self.process_wind_heading_profile_over_average_day()
Expand Down Expand Up @@ -2836,6 +2861,8 @@ def plot_wind_heading_profile_over_average_day(self, clear_range_limits=False):
fig.supylabel(f"Altitude AGL ({self.unit_system['length']})")
plt.show()

return None

def animate_wind_profile_over_average_day(self, clear_range_limits=False):
"""Animation of how wind profile evolves throughout an average day."""
self.process_wind_speed_profile_over_average_day()
Expand Down Expand Up @@ -3110,6 +3137,8 @@ def allInfo(self):
f"Percentage of Days Without Clouds: {100*self.percentage_of_days_with_no_cloud_coverage:.1f} %"
)

return None

def exportMeanProfiles(self, filename="export_env_analysis"):
"""
Exports the mean profiles of the weather data to a file in order to it
Expand Down
2 changes: 1 addition & 1 deletion rocketpy/Function.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ def setSource(self, source):
temp = 1 * source

def source(x):
return 0 * x + temp
return temp

# Handle callable source or number source
if callable(source):
Expand Down
81 changes: 80 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import numericalunits
import pytest

from rocketpy import Environment, Rocket, SolidMotor
from rocketpy import Environment, EnvironmentAnalysis, Function, Rocket, SolidMotor


def pytest_addoption(parser):
Expand Down Expand Up @@ -101,6 +101,85 @@ def dimensionless_rocket(kg, m, dimensionless_solid_motor):
return example_rocket


@pytest.fixture
def example_env():
Env = Environment(railLength=5, datum="WGS84")
return Env


@pytest.fixture
def example_env_robust():
Env = Environment(
railLength=5,
latitude=32.990254,
longitude=-106.974998,
elevation=1400,
datum="WGS84",
)
tomorrow = datetime.date.today() + datetime.timedelta(days=1)
Env.setDate(
(tomorrow.year, tomorrow.month, tomorrow.day, 12)
) # Hour given in UTC time
return Env


# Create a simple object of the Environment Analysis class
@pytest.fixture
def env_analysis():
"""Create a simple object of the Environment Analysis class to be used in
the tests. This allows to avoid repeating the same code in all tests.

Returns
-------
EnvironmentAnalysis
A simple object of the Environment Analysis class
"""
env_analysis = EnvironmentAnalysis(
start_date=datetime.datetime(2019, 10, 23),
end_date=datetime.datetime(2021, 10, 23),
latitude=39.3897,
longitude=-8.28896388889,
start_hour=6,
end_hour=18,
surfaceDataFile="./data/weather/EuroC_single_level_reanalysis_2002_2021.nc",
pressureLevelDataFile="./data/weather/EuroC_pressure_levels_reanalysis_2001-2021.nc",
timezone=None,
unit_system="metric",
forecast_date=None,
forecast_args=None,
maxExpectedAltitude=None,
)

return env_analysis


@pytest.fixture
def linear_func():
"""Create a linear function based on a list of points. The function
represents y = x and may be used on different tests.

Returns
-------
Function
A linear function representing y = x.
"""
return Function(
[[0, 0], [1, 1], [2, 2], [3, 3]],
)


@pytest.fixture
def func_from_csv():
func = Function(
source="tests/fixtures/airfoils/e473-10e6-degrees.csv",
inputs=["Scalar"],
outputs=["Scalar"],
interpolation="linear",
extrapolation="linear",
)
return func


def pytest_collection_modifyitems(config, items):
if config.getoption("--runslow"):
# --runslow given in cli: do not skip slow tests
Expand Down
Loading