Skip to content

Commit

Permalink
Merge branch 'bug/airbrakes-postprocess' of https://github.com/Rocket…
Browse files Browse the repository at this point in the history
…Py-Team/RocketPy into bug/airbrakes-postprocess
  • Loading branch information
MateusStano committed May 16, 2024
2 parents 464f201 + 8b91f0b commit d730b40
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 10 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

### Added

- ENH: Complex step differentiation [#594](https://github.com/RocketPy-Team/RocketPy/pull/594)
- ENH: Exponential backoff decorator (fix #449) [#588](https://github.com/RocketPy-Team/RocketPy/pull/588)
- ENH: Function Validation Rework & Swap `np.searchsorted` to `bisect_left` [#582](https://github.com/RocketPy-Team/RocketPy/pull/582)
- ENH: Add new stability margin properties to Flight class [#572](https://github.com/RocketPy-Team/RocketPy/pull/572)
Expand All @@ -51,6 +52,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

### Fixed

- BUG: Fix minor type hinting problems [#598](https://github.com/RocketPy-Team/RocketPy/pull/598)
- BUG: Optional Dependencies Naming in pyproject.toml. [#592](https://github.com/RocketPy-Team/RocketPy/pull/592)
- BUG: Swap rocket.total_mass.differentiate for motor.total_mass_flow rate [#585](https://github.com/RocketPy-Team/RocketPy/pull/585)
- BUG: export_eng 'Motor' method would not work for liquid motors. [#559](https://github.com/RocketPy-Team/RocketPy/pull/559)
Expand Down
14 changes: 14 additions & 0 deletions docs/user/function.rst
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,7 @@ d. Differentiation and Integration
One of the most useful ``Function`` features for data analysis is easily differentiating and integrating the data source. These methods are divided as follow:

- :meth:`rocketpy.Function.differentiate`: differentiate the ``Function`` at a given point, returning the derivative value as the result;
- :meth:`rocketpy.Function.differentiate_complex_step`: differentiate the ``Function`` at a given point using the complex step method, returning the derivative value as the result;
- :meth:`rocketpy.Function.integral`: performs a definite integral over specified limits, returns the integral value (area under ``Function``);
- :meth:`rocketpy.Function.derivative_function`: computes the derivative of the given `Function`, returning another `Function` that is the derivative of the original at each point;
- :meth:`rocketpy.Function.integral_function`: calculates the definite integral of the function from a given point up to a variable, returns a ``Function``.
Expand All @@ -363,6 +364,19 @@ Let's make a familiar example of differentiation: the derivative of the function
# Differentiate it at x = 3
print(f.differentiate(3))

RocketPy also supports the complex step method for differentiation, which is a very accurate method for numerical differentiation. Let's compare the results of the complex step method with the standard method:

.. jupyter-execute::

# Define the function x^2
f = Function(lambda x: x**2)

# Differentiate it at x = 3 using the complex step method
print(f.differentiate_complex_step(3))

The complex step method can be as twice as faster as the standard method, but
it requires the function to be differentiable in the complex plane.

Also one may compute the derivative function:

.. jupyter-execute::
Expand Down
6 changes: 3 additions & 3 deletions rocketpy/environment/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,9 +269,9 @@ def __init__(
self,
gravity=None,
date=None,
latitude=0,
longitude=0,
elevation=0,
latitude=0.0,
longitude=0.0,
elevation=0.0,
datum="SIRGAS2000",
timezone="UTC",
max_expected_height=80000.0,
Expand Down
36 changes: 34 additions & 2 deletions rocketpy/mathutils/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -860,7 +860,7 @@ def get_value(self, *args):
# if the function is 1-D:
if self.__dom_dim__ == 1:
# if the args is a simple number (int or float)
if isinstance(args[0], (int, float)):
if isinstance(args[0], (int, float, complex)):
return self.source(args[0])
# if the arguments are iterable, we map and return a list
if isinstance(args[0], Iterable):
Expand All @@ -869,7 +869,7 @@ def get_value(self, *args):
# if the function is n-D:
else:
# if each arg is a simple number (int or float)
if all(isinstance(arg, (int, float)) for arg in args):
if all(isinstance(arg, (int, float, complex)) for arg in args):
return self.source(*args)
# if each arg is iterable, we map and return a list
if all(isinstance(arg, Iterable) for arg in args):
Expand Down Expand Up @@ -2427,6 +2427,38 @@ def differentiate(self, x, dx=1e-6, order=1):
+ self.get_value_opt(x - dx)
) / dx**2

def differentiate_complex_step(self, x, dx=1e-200, order=1):
"""Differentiate a Function object at a given point using the complex
step method. This method can be faster than ``Function.differentiate``
since it requires only one evaluation of the function. However, the
evaluated function must accept complex numbers as input.
Parameters
----------
x : float
Point at which to differentiate.
dx : float, optional
Step size to use for numerical differentiation, by default 1e-200.
order : int, optional
Order of differentiation, by default 1. Right now, only first order
derivative is supported.
Returns
-------
float
The real part of the derivative of the function at the given point.
References
----------
[1] https://mdolab.engin.umich.edu/wiki/guide-complex-step-derivative-approximation
"""
if order == 1:
return float(self.get_value_opt(x + dx * 1j).imag / dx)
else:
raise NotImplementedError(
"Only 1st order derivatives are supported yet. " "Set order=1."
)

def identity_function(self):
"""Returns a Function object that correspond to the identity mapping,
i.e. f(x) = x.
Expand Down
2 changes: 1 addition & 1 deletion rocketpy/motors/solid_motor.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ def __init__(
grain_separation,
grains_center_of_mass_position,
center_of_dry_mass_position,
nozzle_position=0,
nozzle_position=0.0,
burn_time=None,
throat_radius=0.01,
reshape_thrust_curve=False,
Expand Down
2 changes: 1 addition & 1 deletion rocketpy/rocket/rocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -954,7 +954,7 @@ def add_trapezoidal_fins(
tip_chord,
span,
position,
cant_angle=0,
cant_angle=0.0,
sweep_length=None,
sweep_angle=None,
radius=None,
Expand Down
6 changes: 3 additions & 3 deletions rocketpy/simulation/flight.py
Original file line number Diff line number Diff line change
Expand Up @@ -490,8 +490,8 @@ def __init__(
rocket,
environment,
rail_length,
inclination=80,
heading=90,
inclination=80.0,
heading=90.0,
initial_solution=None,
terminate_on_apogee=False,
max_time=600,
Expand Down Expand Up @@ -1139,7 +1139,7 @@ def __init_controllers(self):
"""Initialize controllers"""
self._controllers = self.rocket._controllers[:]
if self._controllers:
if self.time_overshoot == True:
if self.time_overshoot:
self.time_overshoot = False

Check warning on line 1143 in rocketpy/simulation/flight.py

View check run for this annotation

Codecov / codecov/patch

rocketpy/simulation/flight.py#L1143

Added line #L1143 was not covered by tests
warnings.warn(
"time_overshoot has been set to False due to the presence of controllers. "
Expand Down
31 changes: 31 additions & 0 deletions tests/unit/test_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,37 @@ def test_differentiate(func_input, derivative_input, expected_derivative):
assert np.isclose(func.differentiate(derivative_input), expected_derivative)


@pytest.mark.parametrize(
"func_input, derivative_input, expected_first_derivative",
[
(1, 0, 0), # Test case 1: Function(1)
(lambda x: x, 0, 1), # Test case 2: Function(lambda x: x)
(lambda x: x**2, 1, 2), # Test case 3: Function(lambda x: x**2)
(lambda x: -(x**3), 2, -12), # Test case 4: Function(lambda x: -x**3)
],
)
def test_differentiate_complex_step(
func_input, derivative_input, expected_first_derivative
):
"""Test the differentiate_complex_step method of the Function class.
Parameters
----------
func_input : function
A function object created from a list of values.
derivative_input : int
Point at which to differentiate.
expected_derivative : float
Expected value of the derivative.
"""
func = Function(func_input)
assert isinstance(func.differentiate_complex_step(x=derivative_input), float)
assert np.isclose(
func.differentiate_complex_step(x=derivative_input, order=1),
expected_first_derivative,
)


def test_get_value():
"""Tests the get_value method of the Function class.
Both with respect to return instances and expected behaviour.
Expand Down

0 comments on commit d730b40

Please sign in to comment.