Summary
LiquidMotor.propellant_I_33 is hard-coded to return 0, so liquid propellant contributes no roll-axis inertia to the 6-DOF dynamics. The physical quantity is far from negligible for launch-vehicle-scale tanks (e.g. a 3,267 kg LOX column in a r = 0.7 m cylindrical tank carries ½mr² ≈ 800 kg·m²; vehicle-scale tanks reach 10³–10⁴ kg·m²).
The building block already exists: TankGeometry.Iz_volume (tank_geometry.py:315-335) implements the volume-of-inertia about the tank axis but has no callers anywhere in the repo.
Version
RocketPy 1.13.0 (pip), Python 3.14, Windows.
Minimal reproduction
import math
from rocketpy import Fluid
from rocketpy.motors import LiquidMotor
from rocketpy.motors.tank import MassFlowRateBasedTank
from rocketpy.motors.tank_geometry import CylindricalTank
geo = CylindricalTank(radius_function=0.7, height=2.0, spherical_caps=False)
m0 = 1141.0 * 3.0788 * 0.93 # 93% fill, LOX density
tank = MassFlowRateBasedTank(
name="lox", geometry=geo, flux_time=(0, 60),
liquid=Fluid(name="LOX", density=1141.0), gas=Fluid(name="He", density=5.0),
initial_liquid_mass=m0, initial_gas_mass=0.4,
liquid_mass_flow_rate_in=0, gas_mass_flow_rate_in=0,
liquid_mass_flow_rate_out=lambda t: m0 / 120.0, gas_mass_flow_rate_out=0,
discretize=2000)
motor = LiquidMotor(
thrust_source=lambda t: 1e5, dry_mass=1000.0, dry_inertia=(1e5, 1e5, 2e4),
nozzle_radius=0.2, center_of_dry_mass_position=1.0, nozzle_position=0,
burn_time=(0, 60), coordinate_system_orientation="nozzle_to_combustion_chamber")
motor.add_tank(tank, position=1.5)
print(motor.propellant_I_33(0)) # -> 0.0
print(0.5 * m0 * 0.7**2) # closed form ½mr² -> 800.42 kg·m²
Actual: propellant_I_33(0) == 0.0. Expected: ≈ 800.4 kg·m² (½mr² for a cylinder; time-varying as liquid drains).
Impact
Any roll-channel analysis (roll control sizing, roll resonance, spin dynamics) on vehicles with
liquid motors silently underestimates roll inertia by the full propellant contribution. Dry +
propellant lateral inertias (I_11/I_22) are handled correctly, so the omission is specific to I_33.
Suggested fix (verified against closed form on 1.13.0)
Wire the existing geometry primitives into propellant_I_33 — for each positioned tank,
with F the antiderivative of area·r² and h(t) the liquid surface height in geometry coordinates:
@funcify_method("Time (s)", "Inertia I_33 (kg m²)")
def propellant_I_33(self):
i33 = 0
for positioned_tank in self.positioned_tanks:
tank = positioned_tank["tank"]
F = (tank.geometry.area * tank.geometry.radius**2).integral_function()
column = F.compose(tank.liquid_height) - F(tank.geometry.bottom)
i33 = i33 + tank.liquid_density * column / 2
return i33
Verified numerically: single-tank case returns 800.42 at t=0 and 400.21 after half drain
(closed-form ½mr² and ½(m/2)r² respectively, exact to displayed digits).
No parallel-axis term is needed — the axial component is invariant under axial translation.
Related observation
While prototyping the fix I noticed TankGeometry.Iz_volume(lower, upper) returns an
antiderivative Function rather than a scalar despite the explicit bounds (the integral_function
call inside appears to ignore the second argument), which may be why it went unused. Happy to
split that into a separate issue/PR if maintainers prefer.
Summary
LiquidMotor.propellant_I_33is hard-coded to return0, so liquid propellant contributes no roll-axis inertia to the 6-DOF dynamics. The physical quantity is far from negligible for launch-vehicle-scale tanks (e.g. a 3,267 kg LOX column in a r = 0.7 m cylindrical tank carries ½mr² ≈ 800 kg·m²; vehicle-scale tanks reach 10³–10⁴ kg·m²).The building block already exists:
TankGeometry.Iz_volume(tank_geometry.py:315-335) implements the volume-of-inertia about the tank axis but has no callers anywhere in the repo.Version
RocketPy 1.13.0 (pip), Python 3.14, Windows.
Minimal reproduction
Actual:
propellant_I_33(0) == 0.0. Expected: ≈ 800.4 kg·m² (½mr² for a cylinder; time-varying as liquid drains).Impact
Any roll-channel analysis (roll control sizing, roll resonance, spin dynamics) on vehicles with
liquid motors silently underestimates roll inertia by the full propellant contribution. Dry +
propellant lateral inertias (I_11/I_22) are handled correctly, so the omission is specific to I_33.
Suggested fix (verified against closed form on 1.13.0)
Wire the existing geometry primitives into
propellant_I_33— for each positioned tank,with
Fthe antiderivative ofarea·r²andh(t)the liquid surface height in geometry coordinates:Verified numerically: single-tank case returns 800.42 at t=0 and 400.21 after half drain
(closed-form ½mr² and ½(m/2)r² respectively, exact to displayed digits).
No parallel-axis term is needed — the axial component is invariant under axial translation.
Related observation
While prototyping the fix I noticed
TankGeometry.Iz_volume(lower, upper)returns anantiderivative
Functionrather than a scalar despite the explicit bounds (theintegral_functioncall inside appears to ignore the second argument), which may be why it went unused. Happy to
split that into a separate issue/PR if maintainers prefer.