Skip to content

Commit c3f9a13

Browse files
Merge pull request #423 from RocketPy-Team/bug/motor-coordinates
Bug/motor coordinates
2 parents 92922c6 + a4155f3 commit c3f9a13

File tree

5 files changed

+146
-95
lines changed

5 files changed

+146
-95
lines changed

rocketpy/prints/rocket_prints.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,8 @@ def rocket_geometrical_parameters(self):
9494
"Rocket Center of Dry Mass - Nozzle Exit Distance: "
9595
+ "{:.3f} m".format(
9696
abs(
97-
self.rocket.center_of_dry_mass_position - self.rocket.motor_position
97+
self.rocket.center_of_dry_mass_position
98+
- self.rocket.nozzle_position
9899
)
99100
)
100101
)

rocketpy/rocket/rocket.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,16 @@ class Rocket:
100100
Rocket.motor : Motor
101101
Rocket's motor. See Motor class for more details.
102102
Rocket.motor_position : float
103-
Position, in m, of the motor's nozzle exit area relative to the user
104-
defined rocket coordinate system. See
105-
:doc:`Positions and Coordinate Systems </user/positions>`
106-
for more information
103+
Position, in meters, of the motor's coordinate system origin
104+
relative to the user defined rocket coordinate system.
105+
See :doc:`Positions and Coordinate Systems </user/positions>`
106+
for more information.
107107
regarding the rocket's coordinate system.
108+
Rocket.nozzle_position : float
109+
Position, in meters, of the motor's nozzle exit relative to the user
110+
defined rocket coordinate system.
111+
See :doc:`Positions and Coordinate Systems </user/positions>`
112+
for more information.
108113
Rocket.center_of_propellant_position : Function
109114
Position of the propellant's center of mass relative to the user defined
110115
rocket reference system. See
@@ -642,14 +647,15 @@ def add_motor(self, motor, position):
642647
self.motor_position = position
643648
_ = self._csys * self.motor._csys
644649
self.center_of_propellant_position = (
645-
self.motor.center_of_propellant_mass - self.motor.nozzle_position
646-
) * _ + self.motor_position
650+
self.motor.center_of_propellant_mass * _ + self.motor_position
651+
)
647652
self.motor_center_of_mass_position = (
648-
self.motor.center_of_mass - self.motor.nozzle_position
649-
) * _ + self.motor_position
653+
self.motor.center_of_mass * _ + self.motor_position
654+
)
650655
self.motor_center_of_dry_mass_position = (
651-
self.motor.center_of_dry_mass_position - self.motor.nozzle_position
652-
) * _ + self.motor_position
656+
self.motor.center_of_dry_mass_position * _ + self.motor_position
657+
)
658+
self.nozzle_position = self.motor.nozzle_position * _ + self.motor_position
653659
self.evaluate_dry_mass()
654660
self.evaluate_total_mass()
655661
self.evaluate_center_of_dry_mass()

rocketpy/simulation/flight.py

Lines changed: 13 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1173,7 +1173,10 @@ def __init_equations_of_motion(self):
11731173

11741174
@cached_property
11751175
def effective_1rl(self):
1176-
nozzle = self.rocket.motor_position
1176+
"""Original rail length minus the distance measured from nozzle exit
1177+
to the upper rail button. It assumes the nozzle to be aligned with
1178+
the beginning of the rail."""
1179+
nozzle = self.rocket.nozzle_position
11771180
try:
11781181
rail_buttons = self.rocket.rail_buttons[0]
11791182
upper_r_button = (
@@ -1187,7 +1190,10 @@ def effective_1rl(self):
11871190

11881191
@cached_property
11891192
def effective_2rl(self):
1190-
nozzle = self.rocket.motor_position
1193+
"""Original rail length minus the distance measured from nozzle exit
1194+
to the lower rail button. It assumes the nozzle to be aligned with
1195+
the beginning of the rail."""
1196+
nozzle = self.rocket.nozzle_position
11911197
try:
11921198
rail_buttons = self.rocket.rail_buttons[0]
11931199
lower_r_button = rail_buttons.position
@@ -1377,7 +1383,7 @@ def u_dot(self, t, u, post_processing=False):
13771383
)
13781384
# c = -self.rocket.distance_rocket_nozzle
13791385
c = (
1380-
-(self.rocket.motor_position - self.rocket.center_of_dry_mass_position)
1386+
-(self.rocket.nozzle_position - self.rocket.center_of_dry_mass_position)
13811387
* self.rocket._csys
13821388
)
13831389
a = b * Mt / M
@@ -1632,7 +1638,7 @@ def u_dot_generalized(self, t, u, post_processing=False):
16321638
r_CM_ddot = Vector([0, 0, r_CM_z.differentiate(t, order=2)])
16331639
## Nozzle gyration tensor
16341640
r_NOZ = (
1635-
-(self.rocket.motor_position - self.rocket.center_of_dry_mass_position)
1641+
-(self.rocket.nozzle_position - self.rocket.center_of_dry_mass_position)
16361642
* self.rocket._csys
16371643
)
16381644
S_noz_33 = 0.5 * self.rocket.motor.nozzle_radius**2
@@ -2411,23 +2417,10 @@ def aerodynamic_spin_moment(self):
24112417
# Kinetic Energy
24122418
@funcify_method("Time (s)", "Rotational Kinetic Energy (J)")
24132419
def rotational_energy(self):
2414-
# b = -self.rocket.distanceRocketPropellant
2415-
b = (
2416-
-(self.rocket.motor_position - self.rocket.center_of_dry_mass_position)
2417-
* self.rocket._csys
2418-
)
2419-
mu = self.rocket.reduced_mass
2420-
Rz = self.rocket.dry_I_33
2421-
Ri = self.rocket.dry_I_11
2422-
Tz = self.rocket.motor.I_33
2423-
Ti = self.rocket.motor.I_11
2424-
I1, I2, I3 = (Ri + Ti + mu * b**2), (Ri + Ti + mu * b**2), (Rz + Tz)
2425-
# Redefine I1, I2 and I3 time grid to allow for efficient Function algebra
2426-
I1.set_discrete_based_on_model(self.w1)
2427-
I2.set_discrete_based_on_model(self.w1)
2428-
I3.set_discrete_based_on_model(self.w1)
24292420
rotational_energy = 0.5 * (
2430-
I1 * self.w1**2 + I2 * self.w2**2 + I3 * self.w3**2
2421+
self.rocket.I_11 * self.w1**2
2422+
+ self.rocket.I_22 * self.w2**2
2423+
+ self.rocket.I_33 * self.w3**2
24312424
)
24322425
rotational_energy.set_discrete_based_on_model(self.w1)
24332426
return rotational_energy
@@ -2564,41 +2557,6 @@ def static_margin(self):
25642557
"""Static margin of the rocket."""
25652558
return self.rocket.static_margin
25662559

2567-
# Rail Button Forces
2568-
@cached_property
2569-
def effective_1rl(self):
2570-
"""Original rail length minus the distance measured from nozzle exit
2571-
to the upper rail button. It assumes the nozzle to be aligned with
2572-
the beginning of the rail."""
2573-
nozzle = (
2574-
self.rocket.motor_position - self.rocket.center_of_dry_mass_position
2575-
) * self.rocket._csys # Kinda works for single nozzle
2576-
try:
2577-
rail_buttons = self.rocket.rail_buttons[0]
2578-
upper_r_button = (
2579-
rail_buttons.component.buttons_distance + rail_buttons.position
2580-
)
2581-
except IndexError: # No rail buttons defined
2582-
upper_r_button = nozzle
2583-
effective_1rl = self.rail_length - abs(nozzle - upper_r_button)
2584-
return effective_1rl
2585-
2586-
@cached_property
2587-
def effective_2rl(self):
2588-
"""Original rail length minus the distance measured from nozzle exit
2589-
to the lower rail button. It assumes the nozzle to be aligned with
2590-
the beginning of the rail."""
2591-
nozzle = (
2592-
self.rocket.motor_position - self.rocket.center_of_dry_mass_position
2593-
) * self.rocket._csys
2594-
try:
2595-
rail_buttons = self.rocket.rail_buttons[0]
2596-
lower_r_button = rail_buttons.position
2597-
except IndexError: # No rail buttons defined
2598-
lower_r_button = nozzle
2599-
effective_2rl = self.rail_length - abs(nozzle - lower_r_button)
2600-
return effective_2rl
2601-
26022560
@cached_property
26032561
def frontal_surface_wind(self):
26042562
"""Surface wind speed in m/s aligned with the launch rail."""

tests/conftest.py

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -111,16 +111,10 @@ def cesaroni_m1670(): # old name: solid_motor
111111

112112

113113
@pytest.fixture
114-
def calisto(cesaroni_m1670): # old name: rocket
114+
def calisto_motorless():
115115
"""Create a simple object of the Rocket class to be used in the tests. This
116-
is the same rocket that has been used in the getting started guide for
117-
years. The Calisto rocket is the Projeto Jupiter's project launched at the
118-
2019 Spaceport America Cup.
119-
120-
Parameters
121-
----------
122-
cesaroni_m1670 : rocketpy.SolidMotor
123-
An object of the SolidMotor class. This is a pytest fixture too.
116+
is the same rocket that has been used in the getting started guide for years
117+
but without a motor.
124118
125119
Returns
126120
-------
@@ -136,60 +130,71 @@ def calisto(cesaroni_m1670): # old name: rocket
136130
center_of_mass_without_motor=0,
137131
coordinate_system_orientation="tail_to_nose",
138132
)
133+
return calisto
134+
135+
136+
@pytest.fixture
137+
def calisto(calisto_motorless, cesaroni_m1670): # old name: rocket
138+
"""Create a simple object of the Rocket class to be used in the tests. This
139+
is the same rocket that has been used in the getting started guide for
140+
years. The Calisto rocket is the Projeto Jupiter's project launched at the
141+
2019 Spaceport America Cup.
142+
143+
Parameters
144+
----------
145+
calisto_motorless : rocketpy.Rocket
146+
An object of the Rocket class. This is a pytest fixture too.
147+
cesaroni_m1670 : rocketpy.SolidMotor
148+
An object of the SolidMotor class. This is a pytest fixture too.
149+
150+
Returns
151+
-------
152+
rocketpy.Rocket
153+
A simple object of the Rocket class
154+
"""
155+
calisto = calisto_motorless
139156
calisto.add_motor(cesaroni_m1670, position=-1.373)
140157
return calisto
141158

142159

143160
@pytest.fixture
144-
def calisto_liquid_modded(liquid_motor):
161+
def calisto_liquid_modded(calisto_motorless, liquid_motor):
145162
"""Create a simple object of the Rocket class to be used in the tests. This
146163
is an example of the Calisto rocket with a liquid motor.
147164
148165
Parameters
149166
----------
167+
calisto_motorless : rocketpy.Rocket
168+
An object of the Rocket class. This is a pytest fixture too.
150169
liquid_motor : rocketpy.LiquidMotor
151170
152171
Returns
153172
-------
154173
rocketpy.Rocket
155174
A simple object of the Rocket class
156175
"""
157-
calisto = Rocket(
158-
radius=0.0635,
159-
mass=14.426,
160-
inertia=(6.321, 6.321, 0.034),
161-
power_off_drag="data/calisto/powerOffDragCurve.csv",
162-
power_on_drag="data/calisto/powerOnDragCurve.csv",
163-
center_of_mass_without_motor=0,
164-
coordinate_system_orientation="tail_to_nose",
165-
)
176+
calisto = calisto_motorless
166177
calisto.add_motor(liquid_motor, position=-1.373)
167178
return calisto
168179

169180

170181
@pytest.fixture
171-
def calisto_hybrid_modded(hybrid_motor):
182+
def calisto_hybrid_modded(calisto_motorless, hybrid_motor):
172183
"""Create a simple object of the Rocket class to be used in the tests. This
173184
is an example of the Calisto rocket with a hybrid motor.
174185
175186
Parameters
176187
----------
188+
calisto_motorless : rocketpy.Rocket
189+
An object of the Rocket class. This is a pytest fixture too.
177190
hybrid_motor : rocketpy.HybridMotor
178191
179192
Returns
180193
-------
181194
rocketpy.Rocket
182195
A simple object of the Rocket class
183196
"""
184-
calisto = Rocket(
185-
radius=0.0635,
186-
mass=14.426,
187-
inertia=(6.321, 6.321, 0.034),
188-
power_off_drag="data/calisto/powerOffDragCurve.csv",
189-
power_on_drag="data/calisto/powerOnDragCurve.csv",
190-
center_of_mass_without_motor=0,
191-
coordinate_system_orientation="tail_to_nose",
192-
)
197+
calisto = calisto_motorless
193198
calisto.add_motor(hybrid_motor, position=-1.373)
194199
return calisto
195200

tests/test_rocket.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,87 @@ def test_add_fins_assert_cp_cm_plus_fins(calisto, dimensionless_calisto, m):
379379
)
380380

381381

382+
@pytest.mark.parametrize(
383+
"""cdm_position, grain_cm_position, nozzle_position, coord_direction,
384+
motor_position, expected_motor_cdm, expected_motor_cpp""",
385+
[
386+
(0.317, 0.397, 0, "nozzle_to_combustion_chamber", -1.373, -1.056, -0.976),
387+
(0, 0.08, -0.317, "nozzle_to_combustion_chamber", -1, -1, -0.92),
388+
(-0.317, -0.397, 0, "combustion_chamber_to_nozzle", -1.373, -1.056, -0.976),
389+
(0, -0.08, 0.317, "combustion_chamber_to_nozzle", -1, -1, -0.92),
390+
(1.317, 1.397, 1, "nozzle_to_combustion_chamber", -2.373, -1.056, -0.976),
391+
],
392+
)
393+
def test_add_motor_coordinates(
394+
calisto_motorless,
395+
cdm_position,
396+
grain_cm_position,
397+
nozzle_position,
398+
coord_direction,
399+
motor_position,
400+
expected_motor_cdm,
401+
expected_motor_cpp,
402+
):
403+
"""Test the method add_motor and related position properties in a Rocket
404+
instance.
405+
406+
This test checks the correctness of the `add_motor` method and the computed
407+
`motor_center_of_dry_mass_position` and `center_of_propellant_position`
408+
properties in the `Rocket` class using various parameters related to the
409+
motor's position, nozzle's position, and other related coordinates.
410+
Different scenarios are tested using parameterization, checking scenarios
411+
moving from the nozzle to the combustion chamber and vice versa, and with
412+
various specific physical and geometrical characteristics of the motor.
413+
414+
Parameters
415+
----------
416+
calisto_motorless : Rocket instance
417+
A predefined instance of a Rocket without a motor, used as a base for testing.
418+
cdm_position : float
419+
Position of the center of dry mass of the motor.
420+
grain_cm_position : float
421+
Position of the grains' center of mass.
422+
nozzle_position : float
423+
Position of the nozzle.
424+
coord_direction : str
425+
Direction for coordinate system orientation;
426+
it can be "nozzle_to_combustion_chamber" or "combustion_chamber_to_nozzle".
427+
motor_position : float
428+
Position where the motor should be added to the rocket.
429+
expected_motor_cdm : float
430+
Expected position of the motor's center of dry mass after addition.
431+
expected_motor_cpp : float
432+
Expected position of the center of propellant after addition.
433+
"""
434+
example_motor = SolidMotor(
435+
thrust_source="data/motors/Cesaroni_M1670.eng",
436+
burn_time=3.9,
437+
dry_mass=0,
438+
dry_inertia=(0, 0, 0),
439+
center_of_dry_mass_position=cdm_position,
440+
nozzle_position=nozzle_position,
441+
grain_number=5,
442+
grain_density=1815,
443+
nozzle_radius=33 / 1000,
444+
throat_radius=11 / 1000,
445+
grain_separation=5 / 1000,
446+
grain_outer_radius=33 / 1000,
447+
grain_initial_height=120 / 1000,
448+
grains_center_of_mass_position=grain_cm_position,
449+
grain_initial_inner_radius=15 / 1000,
450+
interpolation_method="linear",
451+
coordinate_system_orientation=coord_direction,
452+
)
453+
calisto = calisto_motorless
454+
calisto.add_motor(example_motor, position=motor_position)
455+
456+
calculated_motor_cdm = calisto.motor_center_of_dry_mass_position
457+
calculated_motor_cpp = calisto.center_of_propellant_position
458+
459+
assert pytest.approx(expected_motor_cdm) == calculated_motor_cdm
460+
assert pytest.approx(expected_motor_cpp) == calculated_motor_cpp(0)
461+
462+
382463
def test_add_cm_eccentricity_assert_properties_set(calisto):
383464
calisto.add_cm_eccentricity(x=4, y=5)
384465

0 commit comments

Comments
 (0)