Repository navigation
Compute the default simulation time vector from the decay rates of the non-integrator poles - #1085
Merged
Merged
Conversation
…e non-integrator poles The default sample interval and final time of step, impulse and lsim are computed from the poles of the system as follows: - Poles in the origin (z = 1 in discrete time) are classified as integrators with the multiplicity-dependent tolerance of count_eigval_multiplicity, scaled by the norm of A. Previously, an exact comparison with zero was used, and a multiple pole in the origin computed with rounding errors yielded a sample interval of up to 4e14. - Discrete-time poles are mapped to the s-plane by s = log(z)/Ts. Previously, the magnitudes |z| were treated as frequencies, and the final time depended only on Ts. - The final time is computed from the decay rates |Re p| of the stable poles as mu + 6s, where mu and s are the sum of the time constants and the square root of the sum of their squares, which equals seven time constants for a single pole. Previously, the final time was half the slowest time constant, computed from |p| instead of |Re p|, and depended on the time unit through a minimum over both times and frequencies. - Unstable systems are simulated for five time constants of the fastest growing mode, undamped oscillatory modes for at least five periods. - The limit on the number of samples is enforced by increasing the sample interval of a continuous-time system, and by reducing the final time with a warning for a discrete-time system, instead of silently reducing the final time. The resulting time vector is invariant under time_scale up to rounding. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HigNnun4aJKMoWmY7wyULn
|
This is an automated message.
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #1085 +/- ##
==========================================
+ Coverage 91.73% 91.76% +0.03%
==========================================
Files 42 42
Lines 5865 5892 +27
==========================================
+ Hits 5380 5407 +27
Misses 485 485 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
baggepinnen
referenced
this pull request
Oct 13, 2026
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HigNnun4aJKMoWmY7wyULn
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.








Part of #1081.
Problem
The default time vector of
step,impulseandlsim(sys, u::Function, tfinal)(_default_time_vector,_default_dtintimeresp.jl) had the following defects.iszero. A multiple pole in the origin is computed with rounding errors of ordereps^(1/k)·‖A‖, and the sample interval became very large:dt ≈ 1e4. The absolute threshold1e-6used for the final time was inconsistent with this test.minimum(w for w in ws if w > 1e-6; init=dt)included a time (dt) in a minimum over frequencies, so the result depended on the time unit: the step response of1/((s+1)(s+0.01))ended aty = 0.387with the time unit s and aty = 1.0with the time unit ms. (b)1/(2ω_min)is half of the slowest time constant: for the poles-1and-1e-3, the step response ended aty(500) = 0.39. (c) The threshold1e-6was absolute. (d)|p|was used instead of the decay rate|Re p|: forζ = 0.01, the envelope was still0.85attfinal.|z| ≈ 1were treated as frequencies in rad/s, so thattfinaldepended only onTs:step(c2d(ss(tf(1, [100, 1])), Ts))ended aty = 0.39forTs = 0.01,y = 0.86forTs = 1andy = 0.095forTs = 1e-4.100_000samples was enforced by silently reducingtfinal.Change
_nonintegrator_poles(sys): poles in the origin (z = 1in discrete time) are classified as integrators withcount_eigval_multiplicity, i.e., the tolerance(100ϵ)^(1/k)·scalefor a pole of multiplicityk, as incount_integrators/integrator_excess. The scale includesopnorm(A, 1), since the rounding errors of the eigenvalues are proportional to the norm ofA, which is much larger than the magnitude of the computed poles if all poles are in the origin. Discrete-time poles are mapped bys = log(z)/Ts; a pole atz = 0is mapped tos = -∞and does not affect the final time._default_dt(sys)(continuous time):1/(12 max|p|)over the non-integrator poles, as before. If all poles are integrators or the system has no poles,dt = 0.05as before. A system whose poles are all in the origin, such as1/s^k, has no characteristic time (it is invariant under time scaling up to a gain). I did not useopnorm(A)as a time scale for this case, since the norm of a nilpotentAdepends on the scaling of the state variables ([0 c; 0 0]is similar to[0 1; 0 0]for everyc ≠ 0)._default_tfinal(sys, dt):tfinal = μ + 6swithμ = Σ τᵢ,s = √(Σ τᵢ²)andτᵢ = 1/|Re pᵢ|over the stable non-integrator poles. The impulse response of a series connection of first-order systems with time constantsτᵢequals the probability density of a sum of independent exponentially distributed random variables with meansτᵢ, whose mean and standard deviation areμands. For a single pole,tfinal = 7τ, after whichexp(-7) < 1e-3of the initial deviation remains; for a pole of multiplicitym, the term6√m τaccounts for the polynomial factorstᵐ⁻¹e^{-t/τ}(for1/(s+1)^5,tfinal = 18). Each pole of a complex-conjugate pair is counted, which yields≈ 10.5τfor a single pair; counting each pair once would maketfinaldepend discontinuously on the poles, since rounding errors split a multiple real pole into complex-conjugate pairs (this was observed for(s+1)^5after a conversion to a transfer function). The time constants are bounded from below bydt, since a discrete-time mode does not decay faster than in one sample interval.|Re p|below the tolerance100ϵ·scaleof a simple eigenvalue): at least five periods of the slowest such mode.e^5is visible but the response does not overflow (previouslye^16.6for1/(s-1)).200dt, as before.tfinalis rounded to two significant digits.100_001): for a continuous-time system,dtis increased (the simulation of a continuous-time system uses a ZoH discretization, so the samples remain exact, only the resolution of the plot is reduced); for a discrete-time system, whose sample interval is fixed,tfinalis reduced and a warning is emitted.ControlSystemsBase.time_scaleup to the rounding to two significant digits, and for discrete-time systems it does not depend onTs(unless the limit on the number of samples is reached).stepandimpulsedescribe the rule.DelayLtiSystemandHammersteinWienerSystemdo not definepoles, they keeptfinal = 200dtwith their own_default_dt(HammersteinWienerSystemuses the new_default_dtof its linear part)._default_time_datainplotting.jlhas no callers and is unchanged.Effect on users
The default time vector changes for most systems. Systems with only integrators and static systems are unchanged. Representative systems (
y(tfinal)is the last value of the step response, the DC gain is 1 for all stable systems below;dcgainof1/(s-1)is-1):1/(s+1)1/(s+1), time unit ms (time_scale(·, 1e3))1/(s+1)^51/(s²+0.02s+1)(ζ = 0.01)1/(s²+2e-4s+1)(ζ = 1e-4)1/((s+1)(s+1e-3))1/((s+1)(s+0.01))1/((s+1)(s+0.01)), time unit ms1/s1/(s(s+1))1/s², transformed coordinatesminreal(ss((s+1)/(s²(s+1))))1/s³, transformed coordinates1/(s²+1)1/(s-1)1/((s+0.5)(s-1))1/(100s+1)c2d(1/(100s+1), 1e-4)c2d(1/(100s+1), 0.01)c2d(1/(100s+1), 1)c2d(1/(100s+1), 10)1/(z-1), Ts = 0.11/z, Ts = 0.1Notable changes:
ζ = 1e-4, the limit on the number of samples increasesdtto 1.0, i.e., about six samples per period of the oscillation.z = 0) are simulated for a few samples only.stepof a discrete-time system whose slowest mode requires more than100_000samples now emits a warning.Tests
Added a testset
default time vectortolib/ControlSystemsBase/test/test_timeresp.jl: the reproducers above (multiple integrators in transformed coordinates), the final value of the step response compared with the DC gain for stable systems (first order, ζ = 0.01, widely separated poles, a pole of multiplicity five, a third-order system) in three time units, time-scale invariance oftfinalanddt, independence ofTsfor a discrete-time system, the warning for a discrete-time system that exceeds the limit on the number of samples, the coarsening ofdtfor a continuous-time system, unstable systems in three time units, undamped oscillatory systems, and user-providedtfinal. The existing tests of_default_dtand_default_time_vectorpass unchanged.Results (Julia 1.12):
lib/ControlSystemsBase/test:test_timeresp,test_discrete,test_delayed_systems,test_hammerstein_wiener: 457/457 passed.lib/ControlSystemsBase/test:test_simplification,test_plots,test_staticsystems,test_analysis,test_demo_systems,test_complex,test_partitioned_statespace,test_dsp,test_implicit_diff,test_rootlocus: 3494/3494 passed.test/runtests_controlsystems.jl(ControlSystems:test_timeresp, including the test of the length of the default time vector,test_delay_timeresp,test_nonlinear_timeresp): 1151/1151 passed.Host:
demeter6, Claude Code sessionsession_01HigNnun4aJKMoWmY7wyULn, working directory~/.julia/dev/ControlSystems/.claude/worktrees/agent-a4335f463fd6c1073🤖 Generated with Claude Code
https://claude.ai/code/session_01HigNnun4aJKMoWmY7wyULn