Skip to content

ENH: new dimensions route to get rocket geometry#71

Merged
GabrielBarberini merged 12 commits intomasterfrom
enh/rocket-drawing-geometry
May 1, 2026
Merged

ENH: new dimensions route to get rocket geometry#71
GabrielBarberini merged 12 commits intomasterfrom
enh/rocket-drawing-geometry

Conversation

@aasitvora99
Copy link
Copy Markdown
Member

@aasitvora99 aasitvora99 commented Apr 20, 2026

Implements https://github.com/RocketPy-Team/jarvis-ts/issues/51

Summary by CodeRabbit

  • New Features

    • Added /motors/{motor_id}/drawing-geometry and /rockets/{rocket_id}/drawing-geometry endpoints returning structured drawing geometry payloads.
    • Added new drawing geometry view types and full generation support for motor and rocket visuals.
    • Motor tank discretization now defaults to 100.
  • Bug Fixes

    • Validation prevents zero dry inertia for SOLID, LIQUID, or HYBRID motor types.
  • Chores

    • Switched RocketPy requirement from a Git branch to the PyPI package.
  • Tests

    • Updated test fixture to use non-zero dry inertia to satisfy validation.

aasitvora99 and others added 2 commits April 20, 2026 12:40
Exposes structured drawing geometry that mirrors rocketpy.Rocket.draw(),
so clients can redraw a rocket using the same shape math rocketpy uses
without server-side rendering or duplicated geometry logic in the UI.

Response carries per-surface shape_x/shape_y arrays, body tube segments,
motor patch polygons (nozzle, chamber, grains, tanks, outline), rail
button positions, sensors, t=0 CG/CP, and drawing bounds. Coordinates
are already transformed into the draw frame rocketpy uses.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 20, 2026

Warning

Rate limit exceeded

@aasitvora99 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 53 minutes and 37 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: feea5dde-7b75-46bf-b629-47a7d86fe4a7

📥 Commits

Reviewing files that changed from the base of the PR and between 3c4d00e and b1ad925.

📒 Files selected for processing (3)
  • src/services/motor.py
  • src/services/rocket.py
  • src/views/rocket.py
📝 Walkthrough
📝 Walkthrough
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title refers to 'rocket geometry' but the changeset includes significant work on motor geometry, tank defaults, rocket validation, and dependency management alongside rocket drawing geometry. Expand the title to reflect the full scope: 'ENH: drawing geometry endpoints for rockets and motors with validation improvements' or similar.
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch enh/rocket-drawing-geometry

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 53 minutes and 37 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

Align MotorModel and MotorTank with RocketPy's actual constructor
requirements so invalid motors are rejected at the API boundary with a
clear error rather than crashing deep inside RocketPy at simulate time.

- MotorModel.validate_dry_inertia_for_kind: SOLID / LIQUID / HYBRID
  motors in RocketPy require dry_inertia with no default. Only
  GenericMotor accepts (0, 0, 0). Reject the default tuple for every
  kind except GENERIC with a message the user can act on.
- MotorTank.discretize: change to Optional[int] = 100 to match the
  RocketPy Tank classes' default. Forms can now omit the field and still
  submit successfully.
- stub_motor_dump fixture: use dry_inertia=[0.1, 0.1, 0.1] so tests that
  override motor_kind to SOLID / LIQUID / HYBRID still pass the new
  validator without each having to add a dry_inertia override locally.
RocketPy's rocket.draw() does not draw a combustion chamber for
GenericMotor because _MotorPlots._generate_combustion_chamber reads
grain-only attributes (grain_initial_height, grain_outer_radius, etc.)
that GenericMotor lacks — it only emits a nozzle. Users who populate
chamber_radius / chamber_height / chamber_position then saw no chamber
in the jarvis playground.

Add a GenericMotor branch in RocketService._build_motor_geometry that
constructs an equivalent rectangular chamber patch from the chamber_*
fields. Vertex ordering mirrors _generate_combustion_chamber so the
patch flows through _generate_motor_region for outline assembly the
same way a SolidMotor chamber does.

Patch is emitted with role='chamber', flowing through the existing
drawingMotorSchema + GeometryRocket renderer without frontend changes.
@aasitvora99 aasitvora99 marked this pull request as ready for review April 22, 2026 17:55
@aasitvora99 aasitvora99 changed the title Enh/rocket drawing geometry ENH: new dimensions route to get rocket geometry Apr 22, 2026
Comment thread src/models/motor.py
# RocketPy's SolidMotor/LiquidMotor/HybridMotor require dry_inertia with no default.
# Only GenericMotor accepts (0, 0, 0). Surface a clear error at the API boundary
# instead of letting RocketPy crash deep in construction.
if self.motor_kind != MotorKinds.GENERIC and self.dry_inertia == (
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this validator exists (for future reviewers)

RocketPy itself enforces non-zero dry_inertia for every non-Generic motor kind. Verified against the installed rocketpy package:

Motor class dry_inertia default
SolidMotor required (no default)
LiquidMotor required (no default)
HybridMotor required (no default)
GenericMotor (0, 0, 0)

dry_inertia is the motor's dry-mass inertia tensor (I_11, I_22, I_33) in kg·m² about center_of_dry_mass_position. Rocketpy consumes it directly in Motor.__init__:

# rocketpy/motors/motor.py
inertia = (*dry_inertia, 0, 0, 0) if len(dry_inertia) == 3 else dry_inertia
self.dry_I_11 = inertia[0]
self.dry_I_22 = inertia[1]
self.dry_I_33 = inertia[2]

These feed the 6-DOF flight integrator's angular equations of motion. Passing (0, 0, 0) for a Solid/Liquid/Hybrid motor doesn't just fail fast in rocketpy — historically it could silently produce a motor with zero rotational inertia, corrupting every downstream simulation (pitch/yaw response, static margin, stability).

Our MotorModel.dry_inertia defaults to (0, 0, 0) for schema convenience. This validator converts what would otherwise be either (a) a deep rocketpy construction crash or (b) a silent simulation-correctness bug into a clear 422 at the API boundary, with a motor-kind-specific error message telling the client exactly what to provide.

GenericMotor is rocketpy's "black-box thrust curve" motor, explicitly designed for the case where the caller doesn't have real inertia data — so it's the one kind where (0, 0, 0) is legitimate, and the validator allows it.

phmbressan
phmbressan previously approved these changes Apr 23, 2026
Add a standalone GET /motors/{id}/drawing-geometry that returns
exactly the motor patches rocketpy's Rocket.draw() would produce,
rendered at the motor's own coordinate origin rather than embedded
inside a rocket. Lets the jarvis playground show 'rocket + motor'
and 'motor only' as distinct views without duplicating render logic
client-side.

Refactor:
- Extracted motor-drawing code out of RocketService._build_motor_geometry
  into MotorService.build_drawing_geometry(motor_position, parent_csys)
  and a public MotorService.get_drawing_geometry() wrapper. One code
  path now serves both endpoints; RocketService.build_motor_geometry
  is a two-line delegate.
- Moved shared helpers (_polygon_xy, _rebuild_polygon,
  _build_generic_chamber_patch) from src/services/rocket.py to
  src/services/motor.py where they logically belong.
- Moved shared drawing view types (NoseConeGeometry, TailGeometry,
  FinOutline, FinsGeometry, TubeGeometry, MotorPatch,
  MotorDrawingGeometry, RailButtonsGeometry, SensorGeometry,
  DrawingBounds) from src/views/rocket.py to a new
  src/views/drawing.py so both views/rocket.py and views/motor.py
  can depend on them without a circular import. views/rocket.py
  re-exports them for backwards compatibility with callers that
  still import from the old location.

Surface:
- New src/views/motor.py::MotorDrawingGeometryView: thin response
  envelope with motor (MotorDrawingGeometry), bounds (DrawingBounds),
  coordinate_system_orientation. ser_json_exclude_none matches the
  rocket response convention.
- New MotorController.get_motor_drawing_geometry(motor_id) method
  returning the view. 422 when the motor has no drawable patches
  (EmptyMotor edge); 404 from the existing get_motor_by_id path.
- New route GET /motors/{motor_id}/drawing-geometry.

Verified end-to-end against real rocketpy for every motor kind:
- Generic: 3 patches (chamber, nozzle, outline)
- Solid:   9 patches (chamber, 7x grain, nozzle, outline)
- Liquid:  3 patches (tank, nozzle, outline)
- Hybrid:  6 patches (chamber, grain, tank, nozzle, outline)
Rocket-embedded drawing unchanged (regression-tested: rocket.motor
remains at rocket.motor_position; bounds, patch roles, and patch
counts identical to pre-refactor).

Full suite: 156/156 passing.
@aasitvora99 aasitvora99 dismissed stale reviews from phmbressan and GabrielBarberini via 7037785 April 23, 2026 18:16
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (4)
src/services/rocket.py (1)

287-296: Broad except Exception — tighten if possible.

Ruff BLE001 flags these two blocks. The pragma comments acknowledge the intent, but catching AttributeError / TypeError / ValueError specifically would protect against swallowing bugs (e.g., a typo calling a nonexistent method returning a KeyboardInterrupt-like error) while still handling the "rocket not fully built" case. Low priority since the pragma already signals intent.

♻️ Suggested narrowing
-        try:
-            center_of_mass = float(rocket.center_of_mass(0))
-        except (
-            Exception
-        ):  # pragma: no cover - defensive; rocket may not be fully built
-            center_of_mass = None
-        try:
-            cp_position = float(rocket.cp_position(0))
-        except Exception:  # pragma: no cover
-            cp_position = None
+        try:
+            center_of_mass = float(rocket.center_of_mass(0))
+        except (AttributeError, TypeError, ValueError):  # pragma: no cover
+            center_of_mass = None
+        try:
+            cp_position = float(rocket.cp_position(0))
+        except (AttributeError, TypeError, ValueError):  # pragma: no cover
+            cp_position = None
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/services/rocket.py` around lines 287 - 296, The two broad except blocks
around rocket.center_of_mass(0) and rocket.cp_position(0) should be narrowed to
specific exceptions so we don't swallow unexpected errors; catch only
AttributeError, TypeError, and ValueError (or the subset that matches the
possible failure modes) for the calls to center_of_mass and cp_position in place
of "except Exception" and keep the existing fallback to set center_of_mass or
cp_position to None when those specific exceptions occur.
src/services/motor.py (2)

282-282: Line exceeds 79-character limit.

Likely to be rewrapped by Black/Ruff on next make format pass. Pre-wrap to avoid stylistic churn:

♻️ Proposed wrap
-        fallback_radius = float(getattr(self._motor, "nozzle_radius", 0.0) or 0.0)
+        fallback_radius = float(
+            getattr(self._motor, "nozzle_radius", 0.0) or 0.0
+        )

As per coding guidelines: "Target Python 3.12 and enforce 79-character line length using Black, Ruff, and Pylint".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/services/motor.py` at line 282, The line assigning fallback_radius is
over 79 characters; break it into a shorter, PEP8-compliant expression using the
same symbols (fallback_radius, self._motor, "nozzle_radius", getattr) so it
stays within the 79-char limit—for example, compute the attribute value into a
short temporary variable or use a parenthesized expression split across two
lines, then cast to float as before to preserve behavior.

327-396: Heavy coupling to RocketPy private plot APIs — worth documenting.

motor.plots._generate_combustion_chamber, _generate_grains, _generate_positioned_tanks, _generate_nozzle, and _generate_motor_region are all private (_-prefixed) RocketPy internals. Any rocketpy minor-version bump can silently break this endpoint. Since you've already pinned rocketpy and wrapped _generate_motor_region in a defensive try/except, the immediate risk is contained, but consider:

  • Adding a RocketPy version assertion at import-time (or module load) so incompatibilities surface loudly rather than as 500s at request time.
  • Filing an upstream issue asking for a public "geometry export" hook so this service can eventually shed the _plots dependency.

Not blocking — just flagging for future maintenance.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/services/motor.py` around lines 327 - 396, This code relies on RocketPy
private plot functions (_generate_combustion_chamber, _generate_grains,
_generate_positioned_tanks, _generate_nozzle, _generate_motor_region) which can
break across RocketPy minor versions; add an import-time version assertion in
this module that checks rocketpy.__version__ (or other canonical version symbol)
against the pinned version constant and raises a clear ImportError/RuntimeError
if mismatched so incompatibility surfaces at load-time rather than failing at
request time, and add a short TODO comment referencing filing an upstream
request for a public geometry-export hook.
src/views/rocket.py (1)

21-37: Optional nit: sort __all__ to silence Ruff RUF022.

Low priority, but since make lint is part of the project's required workflow, sorting the list keeps the diff noise-free on future autofix runs.

♻️ Proposed sort
 __all__ = [
-    "RocketSimulation",
-    "RocketView",
-    "RocketCreated",
-    "RocketRetrieved",
-    "RocketDrawingGeometry",
-    "NoseConeGeometry",
-    "TailGeometry",
-    "FinOutline",
-    "FinsGeometry",
-    "TubeGeometry",
-    "MotorPatch",
-    "MotorDrawingGeometry",
-    "RailButtonsGeometry",
-    "SensorGeometry",
-    "DrawingBounds",
+    "DrawingBounds",
+    "FinOutline",
+    "FinsGeometry",
+    "MotorDrawingGeometry",
+    "MotorPatch",
+    "NoseConeGeometry",
+    "RailButtonsGeometry",
+    "RocketCreated",
+    "RocketDrawingGeometry",
+    "RocketRetrieved",
+    "RocketSimulation",
+    "RocketView",
+    "SensorGeometry",
+    "TailGeometry",
+    "TubeGeometry",
 ]

As per coding guidelines: "Execute make lint with Flake8 and Pylint against repository presets".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/views/rocket.py` around lines 21 - 37, The __all__ list in this module is
unsorted which triggers Ruff RUF022; update the module-level __all__ definition
(the list containing "RocketSimulation", "RocketView", "RocketCreated",
"RocketRetrieved", "RocketDrawingGeometry", "NoseConeGeometry", "TailGeometry",
"FinOutline", "FinsGeometry", "TubeGeometry", "MotorPatch",
"MotorDrawingGeometry", "RailButtonsGeometry", "SensorGeometry",
"DrawingBounds") to be alphabetically sorted (e.g., arrange the string entries
in ascending lexicographic order) so linting passes and future autofixes don't
churn the file.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@requirements.txt`:
- Line 12: Replace the unpinned "rocketpy" entry in requirements.txt with an
exact, validated version to prevent accidental upgrades; pin to the specific
release you tested (for example 0.x.y) so future PyPI changes that alter private
APIs used in src/services/motor.py and src/services/rocket.py (e.g.,
motor.plots._generate_combustion_chamber, _generate_grains,
_generate_positioned_tanks, _generate_nozzle, _generate_motor_region,
rocket._csys, surface.shape_vec, rocket.aerodynamic_surfaces.sort_by_position,
motor._csys) won’t break the drawing-geometry/ simulation flows, and update the
project’s upgrade notes to bump this pin deliberately when you validate a new
release.

In `@src/services/motor.py`:
- Around line 302-315: The long expression setting fallback_radius should be
split to satisfy the 79-character limit: extract the getattr call into a short
temporary variable (e.g., nozzle_radius_raw = getattr(self._motor,
"nozzle_radius", 0.0) or 0.0) then compute fallback_radius =
float(nozzle_radius_raw); update this in the get_drawing_geometry logic that
references self._motor and fallback_radius (preserving the EmptyMotor
early-return behavior and use of MotorDrawingGeometry).

In `@src/services/rocket.py`:
- Around line 23-36: The import list in src/services/rocket.py unnecessarily
includes MotorPatch which is not used here (motor patches are created in
MotorService.build_drawing_geometry); remove MotorPatch from the from
src.views.rocket import (...) statement so the module only imports the actually
used symbols (e.g., RocketSimulation, RocketDrawingGeometry, NoseConeGeometry,
TailGeometry, FinsGeometry, FinOutline, TubeGeometry, MotorDrawingGeometry,
RailButtonsGeometry, SensorGeometry, DrawingBounds) to satisfy Pylint.
- Around line 397-416: The _build_sensors function currently accesses sensor
positions by indexing (pos[0], pos[1], pos[2]) which is inconsistent with the
attribute-based access used elsewhere (e.g., _build_rail_buttons); update
_build_sensors to read position components via pos.x, pos.y, pos.z and ensure
SensorGeometry construction uses those floats (keep existing normal handling and
name retrieval intact) so the function (_build_sensors) matches the attribute
access pattern used across the codebase.

---

Nitpick comments:
In `@src/services/motor.py`:
- Line 282: The line assigning fallback_radius is over 79 characters; break it
into a shorter, PEP8-compliant expression using the same symbols
(fallback_radius, self._motor, "nozzle_radius", getattr) so it stays within the
79-char limit—for example, compute the attribute value into a short temporary
variable or use a parenthesized expression split across two lines, then cast to
float as before to preserve behavior.
- Around line 327-396: This code relies on RocketPy private plot functions
(_generate_combustion_chamber, _generate_grains, _generate_positioned_tanks,
_generate_nozzle, _generate_motor_region) which can break across RocketPy minor
versions; add an import-time version assertion in this module that checks
rocketpy.__version__ (or other canonical version symbol) against the pinned
version constant and raises a clear ImportError/RuntimeError if mismatched so
incompatibility surfaces at load-time rather than failing at request time, and
add a short TODO comment referencing filing an upstream request for a public
geometry-export hook.

In `@src/services/rocket.py`:
- Around line 287-296: The two broad except blocks around
rocket.center_of_mass(0) and rocket.cp_position(0) should be narrowed to
specific exceptions so we don't swallow unexpected errors; catch only
AttributeError, TypeError, and ValueError (or the subset that matches the
possible failure modes) for the calls to center_of_mass and cp_position in place
of "except Exception" and keep the existing fallback to set center_of_mass or
cp_position to None when those specific exceptions occur.

In `@src/views/rocket.py`:
- Around line 21-37: The __all__ list in this module is unsorted which triggers
Ruff RUF022; update the module-level __all__ definition (the list containing
"RocketSimulation", "RocketView", "RocketCreated", "RocketRetrieved",
"RocketDrawingGeometry", "NoseConeGeometry", "TailGeometry", "FinOutline",
"FinsGeometry", "TubeGeometry", "MotorPatch", "MotorDrawingGeometry",
"RailButtonsGeometry", "SensorGeometry", "DrawingBounds") to be alphabetically
sorted (e.g., arrange the string entries in ascending lexicographic order) so
linting passes and future autofixes don't churn the file.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a70859d5-5343-4be8-82a4-338ad11401bc

📥 Commits

Reviewing files that changed from the base of the PR and between eff3b9d and 7037785.

📒 Files selected for processing (13)
  • requirements.txt
  • src/controllers/motor.py
  • src/controllers/rocket.py
  • src/models/motor.py
  • src/models/sub/tanks.py
  • src/routes/motor.py
  • src/routes/rocket.py
  • src/services/motor.py
  • src/services/rocket.py
  • src/views/drawing.py
  • src/views/motor.py
  • src/views/rocket.py
  • tests/unit/test_routes/conftest.py

Comment thread requirements.txt
gunicorn
uvicorn
git+https://github.com/RocketPy-Team/RocketPy.git@develop
rocketpy
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

latest rocketpy pypi version release notes

💡 Result:

Latest RocketPy PyPI version (as of 2026-04-23): 1.12.0, released 2026-03-20. Release notes (RocketPy v1.12.0): - Added: acceptance tests for 3DOF flight simulation based on Bella Lui rocket; background map auto-download functionality to Monte Carlo plots; net thrust addition to 3DOF in flight class; 3DOF lateral motion improvement; multi-dimensional drag coefficient support (Cd as function of M, Re, α); save functionality to _MonteCarloPlots.all; animations for motor propellant mass and tank fluid volumes; rail button bending moments calculation in Flight class; bootstrapping for confidence interval estimation; built-in FlightComparator for validating simulations against external data; persistent caching for ThrustCurve API; axial_acceleration attribute to Flight class; custom warning when no motor or aerosurface; thrustcurve API integration to retrieve motor engine data; compatibility with MERRA-2 atmosphere reanalysis files; enable only radial burning. - Changed: improve multi-variable drag compatibility and regular-grid handling (with related tests/docs); replace if/elif chains with match statement; refactor Flight class to improve time node handling and sensor/controllers. - Fixed: restore Rocket.power_off_drag and Rocket.power_on_drag as Function objects while preserving raw inputs; add explicit timeouts to ThrustCurve API requests; fix hard-coded radius value for parachute added mass calculation; fix documentation build; energy_data plot not working for 3DOF sims; CSV column header spacing in FlightDataExporter; incorrect iteration count in parallel Monte Carlo simulation; missing titles in roll parameter plots for fin sets; duplicate _controllers in Flight.TimeNodes.merge; incorrect Jacobian in only_radial_burn branch of SolidMotor.evaluate_geometry (also plus repeated timeout fix mention). How to check on your machine: - pip install --upgrade rocketpy - python -c "import pkgutil,rocketpy; import importlib.metadata as m; print(m.version('rocketpy'))"

Citations:


🏁 Script executed:

# First, check the current state of requirements.txt
cat requirements.txt

Repository: RocketPy-Team/Infinity-API

Length of output: 332


🏁 Script executed:

# Search for the claimed private API usages in service files
rg "_generate_combustion_chamber|_generate_grains|_generate_positioned_tanks|_generate_nozzle|_generate_motor_region|_csys|shape_vec|sort_by_position" src/services/

Repository: RocketPy-Team/Infinity-API

Length of output: 3063


🏁 Script executed:

# Check if there are any version pins or comments in requirements.txt or setup files
fd -e txt -e in | xargs grep -l "rocketpy" 2>/dev/null

Repository: RocketPy-Team/Infinity-API

Length of output: 86


🏁 Script executed:

# Check git history of requirements.txt to see if there was a previous pin
git log -p requirements.txt | head -100

Repository: RocketPy-Team/Infinity-API

Length of output: 52


🏁 Script executed:

# Check for version info in CI/deployment configs
fd -e yml -e yaml -e toml | xargs grep -l "rocketpy" 2>/dev/null

Repository: RocketPy-Team/Infinity-API

Length of output: 84


🏁 Script executed:

# Look for any comments or docs mentioning rocketpy version
rg "rocketpy.*version|version.*rocketpy" -i

Repository: RocketPy-Team/Infinity-API

Length of output: 52


🏁 Script executed:

# Check pyproject.toml for rocketpy version specification
cat pyproject.toml

Repository: RocketPy-Team/Infinity-API

Length of output: 1773


🏁 Script executed:

# Check for CI workflows that might reveal tested versions
fd -path ".github/workflows/*" -o -path ".gitlab-ci.yml" | xargs cat 2>/dev/null

Repository: RocketPy-Team/Infinity-API

Length of output: 238


🏁 Script executed:

# Check git log with short format to see recent changes
git log --oneline | head -20

Repository: RocketPy-Team/Infinity-API

Length of output: 127


🏁 Script executed:

# Check if there are tests that might indicate validated versions
find tests -name "*.py" -type f | head -5 | xargs grep -l "rocketpy\|_generate\|_csys" 2>/dev/null

Repository: RocketPy-Team/Infinity-API

Length of output: 176


🏁 Script executed:

# Look for any recent commits or PRs mentioning rocketpy version
git log --all --grep="rocketpy\|version\|pin" --oneline 2>/dev/null | head -20

Repository: RocketPy-Team/Infinity-API

Length of output: 1040


🏁 Script executed:

# Check if there's a lock file (poetry.lock, requirements.lock, etc.)
fd -e "lock" -o -e "frozen" | head -20

Repository: RocketPy-Team/Infinity-API

Length of output: 186


🏁 Script executed:

# Search for any validation or testing comments near the API usage
rg "validate|tested|version" src/services/motor.py src/services/rocket.py -B2 -A2

Repository: RocketPy-Team/Infinity-API

Length of output: 52


Pin rocketpy to a known-good version.

The service layer relies heavily on RocketPy private APIs (motor.plots._generate_combustion_chamber, _generate_grains, _generate_positioned_tanks, _generate_nozzle, _generate_motor_region, plus rocket._csys, surface.shape_vec, rocket.aerodynamic_surfaces.sort_by_position, motor._csys, etc. — see src/services/motor.py and src/services/rocket.py). Switching from a git-pinned develop to an unpinned rocketpy means any future PyPI release that refactors these internals will silently break /drawing-geometry (and potentially existing simulation flows) on the next environment rebuild.

Pin to the exact version you validated against, and bump deliberately.

🔧 Proposed fix
-rocketpy
+rocketpy==<validated version>  # pinned: service depends on private _MotorPlots/_RocketPlots helpers
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@requirements.txt` at line 12, Replace the unpinned "rocketpy" entry in
requirements.txt with an exact, validated version to prevent accidental
upgrades; pin to the specific release you tested (for example 0.x.y) so future
PyPI changes that alter private APIs used in src/services/motor.py and
src/services/rocket.py (e.g., motor.plots._generate_combustion_chamber,
_generate_grains, _generate_positioned_tanks, _generate_nozzle,
_generate_motor_region, rocket._csys, surface.shape_vec,
rocket.aerodynamic_surfaces.sort_by_position, motor._csys) won’t break the
drawing-geometry/ simulation flows, and update the project’s upgrade notes to
bump this pin deliberately when you validate a new release.

Comment thread src/services/motor.py
Comment thread src/services/rocket.py
Comment thread src/services/rocket.py
Comment on lines +397 to +416
def _build_sensors(self) -> list[SensorGeometry]:
rocket = self._rocket
sensors: list[SensorGeometry] = []
for sensor_pos in getattr(rocket, "sensors", []) or []:
sensor = sensor_pos[0]
pos = sensor_pos[1]
normal = getattr(sensor, "normal_vector", None)
normal_tuple = (
(float(normal.x), float(normal.y), float(normal.z))
if normal is not None
else (0.0, 0.0, 0.0)
)
sensors.append(
SensorGeometry(
name=getattr(sensor, "name", None),
position=(float(pos[0]), float(pos[1]), float(pos[2])),
normal=normal_tuple,
)
)
return sensors
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how rocketpy stores sensor positions vs. rail buttons / aero surfaces.
rg -nP --type=py -C3 '\bsensors\s*=' 
rg -nP --type=py -C3 '\bclass\s+Components\b'
rg -nP --type=py -C3 'add_sensor|sensors\.add'

Repository: RocketPy-Team/Infinity-API

Length of output: 960


🏁 Script executed:

cat -n src/services/rocket.py | head -450 | tail -200

Repository: RocketPy-Team/Infinity-API

Length of output: 8521


🏁 Script executed:

rg -n "def _build_rail_buttons" -A 20 src/services/rocket.py

Repository: RocketPy-Team/Infinity-API

Length of output: 914


🏁 Script executed:

rg -n "def _build.*surface\|for.*surface\|aerodynamic" -A 10 src/services/rocket.py

Repository: RocketPy-Team/Infinity-API

Length of output: 52


🏁 Script executed:

rg -n "position\." src/services/rocket.py | head -20

Repository: RocketPy-Team/Infinity-API

Length of output: 109


🏁 Script executed:

rg -n "from.*Vector|import.*Vector" src/services/rocket.py

Repository: RocketPy-Team/Infinity-API

Length of output: 52


🏁 Script executed:

rg -n "rocket\.sensors\|rocket\.rail_buttons" -B 2 -A 2 src/

Repository: RocketPy-Team/Infinity-API

Length of output: 52


🏁 Script executed:

grep -n "def _build_rail_buttons\|def _build_sensors\|rocket.sensors\|rail_buttons" src/services/rocket.py | head -30

Repository: RocketPy-Team/Infinity-API

Length of output: 688


🏁 Script executed:

head -100 src/services/rocket.py

Repository: RocketPy-Team/Infinity-API

Length of output: 3098


🏁 Script executed:

rg -n "Vector|normal_vector" src/services/rocket.py | head -20

Repository: RocketPy-Team/Infinity-API

Length of output: 133


🏁 Script executed:

fd -type f -name "*test*" -o -name "*spec*" | grep -E "\.(py|md)$"

Repository: RocketPy-Team/Infinity-API

Length of output: 240


🏁 Script executed:

rg -n "sensors\|rail_buttons" tests/ -B 2 -A 2 2>/dev/null | head -40

Repository: RocketPy-Team/Infinity-API

Length of output: 52


🏁 Script executed:

web_search
rocketpy library sensor position vector documentation

Repository: RocketPy-Team/Infinity-API

Length of output: 167


🏁 Script executed:

find . -type f -name "*test*.py" 2>/dev/null | head -10

Repository: RocketPy-Team/Infinity-API

Length of output: 458


🏁 Script executed:

rg -n "sensors" tests/ -B 2 -A 2 2>/dev/null || echo "No tests directory or no matches found"

Repository: RocketPy-Team/Infinity-API

Length of output: 108


🏁 Script executed:

cat -n src/services/rocket.py | grep -A 5 -B 5 "def _build_sensors\|def _build_rail_buttons"

Repository: RocketPy-Team/Infinity-API

Length of output: 941


🌐 Web query:

rocketpy sensor position vector indexing attribute access

💡 Result:

In RocketPy, sensors are added to a Rocket instance using rocket.add_sensor(sensor, position), where position is a float (assumed along z-axis), tuple/list (x,y,z), or Vector, relative to the rocket's coordinate system origin (typically center of mass without motor). The rocket.sensors is a Components instance storing sensors and their positions as namedtuples (component, position). Access sensors via rocket.sensors.get_components for the list of sensor objects, or rocket.sensors.get_positions for the list of position Vectors. Iterate over rocket.sensors._components for paired access: for c in rocket.sensors.components: sensor = c.component; pos = c.position. The position vector (relative_position) is passed to sensor.measure during simulation, used to compute sensor-specific acceleration/position from rocket CoM. For example, in Accelerometer/Gyroscope/GnssReceiver source code, relative_position is a np.array/Vector from rocket CoM to sensor. No direct public attribute like sensor.position exists on individual sensors; position is managed by the rocket's Components. Example usage from docs: sensors = rocket.sensors.get_components; first_sensor = sensors[0]; first_pos = rocket.sensors.get_positions[0]. Indexing via lists from get methods. Sensors under experimental development; API may change.

Citations:


🏁 Script executed:

rg -n "\.x\|\.y\|\.z" src/services/rocket.py | head -20

Repository: RocketPy-Team/Infinity-API

Length of output: 52


🏁 Script executed:

rg -n "\[0\]\|\[1\]\|\[2\]" src/services/rocket.py | grep "pos\|position\|normal" | head -20

Repository: RocketPy-Team/Infinity-API

Length of output: 52


🏁 Script executed:

rg -n "\.z|\.y|\.x" src/services/rocket.py

Repository: RocketPy-Team/Infinity-API

Length of output: 599


🏁 Script executed:

grep -n "pos\[0\]\|pos\[1\]\|pos\[2\]" src/services/rocket.py

Repository: RocketPy-Team/Infinity-API

Length of output: 224


🏁 Script executed:

sed -n '380,416p' src/services/rocket.py

Repository: RocketPy-Team/Infinity-API

Length of output: 1431


Use consistent position access pattern with attribute access instead of indexing.

In _build_rail_buttons (line 386) and elsewhere, position Vectors are accessed via attributes (pos.z, normal.x), but _build_sensors (line 412) uses indexing (pos[0], pos[1], pos[2]). Both syntaxes work with rocketpy's Vector, but align sensor position access to use .x, .y, .z for consistency throughout the codebase.

Suggested fix
    position=(float(pos.x), float(pos.y), float(pos.z)),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/services/rocket.py` around lines 397 - 416, The _build_sensors function
currently accesses sensor positions by indexing (pos[0], pos[1], pos[2]) which
is inconsistent with the attribute-based access used elsewhere (e.g.,
_build_rail_buttons); update _build_sensors to read position components via
pos.x, pos.y, pos.z and ensure SensorGeometry construction uses those floats
(keep existing normal handling and name retrieval intact) so the function
(_build_sensors) matches the attribute access pattern used across the codebase.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/services/rocket.py (1)

286-295: ⚡ Quick win

Don’t silently swallow all COM/CP computation failures.

Line 289 and Line 294 catch Exception and drop the error context. That makes debugging data/library regressions harder. Prefer narrowing to expected exception types (based on RocketPy behavior) and/or logging unexpected failures before falling back to None.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/services/rocket.py` around lines 286 - 295, The code currently swallows
all exceptions when calling rocket.center_of_mass(0) and rocket.cp_position(0);
change the broad excepts to handle expected error types (e.g., ValueError,
TypeError) and add a fallback except Exception as e that logs the unexpected
error (using the module/logger already in scope or create one) before setting
center_of_mass or cp_position to None; specifically update the try/except around
calls to rocket.center_of_mass and rocket.cp_position to first catch concrete
exceptions, then use logger.exception(...) in the generic except to preserve
error context while still falling back to None.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/services/rocket.py`:
- Around line 286-295: The code currently swallows all exceptions when calling
rocket.center_of_mass(0) and rocket.cp_position(0); change the broad excepts to
handle expected error types (e.g., ValueError, TypeError) and add a fallback
except Exception as e that logs the unexpected error (using the module/logger
already in scope or create one) before setting center_of_mass or cp_position to
None; specifically update the try/except around calls to rocket.center_of_mass
and rocket.cp_position to first catch concrete exceptions, then use
logger.exception(...) in the generic except to preserve error context while
still falling back to None.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: eac2da30-f15c-41b4-b52c-aa6fc94d68b5

📥 Commits

Reviewing files that changed from the base of the PR and between 7037785 and 3c4d00e.

📒 Files selected for processing (1)
  • src/services/rocket.py

@GabrielBarberini GabrielBarberini merged commit 4b906d4 into master May 1, 2026
4 checks passed
@GabrielBarberini GabrielBarberini deleted the enh/rocket-drawing-geometry branch May 1, 2026 17:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants