Skip to content

Commit

Permalink
Merge branch 'develop' into enh/class_dispersion
Browse files Browse the repository at this point in the history
  • Loading branch information
Gui-FernandesBR committed Mar 5, 2024
2 parents a5267ae + 65b3315 commit 3dbb1f8
Show file tree
Hide file tree
Showing 35 changed files with 1,813 additions and 1,336 deletions.
11 changes: 10 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,23 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

### Added

- ENH: adds `Function.remove_outliers` method [#554](https://github.com/RocketPy-Team/RocketPy/pull/554)

### Changed
- ENH: Optional argument to show the plot in Function.compare_plots [#563](https://github.com/RocketPy-Team/RocketPy/pull/563)

### Fixed
- BUG: export_eng 'Motor' method would not work for liquid motors. [#559](https://github.com/RocketPy-Team/RocketPy/pull/559)

## [v1.2.1] - 2024-02-22

You can install this version by running `pip install rocketpy==1.2.1`

### Fixed

- BUG: Add reference area factor correction to aero surfaces (solves #557) [#558](https://github.com/RocketPy-Team/RocketPy/pull/558)

## [v1.2.0] - 2024-02-dd
## [v1.2.0] - 2024-02-12

You can install this version by running `pip install rocketpy==1.2.0`

Expand Down
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
author = "RocketPy Team"

# The full version, including alpha/beta/rc tags
release = "1.1.5"
release = "1.2.1"


# -- General configuration ---------------------------------------------------
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.1.5
pip install rocketpy==1.2.1
Optional Installation Method: ``conda``
Expand Down
86 changes: 69 additions & 17 deletions rocketpy/mathutils/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -1144,6 +1144,51 @@ def low_pass_filter(self, alpha, file_path=None):
title=self.title,
)

def remove_outliers_iqr(self, threshold=1.5):
"""Remove outliers from the Function source using the interquartile
range method. The Function should have an array-like source.
Parameters
----------
threshold : float, optional
Threshold for the interquartile range method. Default is 1.5.
Returns
-------
Function
The Function with the outliers removed.
References
----------
[1] https://en.wikipedia.org/wiki/Outlier#Tukey's_fences
"""

if callable(self.source):
raise TypeError(
"Cannot remove outliers if the source is a callable object."
+ " The Function.source should be array-like."
)

x = self.x_array
y = self.y_array
y_q1 = np.percentile(y, 25)
y_q3 = np.percentile(y, 75)
y_iqr = y_q3 - y_q1
y_lower = y_q1 - threshold * y_iqr
y_upper = y_q3 + threshold * y_iqr

y_filtered = y[(y >= y_lower) & (y <= y_upper)]
x_filtered = x[(y >= y_lower) & (y <= y_upper)]

return Function(
source=np.column_stack((x_filtered, y_filtered)),
inputs=self.__inputs__,
outputs=self.__outputs__,
interpolation=self.__interpolation__,
extrapolation=self.__extrapolation__,
title=self.title,
)

# Define all presentation methods
def __call__(self, *args):
"""Plot the Function if no argument is given. If an
Expand Down Expand Up @@ -1474,45 +1519,51 @@ def compare_plots(
force_data=False,
force_points=False,
return_object=False,
show=True,
):
"""Plots N 1-Dimensional Functions in the same plot, from a lower
limit to an upper limit, by sampling the Functions several times in
the interval.
Parameters
----------
plot_list : list
plot_list : list[Tuple[Function,str]]
List of Functions or list of tuples in the format (Function,
label), where label is a string which will be displayed in the
legend.
lower : scalar, optional
The lower limit of the interval in which the Functions are to be
plotted. The default value for function type Functions is 0. By
contrast, if the Functions given are defined by a dataset, the
default value is the lowest value of the datasets.
upper : scalar, optional
The upper limit of the interval in which the Functions are to be
plotted. The default value for function type Functions is 10. By
contrast, if the Functions given are defined by a dataset, the
default value is the highest value of the datasets.
lower : float, optional
This represents the lower limit of the interval for plotting the
Functions. If the Functions are defined by a dataset, the smallest
value from the dataset is used. If no value is provided (None), and
the Functions are of Function type, 0 is used as the default.
upper : float, optional
This represents the upper limit of the interval for plotting the
Functions. If the Functions are defined by a dataset, the largest
value from the dataset is used. If no value is provided (None), and
the Functions are of Function type, 10 is used as the default.
samples : int, optional
The number of samples in which the functions will be evaluated for
plotting it, which draws lines between each evaluated point.
The default value is 1000.
title : string, optional
title : str, optional
Title of the plot. Default value is an empty string.
xlabel : string, optional
xlabel : str, optional
X-axis label. Default value is an empty string.
ylabel : string, optional
ylabel : str, optional
Y-axis label. Default value is an empty string.
force_data : Boolean, optional
force_data : bool, optional
If Function is given by an interpolated dataset, setting force_data
to True will plot all points, as a scatter, in the dataset.
Default value is False.
force_points : Boolean, optional
force_points : bool, optional
Setting force_points to True will plot all points, as a scatter, in
which the Function was evaluated to plot it. Default value is
False.
return_object : bool, optional
If True, returns the figure and axis objects. Default value is
False.
show : bool, optional
If True, shows the plot. Default value is True.
Returns
-------
Expand Down Expand Up @@ -1586,7 +1637,8 @@ def compare_plots(
plt.xlabel(xlabel)
plt.ylabel(ylabel)

plt.show()
if show:
plt.show()

if return_object:
return fig, ax
Expand Down
49 changes: 27 additions & 22 deletions rocketpy/motors/motor.py
Original file line number Diff line number Diff line change
Expand Up @@ -1012,31 +1012,36 @@ def export_eng(self, file_name, motor_name):
None
"""
# Open file
file = open(file_name, "w")

# Write first line
file.write(
motor_name
+ " {:3.1f} {:3.1f} 0 {:2.3} {:2.3} RocketPy\n".format(
2000 * self.grain_outer_radius,
1000
* self.grain_number
* (self.grain_initial_height + self.grain_separation),
self.propellant_initial_mass,
self.propellant_initial_mass,
)
)
with open(file_name, "w") as file:
# Write first line
def get_attr_value(obj, attr_name, multiplier=1):
return multiplier * getattr(obj, attr_name, 0)

grain_outer_radius = get_attr_value(self, "grain_outer_radius", 2000)
grain_number = get_attr_value(self, "grain_number", 1000)
grain_initial_height = get_attr_value(self, "grain_initial_height")
grain_separation = get_attr_value(self, "grain_separation")

grain_total = grain_number * (grain_initial_height + grain_separation)

if grain_outer_radius == 0 or grain_total == 0:
warnings.warn(
"The motor object doesn't have some grain-related attributes. "
"Using zeros to write to file."
)

# Write thrust curve data points
for time, thrust in self.thrust.source[1:-1, :]:
# time, thrust = item
file.write("{:.4f} {:.3f}\n".format(time, thrust))
file.write(
f"{motor_name} {grain_outer_radius:3.1f} {grain_total:3.1f} 0 "
f"{self.propellant_initial_mass:2.3} "
f"{self.propellant_initial_mass:2.3} RocketPy\n"
)

# Write last line
file.write("{:.4f} {:.3f}\n".format(self.thrust.source[-1, 0], 0))
# Write thrust curve data points
for time, thrust in self.thrust.source[1:-1, :]:
file.write(f"{time:.4f} {thrust:.3f}\n")

# Close file
file.close()
# Write last line
file.write(f"{self.thrust.source[-1, 0]:.4f} {0:.3f}\n")

return None

Expand Down
9 changes: 8 additions & 1 deletion rocketpy/prints/rocket_prints.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,13 @@ def rocket_aerodynamics_quantities(self):
print("\nAerodynamics Lift Coefficient Derivatives\n")
for surface, position in self.rocket.aerodynamic_surfaces:
name = surface.name
# ref_factor corrects lift for different reference areas
ref_factor = (surface.rocket_radius / self.rocket.radius) ** 2
print(
name
+ " Lift Coefficient Derivative: {:.3f}".format(surface.clalpha(0))
+ " Lift Coefficient Derivative: {:.3f}".format(
ref_factor * surface.clalpha(0)
)
+ "/rad"
)

Expand All @@ -135,6 +139,9 @@ def rocket_aerodynamics_quantities(self):
print(
f"Center of Mass position (time=0): {self.rocket.center_of_mass(0):.3f} m"
)
print(
f"Center of Pressure position (time=0): {self.rocket.cp_position(0):.3f} m"
)
print(
"Initial Static Margin (mach=0, time=0): "
+ "{:.3f}".format(self.rocket.static_margin(0))
Expand Down
12 changes: 10 additions & 2 deletions rocketpy/rocket/aero_surface.py
Original file line number Diff line number Diff line change
Expand Up @@ -349,12 +349,20 @@ def evaluate_geometrical_parameters(self):
# If base radius is not given, the ratio between base radius and
# rocket radius is assumed as 1, meaning that the nose cone has the
# same radius as the rocket
if self.base_radius is None or self.rocket_radius is None:
if self.base_radius is None and self.rocket_radius is not None:
self.radius_ratio = 1
self.base_radius = self.rocket_radius
elif self.base_radius is not None and self.rocket_radius is None:
self.radius_ratio = 1
self.rocket_radius = self.base_radius
# If base radius is given, the ratio between base radius and rocket
# radius is calculated
else:
elif self.base_radius is not None and self.rocket_radius is not None:
self.radius_ratio = self.base_radius / self.rocket_radius
else:
raise ValueError(
"Either base radius or rocket radius must be given to calculate the nose cone radius ratio."
)

self.fineness_ratio = self.length / (2 * self.base_radius)
return None
Expand Down
31 changes: 21 additions & 10 deletions rocketpy/rocket/rocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -517,9 +517,13 @@ def evaluate_center_of_pressure(self):
# Calculate total lift coefficient derivative and center of pressure
if len(self.aerodynamic_surfaces) > 0:
for aero_surface, position in self.aerodynamic_surfaces:
self.total_lift_coeff_der += aero_surface.clalpha
self.cp_position += aero_surface.clalpha * (
position - self._csys * aero_surface.cpz
# ref_factor corrects lift for different reference areas
ref_factor = (aero_surface.rocket_radius / self.radius) ** 2
self.total_lift_coeff_der += ref_factor * aero_surface.clalpha
self.cp_position += (
ref_factor
* aero_surface.clalpha
* (position - self._csys * aero_surface.cpz)
)
self.cp_position /= self.total_lift_coeff_der

Expand Down Expand Up @@ -871,7 +875,9 @@ def add_tail(
self.add_surfaces(tail, position)
return tail

def add_nose(self, length, kind, position, bluffness=0, name="Nose Cone"):
def add_nose(
self, length, kind, position, bluffness=0, name="Nose Cone", base_radius=None
):
"""Creates a nose cone, storing its parameters as part of the
aerodynamic_surfaces list. Its parameters are the axial position
along the rocket and its derivative of the coefficient of lift
Expand All @@ -894,6 +900,9 @@ def add_nose(self, length, kind, position, bluffness=0, name="Nose Cone"):
the radius of the base of the ogive.
name : string
Nose cone name. Default is "Nose Cone".
base_radius : int, float, optional
Nose cone base radius in meters. If not given, the rocket radius
will be used.
See Also
--------
Expand All @@ -907,8 +916,8 @@ def add_nose(self, length, kind, position, bluffness=0, name="Nose Cone"):
nose = NoseCone(
length=length,
kind=kind,
base_radius=self.radius,
rocket_radius=self.radius,
base_radius=base_radius or self.radius,
rocket_radius=base_radius or self.radius,
bluffness=bluffness,
name=name,
)
Expand Down Expand Up @@ -983,8 +992,9 @@ def add_trapezoidal_fins(
with its base perpendicular to the rocket's axis. Cannot be used in
conjunction with sweep_length.
radius : int, float, optional
Reference radius to calculate lift coefficient. If None, which is
default, use rocket radius.
Reference fuselage radius where the fins are located. This is used
to calculate lift coefficient and to draw the rocket. If None,
which is default, the rocket radius will be used.
airfoil : tuple, optional
Default is null, in which case fins will be treated as flat plates.
Otherwise, if tuple, fins will be considered as airfoils. The
Expand Down Expand Up @@ -1064,8 +1074,9 @@ def add_elliptical_fins(
Fins cant angle with respect to the rocket centerline. Must be given
in degrees.
radius : int, float, optional
Reference radius to calculate lift coefficient. If None, which
is default, use rocket radius.
Reference fuselage radius where the fins are located. This is used
to calculate lift coefficient and to draw the rocket. If None,
which is default, the rocket radius will be used.
airfoil : tuple, optional
Default is null, in which case fins will be treated as flat plates.
Otherwise, if tuple, fins will be considered as airfoils. The
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

setuptools.setup(
name="rocketpy",
version="1.1.5",
version="1.2.1",
install_requires=necessary_require,
extras_require={
"env_analysis": env_analysis_require,
Expand Down
Loading

0 comments on commit 3dbb1f8

Please sign in to comment.