Add ClusterMotor for symmetric multi-engine clusters (port of upstream #924)#4
Merged
Conversation
added 2 commits
April 25, 2026 17:28
Port of upstream RocketPy PR RocketPy-Team#924 (RocketPy-Team#924). ClusterMotor wraps a single SolidMotor and replicates it N>=2 times symmetrically around a circle of radius R. Inherits from Motor so it plugs into Rocket.add_motor() unchanged. Inertia is aggregated using the parallel-axis theorem (Huygens-Steiner): Izz = N*Izz_single + N*m*R^2; Ixx = Iyy = N*I_trans + 0.5*N*m*R^2. Thrust, dry mass, and propellant mass scale linearly with N. Files: - rocketpy/motors/cluster_motor.py (new): ClusterMotor class. - rocketpy/motors/__init__.py: export ClusterMotor. - rocketpy/__init__.py: re-export at package root. - rocketpy/plots/rocket_plots.py: render N nozzles/grains around the circle for cluster motors. - tests/integration/motors/test_cluster_motor.py (new): PR test suite. Notes: - The PR's own test_cluster_mass_and_thrust_scaling and test_cluster_propellant_inertia_dynamic fail with "ValueError: truth value of an array..." on rocketpy.Function comparisons. This is a PR-side test bug (np.isclose against a Function object) and not specific to this port. Cluster construction, geometry, dry inertia, methods, and setters tests all pass (4/6). - When upstream PR RocketPy-Team#924 merges, swap this commit for the upstream version during the next fork sync.
The two failing assertions compared rocketpy.Function objects against scalars. np.isclose triggers Function.__array__, returning the source array, which makes np.isclose return an array and crashes assert with "truth value of an array with more than one element is ambiguous". Call propellant_mass(0), propellant_I_11(t), propellant_I_33(t) so np.isclose receives scalars. Matches the pattern already used at line 61 (cluster.thrust(1)). All 6 cluster tests pass after this fix.
raytracerfvf
pushed a commit
that referenced
this pull request
May 9, 2026
…e, unused-argument) Three issues introduced when ClusterMotor (PR #4) and the parachute trigger signature tests landed: 1. Cyclic import in `rocketpy/motors/cluster_motor.py`: importing `Function` and `Motor` from the package facade re-entered `rocketpy.__init__` while the module was still being initialized. Switch to canonical module paths (`rocketpy.mathutils.function`, `rocketpy.motors.motor`). 2. `not-callable` on `cluster.propellant_mass(0)` and `cluster.propellant_I_11(t)` in `tests/integration/motors/test_cluster_motor.py`: a later test in the same file assigned a raw `float` through the property setter (`cluster.propellant_mass = 50.0`), which made pylint infer the attribute as `float | Function` for every read in the file. Exercise the setters with values of the documented `Function` type instead. 3. `unused-argument` on parachute trigger lambdas in `tests/unit/rocket/test_parachute.py`: `Parachute.__evaluate_trigger_function` introspects only `len(sig.parameters)` (parachute.py:300), so parameter names are irrelevant. Rename unused trigger params to `_p`/`_h`/`_y`/ `_sensors` to match the project's `dummy-variables-rgx` convention. Repo-wide pylint score: 9.98 → 10.00.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Port of the open upstream RocketPy PR RocketPy-Team/RocketPy#924 onto our v1.12.0-synced fork. Adds a new `ClusterMotor` class that wraps a `SolidMotor` and replicates it `N >= 2` times symmetrically around a circle of radius `R`. Because it inherits `Motor`, it slots into `Rocket.add_motor()` unchanged.
Approach
Skipped from upstream PR
Known issues
The PR's own tests `test_cluster_mass_and_thrust_scaling` and `test_cluster_propellant_inertia_dynamic` fail with `ValueError: truth value of an array with more than one element is ambiguous` because they use `np.isclose` against `rocketpy.Function` objects directly instead of evaluating them. This is a bug in the upstream PR test code, not in our port — cluster construction, geometry, dry inertia, methods, and setters tests all pass (4/6). Reported here so we can re-verify when we re-sync after upstream PR RocketPy-Team#924 merges.
Test plan
Follow-up
When upstream RocketPy#924 merges, swap this commit for the upstream version during the next fork sync.