Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
4c98f90
ENH: stabilityMargin as a function of mach and time
Gui-FernandesBR Jun 11, 2023
f29f405
ENH: adjust Flight class to new stability
Gui-FernandesBR Jun 11, 2023
2f32686
ENH: update plots and prints
Gui-FernandesBR Jun 11, 2023
6fecf20
TST: adjusting tests to the new stability margin
Gui-FernandesBR Jun 11, 2023
488cb0b
Merge branch 'develop' into enh/new-stability-margin
MateusStano Sep 27, 2023
c2c5b61
ENH: improve stability margin implementation and docs
MateusStano Sep 29, 2023
78e15ab
ENH: adapt prints and plots to new changes
MateusStano Sep 29, 2023
7203528
ENH: adapt utilities for new changes
MateusStano Sep 29, 2023
80177a1
TST: adapt tests for new changes
MateusStano Sep 29, 2023
c31c4e7
Fix code style issues with Black
lint-action Sep 29, 2023
eab0ab0
MAINT: remove unecessary todo
MateusStano Sep 29, 2023
4186339
Merge branch 'enh/new-stability-margin' of https://github.com/RocketP…
MateusStano Sep 29, 2023
80c712c
TST: fix total_lift_coeff_der test issues
MateusStano Sep 29, 2023
e110fff
ENH: improve stability related plots and prints
Gui-FernandesBR Sep 30, 2023
e70825c
Fix code style issues with Black
lint-action Sep 30, 2023
3eeb5a1
ENH: improve plots not related to stability
Gui-FernandesBR Sep 30, 2023
0036e17
Update rocketpy/simulation/flight.py
MateusStano Oct 1, 2023
312f137
ENH: remove unecessary static margin discritization
MateusStano Oct 6, 2023
95792f7
Merge branch 'enh/new-stability-margin' of https://github.com/RocketP…
MateusStano Oct 6, 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
28 changes: 22 additions & 6 deletions rocketpy/Flight.py
Original file line number Diff line number Diff line change
Expand Up @@ -452,8 +452,8 @@ class Flight:
Expressed as the absolute vale of the magnitude as a function
of frequency in Hz. Can be called or accessed as array.

Flight.staticMargin : Function
Rocket's static margin during flight in calibers.
Flight.stabilityMargin : rocketpy.Function
Rocket's stability margin during flight, in calibers.

Fluid Mechanics:
Flight.streamVelocityX : Function
Expand Down Expand Up @@ -2232,10 +2232,26 @@ def attitudeFrequencyResponse(self):
samplingFrequency=100,
)

@cached_property
def staticMargin(self):
"""Static margin of the rocket."""
return self.rocket.staticMargin
@funcify_method("Time (s)", "Stability Margin (c)", "spline", "constant")
def stabilityMargin(self):
"""Stability margin of the rocket along the flight, it considers the
variation of the center of pressure position according to the mach
number, as well as the variation of the center of gravity position
according to the propellant mass evolution.

Parameters
----------
None

Returns
-------
stability : rocketpy.Function
Stability margin as a rocketpy.Function of time. The stability margin
is defined as the distance between the center of pressure and the
center of gravity, divided by the rocket diameter.
"""
s = [(t, self.rocket.stabilityMargin(t, m)) for t, m in self.MachNumber]
return Function(s, "Time (s)", "Stability Margin (c)")

# Rail Button Forces
@funcify_method("Time (s)", "Upper Rail Button Normal Force (N)", "spline", "zero")
Expand Down
119 changes: 75 additions & 44 deletions rocketpy/Rocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,15 +102,14 @@ class Rocket:
Rocket.aerodynamicSurfaces : Components
Collection of aerodynamic surfaces of the rocket. Holds Nose cones,
Fin sets, and Tails.
Rocket.cpPosition : float
Rocket's center of pressure position relative to the user defined rocket
reference system. See `Rocket.centerOfDryMassPosition` for more information
regarding the reference system.
Expressed in meters.
Rocket.staticMargin : float
Float value corresponding to rocket static margin when
loaded with propellant in units of rocket diameter or
calibers.
Rocket.centerOfPressure : rocketpy.Function
Rocket's center of pressure position relative to the user defined
rocket reference system. See `Rocket.coordinateSystemOrientation`
for more information regarding the rocket's coordinate system.
Expressed in meters as a function of Mach number.
Rocket.totalLiftCoeffDer : rocketpy.Function
Rocket's total lift coefficient derivative as a function of Mach
number.
Rocket.powerOffDrag : Function
Rocket's drag coefficient as a function of Mach number when the
motor is off.
Expand Down Expand Up @@ -225,11 +224,6 @@ def __init__(
# Rail buttons data initialization
self.rail_buttons = Components()

self.cpPosition = 0
self.staticMargin = Function(
lambda x: 0, inputs="Time (s)", outputs="Static Margin (c)"
)

# Define aerodynamic drag coefficients
self.powerOffDrag = Function(
powerOffDrag,
Expand All @@ -245,7 +239,9 @@ def __init__(
"linear",
"constant",
)
self.cpPosition = 0 # Set by self.evaluateStaticMargin()
self.centerOfPressure = Function(
lambda mach: 0
) # This will be set by the self.evaluateCenterOfPressure()

# Create a, possibly, temporary empty motor
# self.motors = Components() # currently unused since only one motor is supported
Expand All @@ -262,8 +258,8 @@ def __init__(
self.evaluateReducedMass()
self.evaluateThrustToWeight()

# Evaluate static margin (even though no aerodynamic surfaces are present yet)
self.evaluateStaticMargin()
# Evaluate center of pressure (even if no aerodynamic surfaces is present yet)
self.evaluateCenterOfPressure()

# Initialize plots and prints object
self.prints = _RocketPrints(self)
Expand Down Expand Up @@ -388,48 +384,83 @@ def evaluateThrustToWeight(self):
self.thrustToWeight.setInputs("Time (s)")
self.thrustToWeight.setOutputs("Thrust/Weight")

def evaluateStaticMargin(self):
"""Calculates and returns the rocket's static margin when
loaded with propellant. The static margin is saved and returned
in units of rocket diameter or calibers. This function also calculates
the rocket center of pressure and total lift coefficients.
def evaluateCenterOfPressure(self):
"""Evaluates rocket center of pressure position relative to user defined
rocket reference system. It can be called as many times as needed, as it
will update the center of pressure function every time it is called. The
code will iterate through all aerodynamic surfaces and consider each of
their center of pressure position and derivative of the coefficient of
lift as a function of Mach number.

Parameters
----------
None

Returns
-------
self.staticMargin : float
Float value corresponding to rocket static margin when
loaded with propellant in units of rocket diameter or
calibers.
None
"""
# Initialize total lift coefficient derivative and center of pressure position
self.totalLiftCoeffDer = 0
self.cpPosition = 0
self.totalLiftCoeffDer = Function(lambda mach: 0)
self.centerOfPressure = Function(lambda mach: 0)

# Calculate total lift coefficient derivative and center of pressure
if len(self.aerodynamicSurfaces) > 0:
for surface, position in self.aerodynamicSurfaces:
self.totalLiftCoeffDer += surface.clalpha(0)
self.cpPosition += surface.clalpha(0) * (
self.totalLiftCoeffDer += surface.clalpha
self.centerOfPressure += surface.clalpha * (
position - self._csys * surface.cpz
)
self.cpPosition /= self.totalLiftCoeffDer

# Calculate static margin
self.staticMargin = (self.centerOfMass - self.cpPosition) / (2 * self.radius)
self.staticMargin *= (
self._csys
) # Change sign if coordinate system is upside down
self.staticMargin.setInputs("Time (s)")
self.staticMargin.setOutputs("Static Margin (c)")
self.staticMargin.setDiscrete(
lower=0, upper=self.motor.burnOutTime, samples=200
)
self.centerOfPressure /= self.totalLiftCoeffDer

self.centerOfPressure.setInputs("Mach Number")
self.centerOfPressure.setOutputs("Center of Pressure Position (m)")
self.totalLiftCoeffDer.setInputs("Mach Number")
self.totalLiftCoeffDer.setOutputs("Total Lift Coefficient Derivative")

return None

def stabilityMargin(self, time, mach):
"""Calculates the stability margin of the rocket, defined as the
distance between the center of pressure and the center of mass, divided
by the rocket's diameter. The mach number needs to be specified to
calculate the center of pressure position. The time is used to calculate
the center of mass position.

Parameters
----------
time : int, float
Time at which the center of mass will be calculated.
mach : int, float
Mach number at which the center of pressure will be calculated.

Returns
-------
stabilityMargin : float
Stability margin of the rocket, in calibers.
"""
cp = self.centerOfPressure(mach)
cm = self.centerOfMass(time)
return ((cm - cp) / (2 * self.radius)) * self._csys

def initialStabilityMargin(self, mach=0):
"""Calculates the initial stability margin of the rocket, when the
rocket is fully loaded with propellant and the motor is off. The mach
number is assumed to be zero by default, but can be changed by the user.

Parameters
----------
mach : int, optional
Mach number at which the center of pressure will be calculated,
by default 0, representing the static margin.

Returns
-------
stabilityMargin : float
Initial stability margin of the rocket, in calibers.
"""
return self.stabilityMargin(time=0, mach=mach)

def addMotor(self, motor, position):
"""Adds a motor to the rocket.

Expand Down Expand Up @@ -461,7 +492,7 @@ def addMotor(self, motor, position):
self.evaluateCenterOfMass()
self.evaluateReducedMass()
self.evaluateThrustToWeight()
self.evaluateStaticMargin()
self.evaluateCenterOfPressure()
return None

def addSurfaces(self, surfaces, positions):
Expand Down Expand Up @@ -498,7 +529,7 @@ def addSurfaces(self, surfaces, positions):
except TypeError:
self.aerodynamicSurfaces.add(surfaces, positions)

self.evaluateStaticMargin()
self.evaluateCenterOfPressure()
return None

def addTail(
Expand Down
4 changes: 2 additions & 2 deletions rocketpy/plots/flight_plots.py
Original file line number Diff line number Diff line change
Expand Up @@ -721,8 +721,8 @@ def stability_and_control_data(self):
fig9 = plt.figure(figsize=(9, 6))

ax1 = plt.subplot(211)
ax1.plot(self.flight.staticMargin[:, 0], self.flight.staticMargin[:, 1])
ax1.set_xlim(0, self.flight.staticMargin[:, 0][-1])
ax1.plot(self.flight.stabilityMargin[:, 0], self.flight.stabilityMargin[:, 1])
ax1.set_xlim(0, self.flight.stabilityMargin[:, 0][-1])
ax1.set_title("Static Margin")
ax1.set_xlabel("Time (s)")
ax1.set_ylabel("Static Margin (c)")
Expand Down
17 changes: 14 additions & 3 deletions rocketpy/plots/rocket_plots.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def reducedMass(self):

return None

def staticMargin(self):
def stabilityMargin(self):
"""Plots static margin of the rocket as a function of time.

Parameters
Expand All @@ -78,7 +78,18 @@ def staticMargin(self):
None
"""

self.rocket.staticMargin()
# TODO: it would be interesting to make a 3D plot of stability margin
# (https://matplotlib.org/stable/gallery/mplot3d/surface3d.html)

x = np.linspace(0, self.rocket.motor.burnOutTime, 20)
y = np.array([self.rocket.stabilityMargin(t, 0) for t in x])

plt.plot(x, y)
plt.xlabel("Time (s)")
plt.ylabel("Stability Margin (calibers)")
plt.title("Stability Margin (mach = 0)")
plt.grid()
plt.show()

return None

Expand Down Expand Up @@ -148,7 +159,7 @@ def all(self):
self.totalMass()
self.reducedMass()
print("\nAerodynamics Plots")
self.staticMargin()
self.stabilityMargin()
self.powerOnDrag()
self.powerOffDrag()
self.thrustToWeight()
Expand Down
2 changes: 1 addition & 1 deletion rocketpy/prints/flight_prints.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ def out_of_rail_conditions(self):
)
print(
"Rail Departure Static Margin: {:.3f} c".format(
self.flight.staticMargin(self.flight.outOfRailTime)
self.flight.stabilityMargin(self.flight.outOfRailTime)
)
)
print(
Expand Down
14 changes: 8 additions & 6 deletions rocketpy/prints/rocket_prints.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,18 +118,20 @@ def rocket_aerodynamics_quantities(self):
cpz = surface.cp[2]
print(name + " Center of Pressure to CM: {:.3f}".format(cpz) + " m")
print(
"Distance - Center of Pressure to CM: "
+ "{:.3f}".format(self.rocket.cpPosition)
"Distance - Center of Pressure to CM (Mach=0): "
+ "{:.3f}".format(self.rocket.centerOfPressure(0))
+ " m"
)
print(
"Initial Static Margin: "
+ "{:.3f}".format(self.rocket.staticMargin(0))
"Initial Stability Margin (Mach=0): "
+ "{:.3f}".format(self.rocket.initialStabilityMargin(mach=0))
+ " c"
)
print(
"Final Static Margin: "
+ "{:.3f}".format(self.rocket.staticMargin(self.rocket.motor.burnOutTime))
"Final Stability Margin (Mach=0): "
+ "{:.3f}".format(
self.rocket.stabilityMargin(time=self.rocket.motor.burnOutTime, mach=0)
)
+ " c"
)

Expand Down
2 changes: 1 addition & 1 deletion tests/test_flight.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def compute_static_margin_error_given_distance(position, static_margin, rocket):
length=0.060,
position=-1.194656,
)
return rocket.staticMargin(0) - static_margin
return rocket.stabilityMargin(0, 0) - static_margin

sol = optimize.root_scalar(
compute_static_margin_error_given_distance,
Expand Down
Loading