Skip to content

Add trim option, deprecate delta and stiffness_factor, fix steady-state wind and reel-out velocities - #332

Open
ufechner7 wants to merge 13 commits into
mainfrom
trim
Open

ufechner7 wants to merge 13 commits into
mainfrom
trim

Conversation

@ufechner7

@ufechner7 ufechner7 commented Oct 11, 2026 •

Copy link
Copy Markdown
Member

Summary

  • New: find_steady_state!(s; trim=true) (KPS3 and KPS4). Instead of prescribing set.elevation, the vertical force balance of the kite is solved too, so the kite is placed at the elevation where its lift matches gravity. The default trim=false keeps the previous behaviour.
  • New: init!(s; trim=true) passes trim through to find_steady_state!. The initial state is then an equilibrium of the simulation, so the elevation stays constant.
  • KPS4 trim balances gravity. KPS4's init sets the vertical accelerations in yd0 to -g, so the steady-state solver balances the forces without gravity (the existing sum(res2) ≈ -9.81*N tests rely on this for trim=false, which is unchanged). With trim=true the accelerations are zero, so the kite is trimmed with its weight. KPS3 already balanced gravity.
  • Deep stall trims are rejected. A second, fully balanced trim exists at low elevations with the kite in deep stall. Trimmed solutions with an angle of attack beyond the angle of maximum lift (from alpha_cl/cl_list) are rejected. KPS4 starts the trim search at 80° (or at set.elevation, if higher), because the solver converges from above the natural elevation but often not from far below it. If no valid trim is found, the solver is restarted at other elevations in steps of 10°, with a cheaper solve (no perturbation, no last-resort continuation). If no attached-flow trim is found at all, find_steady_state! throws an error instead of returning a deep-stall or non-converged state, and init!(s; trim=true) prints the reason and returns nothing. With system.yaml and the Maasvlakte profile at 4 m/s, where no such trim exists, failing takes 7 s (KPS3) and 11 s (KPS4).
  • delta is deprecated and ignored (find_steady_state! and init!). It perturbed the initial state, and the returned state was perturbed too, so it wasn't an equilibrium. Now the steady state is solved without perturbation, with delta = 0.001 only as an internal fallback, and always returned unperturbed.
  • stiffness_factor is deprecated and ignored (find_steady_state! and init!). Before, the KPS4 steady state was solved for the reduced tether stiffness stiffness_factor, which next_step! then raised by 0.01 per step to 1.0, so the initial state was not an equilibrium of the simulation and depended on the chosen value. Now the steady state is always solved for the full stiffness (s.stiffness_factor == 1.0 afterwards). A reduced stiffness is only used internally as start value: if the direct solve fails, the schedules 0.1 → 1, 0.1 → 0.3 → 1 and 0.3 → 1 are tried, then the previous continuation (see Solver strategy). For KPS3 the first solve uses a fixed 0.035; its result was already solved for the full stiffness.
  • Warning for deep stall at a prescribed elevation. With trim=false, a prescribed elevation far below the natural one can only be balanced in deep stall; this was returned silently. find_steady_state! now warns and suggests trim=true or a higher elevation. With system.yaml this happens below about 45° for KPS4 and 65° for KPS3.
  • Fix (KPS4): steady state used the ground wind. The wind was set once from the kite height before solving, which is 0 m after clear!, so the kite was balanced for 9.51 m/s instead of 12.6 m/s. The wind is now set from the kite height of every solver iterate.
  • Fix (KPS4): wrong initial reel-out velocities. The particle velocities were multiplied by the segment length (about 25 m), and the KCU direction came from the bridle: with v_reel_out = 1 m/s the KCU started at 20.9 m/s and the kite at 5.0 m/s. With trim=true the kite was placed at 54.8° instead of 68.2° (examples/test_init_4p.jl) and then climbed for about 10 s, with a tether force peak of more than twice the steady value. Now each particle moves along its segment, growing linearly to v_reel_out at the KCU.
  • Fix (KPS3): v_reel_out was ignored in the steady state. All particles were at rest and the reel-out speed in the initial state was zero, so the initial elevation didn't depend on v_reel_out and the winch first had to accelerate. Now the particle velocities grow linearly to v_reel_out at the kite, and the state starts with the reel-out speed. With trim=true at 14.5 m/s: 73.5° at 0 m/s, 70.4° at 1 m/s, 60.9° at 4 m/s, constant during the simulation.
  • Examples: no longer pass delta or stiffness_factor; test_init_1p.jl and test_init_4p.jl use trim=true and print v_reel_out; compare_kps3_kps4.jl uses the same x limits for all frames (the kite seemed to jump after the first frame). The examples use MakieControlPlots from its main branch.

Solver strategy

Benchmark of nlsolve alone (without the elevation restarts) for KPS4 with system.yaml: 160 cases (wind 6–25 m/s, elevation 40–85°, tether 50–1000 m, with and without trim), of which 119 were solvable by at least one strategy. Failures among those 119:

Strategy Failures (fixed elevation / trim) Total time
1.0 directly 14 / 12 12.4 s
0.1 → 1.0 25 / 3 13.5 s
1.0, else 0.1 → 1.0 5 / 3 15.1 s
1.0, else 0.1→1, 0.1→0.3→1, 0.3→1 2 / 1 17.1 s
adaptive continuation from 0.1 24–32 / 3 19–28 s
previous continuation, target 1.0 2 / 0 22.9 s

Starting directly at 1.0 is fastest and works for most fixed-elevation cases, starting at 0.1 is needed mainly for trim, and the schedule chain and the previous continuation fail in different cases. All 41 unsolved cases were trims started at 40° or 55°. The chain, then the previous continuation, with the trim search starting at 80° solved all 160 cases, in 0.14 s on average (max. 1.2 s). With 12 segments: 0.6 s on average; 4 trim cases at 6 m/s and 50 m tether failed in the benchmark (in production the elevation restarts follow).

With the production find_steady_state! (including restarts) on the same 160 cases: KPS4 solves all, 24.9 s in total (max. 2.7 s); KPS3 solves 156, the 4 failures are fixed-elevation cases (trim=false) at 20–25 m/s, 40–55° and 400–1000 m tether, which the trim changes don't touch.

Behaviour changes

  • Initial states of KPS4 change for everyone: the wind fix changes them for trim=false too (with system.yaml the winch force after find_steady_state! goes from 308 N to 456 N), and the tether is now only stretched as much as the full stiffness allows.
  • Initial states change for both models for runs with v_reel_out ≠ 0 (velocity fixes), and slightly for runs that used delta > 0 (including init!'s former default of 0.005).
  • Code that passes delta or stiffness_factor keeps working, with a Base.depwarn. KiteControllers.jl still passes both.
  • With trim=true, a case without an attached-flow trim now raises an error in find_steady_state! (and init! returns nothing); before, a deep-stall state was returned with a warning.

Performance

find_steady_state!(s; trim=true) for KPS4 takes 0.04–0.09 s, independent of set.elevation (10°, 30°, 55°, 70.8°, 85° tested); before, KPS4 started far below the trim needed restarts that took 12–45 s. The Trim testset went from 21 s to 2–3 s.

Tests

  • New test/test-trim.jl: both models, starts at 30°, 55° and 85°, all forces balanced to < 1e-4, no deep stall, no warnings, result independent of the start value, and the elevation stays constant after init!(s; trim=true); wind profiles Maasvlakte (EXPLOG) and Cabauw (EXP) at 4, 7 and 10 m/s (Maasvlakte at 4 m/s skipped: no attached-flow trim exists); delta and stiffness_factor are accepted and ignored; at a prescribed 40°, trim=false warns about deep stall and trim=true finds an attached-flow trim; with v_reel_out = 2 m/s the initial state contains the reel-out speed and the trimmed elevation is lower than at 0 m/s; for the Maasvlakte profile at 4 m/s (no attached-flow trim) find_steady_state! throws and init! returns nothing.
  • test/test-steady-state-robustness.jl: cases at 50° moved to 60° and 65°; the per-case stiffness factor is removed and the test checks stiffness_factor == 1.0 afterwards.
  • Raptures test (test-kps4.jl): stiffness raised to 20 instead of 3 to break the bridle, because the initial state is now at full stiffness.
  • Full test suite (runtests.jl) passes locally on Julia 1.13 (1 min 34 s); test-trim.jl alone: 112/112. Of the examples, only compare_kps3_kps4.jl was run.

🤖 Generated with Claude Code

ufechner7 and others added 2 commits October 11, 2026 02:24
find_steady_state!(s; trim=true) (KPS3 and KPS4) solves the vertical force
balance of the kite instead of prescribing set.elevation, placing the kite at
the elevation where its lift matches gravity. Deep stall trims (angle of
attack beyond the angle of maximum lift) are rejected, and the solver is
restarted at other elevations if no valid trim is found.

KPS4: the wind is now set from the kite height of each solver iterate.
Before, the kite was balanced for the ground wind (height 0 after clear!),
leaving force residuals of about 130 N on the kite points.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
init!(s; trim=true) passes trim to find_steady_state!.

KPS4: init sets the vertical accelerations in yd0 to -g, so the steady
state solver balanced the forces without gravity. With trim=true the
accelerations are now zero, so the kite is trimmed with its weight and the
returned state is an equilibrium of the simulation: with
init!(s; trim=true, stiffness_factor=1.0) the elevation stays within 0.1°
over 10 s. trim=false is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ufechner7 ufechner7 changed the title Add trim option to find_steady_state! and fix KPS4 steady-state wind Add trim option to find_steady_state! and init!, fix KPS4 steady-state wind Oct 11, 2026
For KPS3 and KPS4 at 4, 7 and 10 m/s: after
init!(s; trim=true, stiffness_factor=1.0, delta=0.0) the wind at the kite
matches the profile, the kite is not stalled, and the elevation stays
within 0.01° over 10 s. Maasvlakte at 4 m/s is skipped, because there the
kite cannot be carried with attached flow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov-commenter

codecov-commenter commented Oct 11, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 89.70588% with 14 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/KPS4.jl 84.61% 12 Missing ⚠️
src/KPS3.jl 95.65% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

The keyword argument delta of find_steady_state! and init! is deprecated
and ignored. The steady state is solved without perturbation, with a
perturbation of 0.001 only as an internal fallback, and it is always
returned without perturbation, so it is an equilibrium of the simulation.
KPS3 now converges in all 72 cases of a sweep over wind speed, elevation,
tether length and stiffness factor. The examples, tests and precompile.jl
no longer pass delta.

With trim=false, find_steady_state! warns if the kite is in deep stall,
which happens if the prescribed elevation is far below the natural
elevation (with system.yaml below about 60°). The robustness test cases
at 50° are moved to 60° and 65° for this reason.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ufechner7 ufechner7 changed the title Add trim option to find_steady_state! and init!, fix KPS4 steady-state wind Add trim option, deprecate delta, fix KPS4 steady-state wind Oct 11, 2026
ufechner7 and others added 5 commits October 11, 2026 09:41
The velocities were multiplied by the length of the tether segments, so
with trim=true the kite was placed far below its natural elevation.
Use trim=true in test_init_4p.jl and print v_reel_out.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The steady state is now always solved for the full tether stiffness, so
the initial state is an equilibrium of the simulation. A reduced
stiffness is only used internally as start value: if the direct solve
fails, the schedules 0.1 -> 1, 0.1 -> 0.3 -> 1 and 0.3 -> 1 are tried,
then the previous continuation. With trim=true, the KPS4 search starts
at 80 degrees, from where the solver converges.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The particles were at rest and the initial reel-out speed was zero, so
the initial elevation did not depend on v_reel_out. Now the particle
velocities grow linearly along the tether to v_reel_out at the kite, and
the initial state contains the reel-out speed, as for KPS4.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The first frame used xlim = (35, 55) and the later frames (35, 75), so the
kite seemed to jump after the first frame. Both now use (40, 55). Also
use MakieControlPlots from its main branch in the examples.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@ufechner7 ufechner7 changed the title Add trim option, deprecate delta, fix KPS4 steady-state wind Add trim option, deprecate delta and stiffness_factor, fix steady-state wind and reel-out velocities Oct 11, 2026
@ufechner7
ufechner7 requested a balanced review from Copilot October 11, 2026 09:32

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

Both trim solvers can still return an invalid deep-stall or unconverged state after failing to find the promised attached-flow equilibrium.

6 open findings
What changed in this PR

Adds natural-elevation trimming to KPS3/KPS4 while correcting steady-state wind, reel-out velocity, and tether-stiffness initialization.

Changes:

  • Adds trim=true, attached-flow checks, and solver restart strategies.
  • Deprecates delta and stiffness_factor; initializes at full stiffness.
  • Updates tests, examples, documentation, and release notes.
File Description
src/​KPS3.jl Adds trimming and reel-out-aware initialization.
src/​KPS4.jl Adds trimming, wind updates, continuation, and velocity fixes.
src/​KiteModels.jl Adds shared options, deprecations, and init! integration.
src/​precompile.jl Updates precompile calls.
test/​test-trim.jl Tests trimming, profiles, deprecations, and reel-out.
test/​test-steady-state-robustness.jl Updates full-stiffness robustness cases.
test/​test-steady-state-kps4.jl Removes deprecated arguments.
test/​test-winch-state.jl Uses default initialization.
test/​test-update-sys-state.jl Removes deprecated options.
test/​test-simulate_4p.jl Updates initialization.
test/​test-kps4.jl Updates steady-state and breakage tests.
test/​test-kps3.jl Updates steady-state calls.
test/​test-interface.jl Updates interface tests.
test/​test_for_precompile.jl Updates precompile test initialization.
test/​plot_kps4.jl Updates plotting setup.
examples/​Project.toml Sources MakieControlPlots from main.
examples/​test_steady_state.jl Removes deprecated options.
examples/​test_init_4p.jl Demonstrates trimmed KPS4 initialization.
examples/​test_init_1p.jl Demonstrates reel-out-aware KPS3 trim.
examples/​steering_test_4p.jl Updates initialization.
examples/​steering_test_1p.jl Updates initialization.
examples/​simulate_steering.jl Updates initialization.
examples/​simulate_simple.jl Updates non-steady initialization.
examples/​reel_out_4p.jl Updates initialization.
examples/​reel_out_4p_torque_control.jl Updates initialization.
examples/​reel_out_1p.jl Updates initialization.
examples/​plot_side_cl.jl Updates initialization.
examples/​plot_pitch_stability.jl Updates initialization.
examples/​plot_parking_test.jl Updates initialization.
examples/​plot_cl_cd.jl Updates initialization.
examples/​plot_alpha2.jl Updates initialization.
examples/​compare_kps3_kps4.jl Stabilizes plot limits and updates initialization.
examples/​calc_spectrum.jl Updates initialization.
examples/​bench.jl Updates KPS3 benchmark setup.
examples/​bench_4p.jl Updates KPS4 benchmark setup.
examples_3d/​parking_wind_dir.jl Partially removes deprecated options.
examples_3d/​parking_4p.jl Partially removes deprecated options.
examples_3d/​auto_parking_4p.jl Partially removes deprecated options.
docs/​src/​examples_4p.md Removes obsolete stiffness explanation.
CHANGELOG.md Documents trim, deprecations, and fixes.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/KPS3.jl Outdated
Comment thread src/KPS4.jl Outdated
Comment thread examples_3d/auto_parking_4p.jl Outdated
Comment thread examples_3d/parking_4p.jl Outdated
Comment thread examples_3d/parking_wind_dir.jl Outdated
Comment thread src/KiteModels.jl Outdated
ufechner7 and others added 3 commits October 11, 2026 12:13
With trim=true, find_steady_state! (KPS3 and KPS4) returned a deep stall
or non-converged state if all restarts failed. Now it throws an error, so
init! returns nothing instead of initializing from an invalid trim. The
restarts use 10 degree steps and a cheaper solve, so that failing takes
7 s (KPS3) and 11 s (KPS4) instead of 25 s and 78 s.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The keyword is deprecated and ignored. Also update the paragraph in
examples_4p.md, which still described the reduced stiffness.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
With steady_state=false, the initial state is not an equilibrium.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@ufechner7
ufechner7 requested a review from 1-Bart-1 October 11, 2026 10:19

This branch has not been deployed

No deployments
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