Skip to content

Issue: LiquidMotor.propellant_I_33 always returns 0 (missing propellant roll inertia) #1191

Description

@zjk199506

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions