Skip to content

Linearize opened signals in trajectory_ss at their trajectory values and correct the batch-linearization tutorial - #86

Merged
baggepinnen merged 3 commits into
masterfrom
fbc/trajectory-loop-opening-defaults
Oct 12, 2026
Merged

baggepinnen merged 3 commits into
masterfrom
fbc/trajectory-loop-opening-defaults

Conversation

@baggepinnen

@baggepinnen baggepinnen commented Aug 6, 2026 •

Copy link
Copy Markdown
Member

Opening a loop during linearization turns the opened signal into a parameter. The batch-linearization tutorial previously held these signals at 0, which linearizes at the wrong point for systems that are nonlinear in the opened signal. In the tutorial, this was visible as identical controller linearizations along the entire trajectory in the plot "Loop open at v and y", although the scheduling input varies.

The semantics are now defined in ModelingToolkit (SciML/ModelingToolkit.jl#5265, summarized in JuliaComputing/DyadControlSystems.jl#817): a loop opening changes the input-output structure of the linearization, not the operating point. A disconnected signal is held at the value of its driver at the operating point of the loop-closed system, which along a solution is its value in the solution at each time point. An entry in op takes precedence, e.g., op = Dict(sys.opened_signal => 0).

Change

  • trajectory_ss relies on this default and no longer constructs its own (default_loop_opening_op and strip_root_namespace, added in the first version of this PR, are removed). The docstring documents loop_openings and op accordingly.
  • Tutorial: the explicit op = Dict(… => 0) entries are removed, and the text explains that the parts are linearized at the operating point of the closed loop. The reconstruction of the closed loop from its parts is corrected and checked against the linearization of the closed loop with @test:
    • Opening only at y retains the path from the controller output through the plant to the scheduling input, so feedback(P * C_y) reproduces the loop-closed transfer function from r to y exactly.
    • Opening at u yields the controller evaluated at the scheduling value, without the derivative of the controller output with respect to the scheduling variable. feedback(P * C_u) omits this path and differs from the closed loop (by up to 7 % in the frequency response on the tutorial model).
    • Opening at v and y coincides with opening at u, since the opened scheduling signal keeps its value in the solution.
    • The two-input controller [r, v] -> u previously entered the loop as feedback(C * P), which treats the scheduling channel as negative feedback. The scheduling variable enters the controller with a positive sign, so the input loop gain is (C_r - C_v) P (verified against the complementary sensitivity function at u computed by ModelingToolkit). The tutorial now forms the transfer function from r to y, feedback(P, C_r - C_v) * C_r, which reproduces the closed loop exactly.
    • The closing paragraph distinguishes the purposes: opening at u to obtain the controller itself, and opening only at y, or using the two-input controller, to reconstruct the closed loop.
  • test/test_batchlin.jl checks that the default equals the explicit mapping of the opened signal to its driving signal.

Depends on SciML/ModelingToolkit.jl#5265 (stacked on SciML/ModelingToolkit.jl#5264 and SciML/ModelingToolkit.jl#4894). [sources] in Project.toml and docs/Project.toml points to the branch of SciML/ModelingToolkit.jl#5265 until it is released; it must be reverted to master and the ModelingToolkit compat bound raised to the release containing it before this PR is merged.

Verification

With the ModelingToolkit branch, test/test_batchlin.jl passes locally (Julia 1.13.0). The tutorial code was run without the plotting calls, with the following maximum relative errors of the frequency responses over 81 time points: opening at y 3e-14, opening at u 6.8e-2, two-input controller 1.5e-12, v and y versus u 3e-14. The documentation was not built.


Host: demeter2, Claude Code session 5674d4b9-6638-4fc2-bfbf-06ad6aac6235, working directory /home/baggepinnen/.julia/dev/DyadControlSystems (ControlSystemsMTK edited in a clone in the session scratch directory)

🤖 Generated with Claude Code

https://claude.ai/code/session_01KecV2uixYREhdQWw1XSAC8

baggepinnen and others added 3 commits August 6, 2026 12:00
Opening a loop turns the opened signal into a parameter whose value must be
supplied in the operating point. Previously the tutorial held these at 0,
which linearizes at the wrong point for systems nonlinear in the opened
signal — visible as identical controller linearizations along the whole
trajectory in the batch-linearization tutorial.

trajectory_ss now maps each opened signal to itself as a symbolic operating-
point value, which ModelingToolkit.LinearizationOpPoint resolves from the
loop-closed solution at each time point (requires the corresponding MTK
support for symbolic op values). User-supplied op entries take precedence.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SWPXe76HwiugWWyLP7BYrL
…lues

SciML/ModelingToolkit.jl#5265 holds every signal disconnected by a loop opening at the value of its driver at the operating point of the loop-closed system, which along a solution is its value in the solution. `trajectory_ss` therefore no longer maps the opened signals to themselves (`default_loop_opening_op` and `strip_root_namespace` are removed), and `[sources]` points to the branch of that PR until it is released.

The batch-linearization tutorial now explains that loop openings do not change the operating point, and corrects the reconstruction of the closed loop from its parts: opening only at `y` reproduces the loop-closed transfer function exactly, opening at `u` omits the dependence of the controller output on the scheduling variable, and the two-input controller must enter the loop with the input loop gain `(C_r - C_v) P`, since the scheduling variable enters the controller with a positive sign. The exact reconstructions are checked against the linearization of the closed loop.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KecV2uixYREhdQWw1XSAC8
@baggepinnen baggepinnen changed the title Default loop-opened signals in trajectory_ss to their trajectory values Linearize opened signals in trajectory_ss at their trajectory values and correct the batch-linearization tutorial Oct 12, 2026
@baggepinnen
baggepinnen merged commit 46db642 into master Oct 12, 2026
2 checks passed
@baggepinnen
baggepinnen deleted the fbc/trajectory-loop-opening-defaults branch October 12, 2026 12:29
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.

1 participant