Skip to content

ENH: Add tube-fin aerodynamic surface - #1144

Merged
Gui-FernandesBR merged 1 commit into
RocketPy-Team:developfrom
ting-hong-shieh:codex/enh-tube-fins-816
Aug 13, 2026
Merged

ENH: Add tube-fin aerodynamic surface#1144
Gui-FernandesBR merged 1 commit into
RocketPy-Team:developfrom
ting-hong-shieh:codex/enh-tube-fins-816

Conversation

@ting-hong-shieh

Copy link
Copy Markdown

Closes #816.

Pull request type

  • Code changes (bugfix, features)
  • Code maintenance (refactoring, formatting, tests)
  • ReadMe, Docs and GitHub updates

Checklist

  • Tests for the changes have been added
  • Docs have been reviewed and updated
  • Lint has passed locally with Ruff and Pylint
  • All tests have passed locally; four existing sensitivity tests require the optional statsmodels dependency, which is not installed in the local test environment
  • CHANGELOG.md — no action needed; an LLM workflow auto-updates it after merge

Current behavior

RocketPy has no built-in aerodynamic model for tube fins. Users must supply custom coefficients through GenericSurface, which requires external aerodynamic data and does not expose tube-fin geometry directly.

New behavior

This PR adds a TubeFins aerodynamic surface and Rocket.add_tube_fins. The first implementation follows the Ribner ring-airfoil normal-force slope used by OpenRocket and includes:

  • a normal-force coefficient derived from tube count, length, inner radius, outer radius, and rocket radius;
  • a 20-degree angle-of-attack cap;
  • a fixed quarter-chord center of pressure for Mach 0.5 and below;
  • validation for at least three evenly distributed, uncanted tubes that touch the rocket body and both neighboring tubes;
  • JSON serialization, top-level exports, rocket integration, prints, plots, tests, and user and technical documentation.

The model intentionally does not calculate Mach-dependent center of pressure above Mach 0.5, tube friction or pressure drag, cant and roll behavior, side force and yaw, or separated and overlapping tube layouts. Tube-fin drag must remain included in the rocket's power-on and power-off drag curves.

Validation

  • 18 passed in the focused TubeFins tests.
  • 200 passed, 7 skipped across aerodynamic-surface, Rocket, and encoding tests.
  • 2170 passed, 59 skipped in the complete test suite; four sensitivity tests failed because the optional statsmodels dependency is not installed.
  • Ruff formatting and lint checks passed.
  • Pylint rated the new implementation and tests 10.00/10.
  • The new technical page and API reference passed targeted Sphinx builds with warnings treated as errors.

Breaking change

  • No

@codecov

codecov Bot commented Aug 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.90643% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.28%. Comparing base (e0ff281) to head (b454107).
⚠️ Report is 50 commits behind head on develop.

Files with missing lines Patch % Lines
rocketpy/rocket/aero_surface/tube_fins.py 94.65% 7 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #1144      +/-   ##
===========================================
+ Coverage    82.18%   83.28%   +1.10%     
===========================================
  Files          122      130       +8     
  Lines        16355    17072     +717     
===========================================
+ Hits         13441    14219     +778     
+ Misses        2914     2853      -61     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@ting-hong-shieh
ting-hong-shieh marked this pull request as ready for review August 13, 2026 20:21
@ting-hong-shieh
ting-hong-shieh requested a review from a team as a code owner August 13, 2026 20:21
@Gui-FernandesBR Gui-FernandesBR linked an issue Aug 13, 2026 that may be closed by this pull request

@Gui-FernandesBR Gui-FernandesBR left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The implementation is clean and solid.

Only thing which concerns me is related to the validation. Could you run some tests comparing your implementation against openrocket, just to make sure we are calculating similar results?

@ting-hong-shieh

Copy link
Copy Markdown
Author

Thank you for the review, @Gui-FernandesBR. I ran a source-anchored numerical cross-check against OpenRocket's tube-fin implementation.

Revisions tested

Method

I evaluated the Ribner normal-force equation from OpenRocket's TubeFinSetCalc independently and compared it with the runtime output from RocketPy's TubeFins.

The comparison covered:

  • the set-level normal-force derivative, CNa;
  • normal force at 10° angle of attack;
  • the 20° stall cap, checked at 30° angle of attack;
  • the center of pressure for Mach ≤ 0.5.

OpenRocket computes the coefficient from the single-tube expression, with the aerodynamic aggregation accounting for the number of tubes. RocketPy computes the equivalent set-level value directly by multiplying by the tube count.

All cases used mutually tangent tubes. The reference area was π × rocket_radius², matching both implementations.

Results

Tubes Length (m) Inner radius (m) Outer radius (m) Body radius (m) OpenRocket CNa RocketPy CNa Absolute error CP/chord
3 0.08 0.022 0.258564065 0.04 5.377216656 5.377216656 0.0 0.25
4 0.12 0.030 0.120710678 0.05 8.738461359 8.738461359 0.0 0.25
6 0.10 0.045 0.050000000 0.05 24.717764278 24.717764278 0.0 0.25
8 0.15 0.035 0.037194864 0.06 16.789729899 16.789729899 0.0 0.25

The 10° normal-force values also matched. At 30°, RocketPy produced the same values obtained by applying OpenRocket's 20° stall cap.

The assertions used an absolute tolerance of 1e-12 for aerodynamic coefficients and 1e-15 for CP/chord.

Environment

  • RocketPy package version: 1.13.0
  • Python: 3.12.6
  • NumPy: 2.5.2
  • Platform: macOS 26.5.2, ARM64

Scope and limitation

This was a source-level numerical comparison against a fixed OpenRocket commit, not a black-box execution of the OpenRocket application. OpenRocket itself was not executed because the validation environment did not have a Java runtime.

The comparison is limited to the scope implemented in this PR: subsonic normal force and the quarter-chord CP for Mach ≤ 0.5. OpenRocket's transonic and supersonic CP model, roll behavior, friction drag, and pressure drag are intentionally outside this initial implementation.

Within that scope, RocketPy and the referenced OpenRocket implementation produced matching results for every tested case.

@Gui-FernandesBR
Gui-FernandesBR merged commit e00a07e into RocketPy-Team:develop Aug 13, 2026
19 checks passed
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.

ENH: Tube Fins

2 participants