Skip to content

feat: scipy 5-DOF optimizer, depowering, and input validation - #58

Merged
marinlauber merged 24 commits into
masterfrom
pr/core-physics
Oct 17, 2026
Merged

marinlauber merged 24 commits into
masterfrom
pr/core-physics

Conversation

@TAJD

@TAJD TAJD commented Jun 17, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Replace NLopt with scipy optimizer — resolves Python 3.13 incompatibility
  • Iterative depowering with configurable heel limit (phi_max); fixes solver progression past first solution
  • Leeway fix — remove clamp, clarify radians conversion, drop dead _cl
  • Input validation and error handling across the full VPP stack
  • Comprehensive new test suite: hydro, aero, resistance, API, utils

Part of

Second of four PRs from #55. Base is pr/uv-ci (stacked); merge after #57.

Test plan

  • uv run pytest tests/test_vpp.py tests/test_hydro.py tests/test_aero.py tests/test_resistance.py tests/test_api.py tests/test_utils.py -v
  • Polars output visually unchanged vs main

- Replace NLopt with scipy optimizer for Python 3.13 compatibility
- Add iterative depowering with heel limit enforcement (phi_max)
- Fix leeway clamp and radians conversion in force balance
- Fix depowering solver progression past first phi_max solution
- Add input validation and error handling across VPP stack
- Add comprehensive test suite (hydro, aero, resistance, API, utils)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
TAJD and others added 3 commits June 17, 2026 20:42
- Add ORC spinnaker variants and low-performance sail coefficient data
- Add ShortKeel appendage class for hull-integrated keels (Daring class)
- Add Daring 5.5m yacht definition with estimated parameters
- Add Daring baseline performance results and righting moment data
- Add yacht and Daring-specific test suites

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Base automatically changed from pr/uv-ci to master October 13, 2026 11:26
marinlauber and others added 20 commits October 13, 2026 13:33
Resolve conflicts with #51: keep this branch's scipy interpolation and
solver rewrite, restore master's VPPResults loader for the dict format
written by VPP.write.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in master (uv/CI, #51) via #58. Conflicts in SailMod, YachtMod
and api.py resolved in favour of this branch, which supersedes master's
Akima interpolation and whitespace changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ShortKeel stays as a generic low-aspect-ratio appendage; its unit tests
move to test_yacht.py.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
feat: ORC sail variants, ShortKeel appendage, and Daring yacht
The ITTC 1978 roughness allowance is calibrated on ships; at 150 um it
added ~80% to the friction of a 12 m yacht and slowed light-air speeds
by 1-1.5 kn versus master and WinVPP. The ORC VPP has no roughness term,
so default to 0 (Yacht and API) and keep it as an opt-in.

Also use the passed length in the allowance (appendages used the hull
length) and label the friction line as Hughes, which is what it is.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
_get_cross padded each sail's fastest-TWA range by two indices, which
could reach TWAs where the sail is skipped (e.g. kites at TWA <= lim_up)
and still stores zeros, so the curve dropped to the origin (MN1 + A5 at
18 kn in runVPP). Only pad over points with a non-zero speed, and make
the upper padding match the lower one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The A5 had vce=2.75 m (the A2 is 9.55 m; ORC gives ~0.565*ISP), so it
produced under a third of the heeling moment and won down to 64 deg TWA
in a breeze. Use area=110 m^2, vce=9.0 m, which restricts it to the
~100-125 deg heavy-air reaching band. Regenerate the example outputs
and the README figure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rudder._Ksff ramped from 1 to 2 up to 30 deg heel and then dropped back
to 1, halving the rudder lift slope past 30 deg. The jump made the
equilibrium discontinuous and trapped the optimiser at exactly 30 deg.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replace the flatten-then-reef heuristic with a single point solver that
maximises boat speed over [vb, phi, leeway, flat, red] subject to scaled,
unsquared force/moment equilibrium and phi <= phi_max. The old solver
took the first setting under the heel limit rather than the fastest one,
so boat speed could drop as TWS rose (up to 0.6 kn at 14->16 kn), and it
stored non-equilibrium points.

- warm start along TWA, keep the fastest converged guess
- check convergence; sail sets that cannot be carried are left at 0
- sequential and parallel share the same row solver (identical results)
- 'iterative' and '5dof' remain as aliases of 'sequential'
- drop tqdm, no longer used
- tests: speed monotonic in TWS, stored points in equilibrium,
  infeasible kite left unsolved

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JN4y2NWa8kFMBZ5FV92jkj
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JN4y2NWa8kFMBZ5FV92jkj
Bugs fixed against the documentation:
- heeling moment uses the full heeling force (no extra cos(heel)) and the
  centre of effort height above the waterline, HBI + ZCE (5.56); the main
  and kite CE were measured from the deck, missing the freeboard
- effective span coefficient uses 0.075 * overlap (5.42), not 0.0075
- hull windage blends head-on to side area with the AWA in the right
  units, and is no longer scaled by cos(AWA) twice
- dynamic righting moment follows eq. 4.32 (incl. the -2.1 term and SLR
  in knots/sqrt(ft)); it was ~0 N.m
- fractionality uses the reefed P and is capped at 1

ORC 2024-2026 changes:
- minimum flat 0.53
- twist function (5.40) with the overlap term; the 1/3 factor follows
  Figure 5.14, the printed equation gives no lowering at 150% overlap
- jib twist delta CEH removed, as in the 2026 document
- kites depower by reef: area * reef^2, CE * reef, reef >= Kite.min_reef
  (default 0.85)
- reduced jib keeps its full hoist down to LPG = J (luff no longer goes
  to zero with ftj)
- sail coefficient tables updated to ORC 2026 (kites, low main/jib sets);
  the default kite is the symmetric spinnaker

Also adds a low-speed fallback start for the first point of a row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JN4y2NWa8kFMBZ5FV92jkj
Figure.png stays as the README image, since GitHub cannot inline a PDF.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JN4y2NWa8kFMBZ5FV92jkj
Kites take a max_heel (default 21.5) which tightens the heel bound of
the equilibrium solve; ORC models it as a soft resistance boundary.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JN4y2NWa8kFMBZ5FV92jkj
TWS is given at 10 m and scaled with a log profile (z0 = 0.005 m) to
the sail plan's centre of effort, and to half the freeboard for hull
windage. The cross-track true wind is projected on the heeled mast
plane with cos(heel) (ORC VPP 2026, 7.1).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JN4y2NWa8kFMBZ5FV92jkj
- Rudder lift area ramps from 0 upright to twice its area at 30 deg.
- Induced drag heel multiplier (ORC Fig. 6.10), faded out with the
  drive to side force ratio from 0.2 to 0.4.
- Keel root free surface and funsteady factors on the appendage
  effective span (ORC 6.5.1); HydroMod.update takes an optional tws.
- Appendage friction from five strips with the ORC friction line, the
  t/c form factor and lift-dependent drag (ORC 6.1.2).
- Bulbs no longer carry side force or double their wetted area.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JN4y2NWa8kFMBZ5FV92jkj
Ar was span/area (dimensional). It is now span^2/area, doubled for the
hull reflection, and the lift slope uses 2*pi*Ae/(Ae+2), which grows with
aspect ratio. The short keel Jones slope uses the same Ae.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JN4y2NWa8kFMBZ5FV92jkj
- bump version to 1.0.0 and update the author email
- remove the per-module __author__/__version__ headers
- drop the unused verbose argument from VPP.run and its callers
- README: document the PDF outputs, benchmark figures and appendage lift

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JN4y2NWa8kFMBZ5FV92jkj
@marinlauber
marinlauber merged commit bd97605 into master Oct 17, 2026
3 checks passed
@marinlauber
marinlauber deleted the pr/core-physics branch October 17, 2026 08:49
@marinlauber

Copy link
Copy Markdown
Owner

This fixes most parts of #55, I'll close the old one.

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.

2 participants