Skip to content

Commit 48e4f5d

Browse files
authored
Merge branch 'develop' into enh/shepard-multiple-opt
2 parents 1de49c9 + 2ebed8d commit 48e4f5d

File tree

15 files changed

+423
-42
lines changed

15 files changed

+423
-42
lines changed

.pylintrc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ function-naming-style=snake_case
187187
# Good variable names which should always be accepted, separated by a comma.
188188
good-names=FlightPhases,
189189
WindroseAxes,
190+
barometric_height_ISA,
190191

191192

192193
# Good variable names regexes, separated by a comma. If names match any regex,
@@ -435,6 +436,8 @@ disable=raw-checker-failed,
435436
use-implicit-booleaness-not-comparison-to-zero,
436437
no-else-return,
437438
inconsistent-return-statements,
439+
unspecified-encoding,
440+
no-member, # because we use funcify_method decorator
438441

439442
# Enable the message, report, category or checker with the given id(s). You can
440443
# either give multiple identifier separated by comma (,) or put this option

CHANGELOG.md

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,20 +33,34 @@ straightforward as possible.
3333
### Added
3434

3535
- ENH: Shepard Optimized Interpolation - Multiple Inputs Support [#515](https://github.com/RocketPy-Team/RocketPy/pull/515)
36+
- ENH: adds new Function.savetxt method [#514](https://github.com/RocketPy-Team/RocketPy/pull/514)
3637
- ENH: Argument for Optional Mutation on Function Discretize [#519](https://github.com/RocketPy-Team/RocketPy/pull/519)
3738

3839
### Changed
3940

4041
- MNT: Add repr method to Parachute class [#490](https://github.com/RocketPy-Team/RocketPy/pull/490)
4142
- ENH: Function Reverse Arithmetic Priority [#488](https://github.com/RocketPy-Team/RocketPy/pull/488)
4243
- DOC: Update Header Related Docs
44+
- ENH Precalculate Barometric Height [#511](https://github.com/RocketPy-Team/RocketPy/pull/511)
45+
- MNT: Encapsulate quaternion conversions [#537](https://github.com/RocketPy-Team/RocketPy/pull/537)
46+
- MNT: improve the low pass filter and document an example [#538](https://github.com/RocketPy-Team/RocketPy/pull/538)
47+
4348

4449
### Fixed
4550

4651
- ENH: Parachute trigger doesn't work if "Apogee" is used instead of "apogee" [#489](https://github.com/RocketPy-Team/RocketPy/pull/489)
4752
- BUG: fin_flutter_analysis doesn't find any fin set [#510](https://github.com/RocketPy-Team/RocketPy/pull/510)
4853
- FIX: EmptyMotor is breaking the Rocket.draw() method [#516](https://github.com/RocketPy-Team/RocketPy/pull/516)
4954
- BUG: 3D trajectory plot not labeling axes [#533](https://github.com/RocketPy-Team/RocketPy/pull/533)
55+
-
56+
57+
## [v1.1.5] - 2024-01-21
58+
59+
You can install this version by running `pip install rocketpy==1.1.5`
60+
61+
### Fixed
62+
63+
- BUG: Parachute Pressures not being Set before All Info. [#534](https://github.com/RocketPy-Team/RocketPy/pull/534)
5064
- BUG: Invalid Arguments on Two Dimensional Discretize. [#521](https://github.com/RocketPy-Team/RocketPy/pull/521)
5165

5266
## [v1.1.4] - 2023-12-07
@@ -64,8 +78,8 @@ You can install this version by running `pip install rocketpy==1.1.3`
6478

6579
### Fixed
6680

67-
- FIX: Broken Function.get_value_opt for N-Dimensional Functions [#492](https://github.com/RocketPy-Team/RocketPy/pull/492)
68-
- FIX: Never ending Flight simulations when using a GenericMotor [#497](https://github.com/RocketPy-Team/RocketPy/pull/497)
81+
- FIX: Broken Function.get_value_opt for N-Dimensional Functions [#492](https://github.com/RocketPy-Team/RocketPy/pull/492)
82+
- FIX: Never ending Flight simulations when using a GenericMotor [#497](https://github.com/RocketPy-Team/RocketPy/pull/497)
6983

7084
## [v1.1.2] - 2023-11-25
7185

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
author = "RocketPy Team"
2525

2626
# The full version, including alpha/beta/rc tags
27-
release = "1.1.4"
27+
release = "1.1.5"
2828

2929

3030
# -- General configuration ---------------------------------------------------

docs/user/function.rst

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,62 @@ Here we have shown that we can integrate the gaussian function over a defined in
406406
# Compare the function with the integral
407407
Function.compare_plots([f, f_int], lower=-4, upper=4)
408408

409+
e. Export to a text file (CSV or TXT)
410+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
411+
412+
Since rocketpy version 1.2.0, the ``Function`` class supports exporting the
413+
source data to a CSV or TXT file. This is accomplished by the method
414+
:meth:`rocketpy.Function.savetxt` and allows for easy data exportation for
415+
further analysis.
416+
417+
Let's export the gaussian function to a CSV file:
418+
419+
.. jupyter-execute::
420+
421+
# Define the gaussian function
422+
def gaussian(x):
423+
return 1 / np.sqrt(2*np.pi) * np.exp(-x**2/2)
424+
425+
f = Function(gaussian, inputs="x", outputs="f(x)")
426+
427+
# Export to CSV
428+
f.savetxt("gaussian.csv", lower=-4, upper=4, samples=20, fmt="%.2f")
429+
430+
# Read the CSV file
431+
import pandas as pd
432+
pd.read_csv("gaussian.csv")
433+
434+
435+
.. jupyter-execute::
436+
437+
# Delete the CSV file
438+
import os
439+
os.remove("gaussian.csv")
440+
441+
f. Filter data
442+
~~~~~~~~~~~~~~
443+
444+
Since rocketpy version 1.2.0, the ``Function`` class supports filtering the
445+
source data. This is accomplished by the method :meth:`rocketpy.Function.low_pass_filter`
446+
and allows for easy data filtering for further analysis.
447+
448+
Let's filter an example function:
449+
450+
.. jupyter-execute::
451+
452+
x = np.linspace(-4, 4, 1000)
453+
y = np.sin(x) + np.random.normal(0, 0.1, 1000)
454+
455+
f = Function(list(zip(x, y)), inputs="x", outputs="f(x)")
456+
457+
# Filter the function
458+
f_filtered = f.low_pass_filter(0.5)
459+
460+
# Compare the function with the filtered function
461+
Function.compare_plots(
462+
[(f, "Original"), (f_filtered, "Filtered")], lower=-4, upper=4
463+
)
464+
409465
........
410466

411467
This guide shows some of the capabilities of the ``Function`` class, but there are many other functionalities to enhance your analysis. Do not hesitate in tanking a look at the documentation :class:`rocketpy.Function`.

docs/user/installation.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ If you want to choose a specific version to guarantee compatibility, you may ins
1919

2020
.. code-block:: shell
2121
22-
pip install rocketpy==1.1.4
22+
pip install rocketpy==1.1.5
2323
2424
2525
Optional Installation Method: ``conda``

rocketpy/environment/environment.py

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ class Environment:
111111
Environment.pressure : Function
112112
Air pressure in Pa as a function of altitude. Can be accessed as regular
113113
array, or called as a Function. See Function for more information.
114+
Environment.barometric_height : Function
115+
Geometric height above sea level in m as a function of pressure. Can be
116+
accessed as regular array, or called as a Function. See Function for
117+
more information.
114118
Environment.temperature : Function
115119
Air temperature in K as a function of altitude. Can be accessed as
116120
regular array, or called as a Function. See Function for more
@@ -1315,6 +1319,8 @@ def process_standard_atmosphere(self):
13151319

13161320
# Save temperature, pressure and wind profiles
13171321
self.pressure = self.pressure_ISA
1322+
self.barometric_height = self.barometric_height_ISA
1323+
13181324
self.temperature = self.temperature_ISA
13191325
self.wind_direction = Function(
13201326
0,
@@ -1433,6 +1439,7 @@ def process_custom_atmosphere(
14331439
if pressure is None:
14341440
# Use standard atmosphere
14351441
self.pressure = self.pressure_ISA
1442+
self.barometric_height = self.barometric_height_ISA
14361443
else:
14371444
# Use custom input
14381445
self.pressure = Function(
@@ -1441,6 +1448,11 @@ def process_custom_atmosphere(
14411448
outputs="Pressure (Pa)",
14421449
interpolation="linear",
14431450
)
1451+
self.barometric_height = self.pressure.inverse_function().set_discrete(
1452+
0, max_expected_height, 100, extrapolation="constant"
1453+
)
1454+
self.barometric_height.set_inputs("Pressure (Pa)")
1455+
self.barometric_height.set_outputs("Height Above Sea Level (m)")
14441456
# Check maximum height of custom pressure input
14451457
if not callable(self.pressure.source):
14461458
max_expected_height = max(self.pressure[-1, 0], max_expected_height)
@@ -1605,6 +1617,15 @@ def process_windy_atmosphere(self, model="ECMWF"):
16051617
outputs="Pressure (Pa)",
16061618
interpolation="linear",
16071619
)
1620+
# Linearly extrapolate pressure to ground level
1621+
bar_height = data_array[:, (0, 1)]
1622+
self.barometric_height = Function(
1623+
bar_height,
1624+
inputs="Pressure (Pa)",
1625+
outputs="Height Above Sea Level (m)",
1626+
interpolation="linear",
1627+
extrapolation="natural",
1628+
)
16081629
self.temperature = Function(
16091630
data_array[:, (1, 2)],
16101631
inputs="Height Above Sea Level (m)",
@@ -1732,6 +1753,15 @@ def process_wyoming_sounding(self, file):
17321753
outputs="Pressure (Pa)",
17331754
interpolation="linear",
17341755
)
1756+
# Linearly extrapolate pressure to ground level
1757+
bar_height = data_array[:, (0, 1)]
1758+
self.barometric_height = Function(
1759+
bar_height,
1760+
inputs="Pressure (Pa)",
1761+
outputs="Height Above Sea Level (m)",
1762+
interpolation="linear",
1763+
extrapolation="natural",
1764+
)
17351765

17361766
# Retrieve temperature from data array
17371767
data_array[:, 2] = data_array[:, 2] + 273.15 # Converts C to K
@@ -1845,6 +1875,7 @@ def process_noaaruc_sounding(self, file):
18451875

18461876
# Extract pressure as a function of height
18471877
pressure_array = []
1878+
barometric_height_array = []
18481879
for line in lines:
18491880
# Split line into columns
18501881
columns = re.split(" +", line)[1:]
@@ -1858,7 +1889,9 @@ def process_noaaruc_sounding(self, file):
18581889
if max(columns) != 99999:
18591890
# Save value
18601891
pressure_array.append(columns)
1892+
barometric_height_array.append([columns[1], columns[0]])
18611893
pressure_array = np.array(pressure_array)
1894+
barometric_height_array = np.array(barometric_height_array)
18621895

18631896
# Extract temperature as a function of height
18641897
temperature_array = []
@@ -1905,6 +1938,15 @@ def process_noaaruc_sounding(self, file):
19051938
outputs="Pressure (Pa)",
19061939
interpolation="linear",
19071940
)
1941+
# Converts 10*hPa to Pa and save values
1942+
barometric_height_array[:, 0] = 10 * barometric_height_array[:, 0]
1943+
self.barometric_height = Function(
1944+
barometric_height_array,
1945+
inputs="Pressure (Pa)",
1946+
outputs="Height Above Sea Level (m)",
1947+
interpolation="linear",
1948+
extrapolation="natural",
1949+
)
19081950

19091951
# Convert 10*C to K and save values
19101952
temperature_array[:, 1] = (
@@ -2274,6 +2316,15 @@ def process_forecast_reanalysis(self, file, dictionary):
22742316
outputs="Pressure (Pa)",
22752317
interpolation="linear",
22762318
)
2319+
# Linearly extrapolate pressure to ground level
2320+
bar_height = data_array[:, (0, 1)]
2321+
self.barometric_height = Function(
2322+
bar_height,
2323+
inputs="Pressure (Pa)",
2324+
outputs="Height Above Sea Level (m)",
2325+
interpolation="linear",
2326+
extrapolation="natural",
2327+
)
22772328
self.temperature = Function(
22782329
data_array[:, (1, 2)],
22792330
inputs="Height Above Sea Level (m)",
@@ -2803,6 +2854,15 @@ def select_ensemble_member(self, member=0):
28032854
outputs="Pressure (Pa)",
28042855
interpolation="linear",
28052856
)
2857+
# Linearly extrapolate pressure to ground level
2858+
bar_height = data_array[:, (0, 1)]
2859+
self.barometric_height = Function(
2860+
bar_height,
2861+
inputs="Pressure (Pa)",
2862+
outputs="Height Above Sea Level (m)",
2863+
interpolation="linear",
2864+
extrapolation="natural",
2865+
)
28062866
self.temperature = Function(
28072867
data_array[:, (1, 2)],
28082868
inputs="Height Above Sea Level (m)",
@@ -2965,7 +3025,12 @@ def pressure_function(h):
29653025
outputs="Pressure (Pa)",
29663026
)
29673027

2968-
return None
3028+
# Discretize Function to speed up the trajectory simulation.
3029+
self.barometric_height_ISA = self.pressure_ISA.inverse_function().set_discrete(
3030+
pressure[-1], pressure[0], 100, extrapolation="constant"
3031+
)
3032+
self.barometric_height_ISA.set_inputs("Pressure (Pa)")
3033+
self.barometric_height_ISA.set_outputs("Height Above Sea Level (m)")
29693034

29703035
def calculate_density_profile(self):
29713036
"""Compute the density of the atmosphere as a function of

0 commit comments

Comments
 (0)