Skip to content

Add load_state and init!(s; initial_state) to continue a simulation - #336

Merged
ufechner7 merged 10 commits into
mainfrom
load_sys_state
Oct 17, 2026
Merged

ufechner7 merged 10 commits into
mainfrom
load_sys_state

Conversation

@ufechner7

@ufechner7 ufechner7 commented Oct 12, 2026 •

Copy link
Copy Markdown
Member

Based on main. It was stacked on #335, which is merged now, and main has been merged into this branch.

What

  • load_state(filename) reads a file written by save_state. It returns a new SysState and the meta data dict.
    • The number of points and springs comes from the lengths of X and spring_force.
    • Fields that are not in the file keep their default values.
    • A vector of the wrong length throws an ArgumentError.
  • init!(s; initial_state) (KPS3 and KPS4) continues a simulation from a SysState.
    • It restores the particle positions and velocities, the tether length, the reel-out speed, the set values and internal state of the winch, and the actual and set depower and steering of the KCU.
    • The winch's rate-limited set speed and brake state are stored by update_sys_state! in the free fields var_13 and var_14. Saved during a winch ramp, the tether length after one more second differs by 0.3 mm from the uninterrupted run, instead of 0.5 m without them.
    • The accelerations are not stored in a SysState, so they are computed from the residual. This gives the solver a consistent yd0.
    • For KPS4, the pitch rate of the kite (used by the pitch damping term, cmq) is computed from the particle velocities by the new internal calc_pitch_rate, instead of starting at zero. next_step! uses the change of the pitch angle over the last step instead, so the two differ by O(dt), here about 0.0025 rad/s at 20 Hz. This only affects the first step after loading.
    • The integrator continues at initial_state.time, which must be the time of the state (integrator.t when it was saved). A SysState from update_sys_state! holds the start time of the last step instead; the example scripts save integrator.t.
  • examples_3d/parking_wind_dir.jl: with LOAD_SYS_STATE = true, the script continues from output/sys_state_parking_wind_dir.yaml instead of starting at zero, and does not save the state. If the file does not exist yet, it starts at zero and saves it.

Limitations

  • Not restored: the internal state of the caller's controllers. The parking controller therefore has a short steering transient after loading.

Testing

New testsets in test/test-update-sys-state.jl:

  • load_state:
    • A KPS4 state saved with all fields loads back with every field equal, including NaNs and Float32 vectors.
    • A KPS3 state saved with UNUSED_FIELDS_KPS3 loads back and keeps the defaults for the excluded fields.
    • A file with a vector of the wrong length is rejected.
  • init! from a loaded state, for KPS4 and KPS3: a steered kite is saved after 40 steps (KPS3 with UNUSED_FIELDS_KPS3), then the original and a restarted model each run 20 more steps.
    • The integrator times are equal.
    • The positions differ by less than 1 mm (for KPS4, 0.007 mm right after loading, from Float32 rounding).
    • The winch forces agree within rtol=1e-4.
    • A state with a different number of points is rejected.
    • For KPS4, the restored pitch rate matches calc_pitch_rate of the original within rtol=1e-4 (YAML rounding), and the original's pitch rate within 10%.
    • KPS3 runs with a solver tolerance of 1e-6. With the default of 1e-3, a turning KPS3 is only accurate to about half a metre after one second; the restarted BDF solver starts again at low order and lands at a different point within that error band. The difference after 1 s is 0.44 m at 1e-3, 1.3 mm at 1e-5 and 0.04 mm at 1e-7, also when restarting from an exact Float64 state, so it is not missing state.

All testsets in that file pass. parking_wind_dir.jl was run in the viewer with LOAD_SYS_STATE = true and works.

🤖 Generated with Claude Code

@ufechner7
ufechner7 force-pushed the load_sys_state branch 4 times, most recently from bcc289a to d68c688 Compare October 12, 2026 20:19
ufechner7 and others added 2 commits October 12, 2026 22:23
- load_state(filename) reads a SysState and the meta data that save_state wrote
- init! takes the keyword initial_state (KPS4) and continues from it: positions,
  velocities, tether length, reel-out speed and KCU state are restored, the
  accelerations are calculated from the residual, and the integrator continues
  at the time of the state
- parking_wind_dir.jl continues from its saved state if LOAD_SYS_STATE is true

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The example scripts now save integrator.t as SysState.time, so init! continues
at initial_state.time instead of one time step later. parking_wind_dir.jl sets
the time of the loaded SysState back to the start of the last time step, which
update_sys_state! needs to calculate the rates.

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

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

Restart initialization omits important actuator and winch state, mishandles turbulent-wind time, and enables loading a nonexistent file by default.

3 open findings
What changed in this PR

Extends KPS4 state persistence to support resuming simulations.

Changes:

  • Adds load_state and KPS4 init!(...; initial_state).
  • Adds restart coverage and documentation.
  • Updates the parking example to optionally resume a saved state.
File Description
src/​KPS4.jl Restores KPS4 simulation state.
src/​KiteModels.jl Adds loading and initialization APIs.
test/​test-update-sys-state.jl Tests state loading and continuation.
examples_3d/​parking_wind_dir.jl Demonstrates simulation restart.
docs/​src/​functions.md Lists load_state.
CHANGELOG.md Documents the new functionality.

🧠 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 examples_3d/parking_wind_dir.jl Outdated
Comment thread src/KPS4.jl Outdated
Comment thread src/KiteModels.jl Outdated
ufechner7 and others added 3 commits October 12, 2026 22:33
init_from_state! moves from KPS4.jl to KiteModels.jl and takes the velocities
directly from the SysState, since KPS3 has no vel field; both models use the
same state vector and residual convention. The restart test runs for both
models; for KPS3 with a tolerance of 1e-6, because with the default of 1e-3 a
turning KPS3 is only accurate to about half a metre and the restarted solver
takes different steps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…mments

- update_sys_state! stores the rate limited set speed and the brake state of the
  winch in var_13 and var_14; init! restores them together with the set speed
  and set torque. Saved during a winch ramp, the tether length after one more
  second differs by 0.3 mm instead of 0.5 m from the uninterrupted simulation.
- parking_wind_dir.jl starts at zero and saves the state if LOAD_SYS_STATE is
  true but the state file does not exist yet.
- load_state docstring: missing fields keep the SysState defaults, which are
  zero except for the orientation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Calculate it from the velocities of the particles with calc_pitch_rate instead
of setting it to zero.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ufechner7
ufechner7 changed the base branch from fill-sysstate-fields to main October 15, 2026 11:55
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@ufechner7
ufechner7 merged commit 4b0467b into main Oct 17, 2026
5 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.

4 participants