{{ message }}
Repository navigation
Make linearization a query and add system level to_statespace() - #239
Merged
Merged
Conversation
This was referenced Jul 25, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Makes linearization a query that returns matrices instead of a mode the system
is switched into, and builds the system level assembly on top of it. Design
discussed in #195.
Changes
Block.to_statespace(t)returns the local(A, B, C, D)in the currentoperating point. Pure query, the block keeps evaluating its original functions.
Reads the Jacobians from the existing
Operator.jac/DynamicOperator.jac_x/.jac_u, which never needed the mode switch.IntegratorandODEoverride itwith their exact models instead of being differenced numerically.
Block.linearize(t)does what it always did and additionally returns thematrices it computes anyway. The return value was
None, so nothing breaks.Blocks without a valid linear model raise
LinearizationError, declaredthrough a
linearizableclass attribute:Comparator,Relay,Switch,RNG,Counter, theLogicblocks, the discrete time blocks,ADC/DACandDelay.This means
Simulation.linearize()now raises on a system containing one ofthese where it previously succeeded silently. That is intended -- a "linearized"
system with a relay in it was never linear.
Simulation.to_statespace(inputs, outputs, t=None)assembles the globalmodel and returns a
StateSpaceblock. The assembly stacks the local modelsblock diagonally and eliminates the internal signals in one linear solve
(
(I - L·D)·v = L·C·x + M·u) rather than substituting along a topological order,so algebraic loops surviving the input break are resolved instead of rejected.
Well posedness is the invertibility of that matrix.
StateSpacegainsstate_labels/input_labels/output_labelsconstructor arguments. These are the
states/inputs/outputskeywordarguments of
control.StateSpace, so an assembled model hands over topython-control without an adapter.
Subsystemimplements the block interface recursively.state(getter andsetter),
get_allandderivativewere inherited fromBlockand read thedummy engine, which is only a marker and never written -- a subsystem with two
internal integrators reported a single placeholder state.
to_statespace()takesno break or tap points because the interface already designates them, and since a
Subsystemis aBlockit composes: nested hierarchies linearizehierarchically.
Test plan
pytest -m "slow or not slow"-- 1048 passed, 12 skippedtests/pathsim/optim/test_linearization.py, checking theassembly against analytic models for gain cascades, closed loops, MIMO
round trips, resolved algebraic loops, subsystems and nested subsystems
nonlinear system it was linearized from
Pre-existing on
masterand unrelated: the 15test_fmuwrapper.pymodelexchange failures, and
steadystate()failing withLinAlgErroron a PID plusnonlinear plant loop.
Not in this PR
trim()-- solving backwards for the input that produces a desired output. Wantedas an extension of
steadystate()rather than a separate solver stack, see #237and #195.