Skip to content

Commit 6327807

Browse files
authored
Merge pull request #748 from JuliaControl/lsimerror
Add error hint to lsim
2 parents 6737bf1 + ff258eb commit 6327807

16 files changed

+59
-35
lines changed

Diff for: .github/workflows/test.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919
strategy:
2020
fail-fast: false
2121
matrix:
22-
version: ['1.7']
22+
version: ['1.8.1']
2323
os: [ubuntu-latest]
2424
arch: [x64]
2525
group:
@@ -47,8 +47,8 @@ jobs:
4747
GROUP: ${{ matrix.group }}
4848
# continue-on-error: ${{ matrix.version == 'nightly' }} # Allow nightly to fail and workflow still count as completed
4949
- uses: julia-actions/julia-processcoverage@v1
50-
if: ${{ matrix.version == '1.7' }}
50+
if: ${{ matrix.version == '1.8.1' }}
5151
- uses: codecov/codecov-action@v1
52-
if: ${{ matrix.version == '1.7' }}
52+
if: ${{ matrix.version == '1.8.1' }}
5353
with:
5454
file: lcov.info

Diff for: docs/Project.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
[deps]
22
ControlSystems = "a6e380b2-a6ca-5380-bf3e-84a91bcd477e"
3+
ControlSystemsBase = "aaaaaaaa-a6ca-5380-bf3e-84a91bcd477e"
34
DSP = "717857b8-e6f2-59f4-9121-6e50c889abd2"
45
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
56
GR = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71"
@@ -8,4 +9,4 @@ Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
89
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
910

1011
[compat]
11-
Documenter = "0.27.10"
12+
Documenter = "0.27.10"

Diff for: docs/src/examples/example.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ P = tf(B,A)
128128
129129
# output
130130
131-
TransferFunction{Continuous, ControlSystems.SisoRational{Float64}}
131+
TransferFunction{Continuous, ControlSystemsBase.SisoRational{Float64}}
132132
1.0
133133
-------------------
134134
1.0s^2 + 0.4s + 1.0

Diff for: docs/src/lib/nonlinear.md

+9-9
Original file line numberDiff line numberDiff line change
@@ -42,16 +42,16 @@ plot(step([G; Gu], 5), lab = ["Linear y" "Linear u"])
4242
plot!(step([Gnl; Gunl], 5), lab = ["Nonlinear y" "Nonlinear u"])
4343
```
4444

45-
Since the saturating nonlinearity is common, we provide the constructor [`ControlSystems.saturation`](@ref) that automatically forms the equivalent to `nonlinearity(x->clamp(x, -0.7, 0.7))` while at the same time making sure the function has a recognizable name when the system is printed
45+
Since the saturating nonlinearity is common, we provide the constructor [`ControlSystemsBase.saturation`](@ref) that automatically forms the equivalent to `nonlinearity(x->clamp(x, -0.7, 0.7))` while at the same time making sure the function has a recognizable name when the system is printed
4646
```@example nonlinear
4747
using ControlSystems: saturation
4848
saturation(0.7)
4949
```
5050

51-
See also [`ControlSystems.ratelimit`](@ref) that saturates the derivative of a signal.
51+
See also [`ControlSystemsBase.ratelimit`](@ref) that saturates the derivative of a signal.
5252

5353
### Non-zero operating point
54-
It's common to linearize nonlinear systems around some operating point. We may make use of the helper constructor [`ControlSystems.offset`](@ref) to create affine functions at the inputs and outputs of the linearized system to, e.g.,
54+
It's common to linearize nonlinear systems around some operating point. We may make use of the helper constructor [`ControlSystemsBase.offset`](@ref) to create affine functions at the inputs and outputs of the linearized system to, e.g.,
5555
1. Make sure that simulations result are given in the original coordinates rather than in the coordinates of the linearization point.
5656
2. Allow nonlinearities that are added back after the linearization (such as saturations) to operate with their original parameters.
5757

@@ -172,7 +172,7 @@ plot(step(duffing, 20), title="Duffing oscillator open-loop step response")
172172

173173
We now show how we can make use of the circle criterion to prove stability of the closed loop. The function `circle_criterion` below plots the Nyquist curve of the loop-transfer function and figures out the circle to avoid by finding sector bounds for the static nonlinearity ``f(x) = x^3``. We then choose a controller an check that it stays outside of the circle. To find the sector bounds, we choose a domain to evaluate the nonlinearity over. The function ``f(x) = x^3`` goes to infinity faster than any linear function, and the upper sector bound is thus ∞, but if we restrict the nonlinearity to a smaller domain, we get a finite sector bound:
174174
```@example DUFFING
175-
function circle_criterion(L::ControlSystems.HammersteinWienerSystem, domain::Tuple; N=10000)
175+
function circle_criterion(L::ControlSystemsBase.HammersteinWienerSystem, domain::Tuple; N=10000)
176176
fun = x->L.f[](x)/x
177177
x = range(domain[1], stop=domain[2], length=N)
178178
0 ∈ x && (x = filter(!=(0), x)) # We cannot divide by zero
@@ -244,9 +244,9 @@ plot(f1,f2, size=(1300,800))
244244
## Docstrings
245245

246246
```@docs
247-
ControlSystems.nonlinearity
248-
ControlSystems.offset
249-
ControlSystems.saturation
250-
ControlSystems.ratelimit
251-
ControlSystems.deadzone
247+
ControlSystemsBase.nonlinearity
248+
ControlSystemsBase.offset
249+
ControlSystemsBase.saturation
250+
ControlSystemsBase.ratelimit
251+
ControlSystemsBase.deadzone
252252
```

Diff for: docs/src/lib/plotting.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Pages = ["plotting.md"]
66
All plotting requires the user to manually load the Plots.jl library, e.g., by calling `using Plots`.
77

88
!!! note "Time-domain responses"
9-
There are no special functions to plot time-domain results, such as step and impulse responses, instead, simply call `plot` on the result structure ([`ControlSystems.SimResult`](@ref)) returned by [`lsim`](@ref), [`step`](@ref), [`impulse`](@ref) etc.
9+
There are no special functions to plot time-domain results, such as step and impulse responses, instead, simply call `plot` on the result structure ([`ControlSystemsBase.SimResult`](@ref)) returned by [`lsim`](@ref), [`step`](@ref), [`impulse`](@ref) etc.
1010

1111
# Plotting functions
1212

Diff for: docs/src/man/creating_systems.md

+5-5
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ tf([1.0],[1,2,1])
1919
2020
# output
2121
22-
TransferFunction{Continuous, ControlSystems.SisoRational{Float64}}
22+
TransferFunction{Continuous, ControlSystemsBase.SisoRational{Float64}}
2323
1.0
2424
-------------------
2525
1.0s^2 + 2.0s + 1.0
@@ -42,7 +42,7 @@ zpk([-1.0,1], [-5, -10], 2)
4242
4343
# output
4444
45-
TransferFunction{Continuous, ControlSystems.SisoZpk{Float64, Float64}}
45+
TransferFunction{Continuous, ControlSystemsBase.SisoZpk{Float64, Float64}}
4646
(1.0s + 1.0)(1.0s - 1.0)
4747
2.0-------------------------
4848
(1.0s + 5.0)(1.0s + 10.0)
@@ -71,7 +71,7 @@ function HeteroStateSpace(A,B,C,D,Ts=0,f::F=to_static) where F
7171
HeteroStateSpace(f(A),f(B),f(C),f(D),Ts)
7272
end
7373
HeteroStateSpace(s,f) = HeteroStateSpace(s.A,s.B,s.C,s.D,s.timeevol,f)
74-
ControlSystemsBase._string_mat_with_headers(a::SizedArray) = ControlSystems._string_mat_with_headers(Matrix(a)); # Overload for printing purposes
74+
ControlSystemsBase._string_mat_with_headers(a::SizedArray) = ControlSystemsBase._string_mat_with_headers(Matrix(a)); # Overload for printing purposes
7575
7676
nothing # hide
7777
```
@@ -90,7 +90,7 @@ tf(zpk([-1], [1], 2, 0.1))
9090
9191
# output
9292
93-
TransferFunction{Discrete{Float64}, ControlSystems.SisoRational{Int64}}
93+
TransferFunction{Discrete{Float64}, ControlSystemsBase.SisoRational{Int64}}
9494
2z + 2
9595
------
9696
z - 1
@@ -221,4 +221,4 @@ StateSpace[P_cont, P_disc]
221221
The type `StateSpace` is abstract, since the type parameters are not specified.
222222

223223
## Demo systems
224-
The module `ControlSystems.DemoSystems` contains a number of demo systems demonstrating different kinds of dynamics.
224+
The module `ControlSystemsBase.DemoSystems` contains a number of demo systems demonstrating different kinds of dynamics.

Diff for: docs/src/man/differences.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ The rest of this page will list noteworthy differences between ControlSystems.jl
99
- Simulation using [`lsim`](@ref), [`step`](@ref), [`impulse`](@ref) etc. return a structure that can be plotted. These functions never plot anything themselves.
1010
- Functions [`bode`](@ref), [`nyquist`](@ref) etc. never produce a plot. Instead, see [`bodeplot`](@ref), [`nyquistplot`](@ref) etc.
1111
- In Julia, functionality is often split up into several different packages. You may therefore have to install and use additional packages in order to cover all your needs. See [Ecosystem](@ref) for a collection of control-related packages.
12-
- In Julia, `1` has a different type than `1.0`, and the types in ControlSystems.jl respect the types chosen by the user. As an example, `tf(1, [1, 1])` is a transfer function with integer coefficients, while `tf(1.0, [1, 1])` will promote all coefficients to `Float64`.
12+
- In Julia, `1` has a different type than `1.0`, and the types in ControlSystemsBase.jl respect the types chosen by the user. As an example, `tf(1, [1, 1])` is a transfer function with integer coefficients, while `tf(1.0, [1, 1])` will promote all coefficients to `Float64`.
1313
- In Julia, code can often be differentiated using automatic differentiation. When using ControlSystems.jl, we recommend trying [ForwardDiff.jl](https://github.com/JuliaDiff/ForwardDiff.jl/) for AD. An example making use of this is available [here](https://github.com/JuliaControl/ControlExamples.jl/blob/master/autotuning.ipynb).
1414
- In Julia, the source code is often very readable. If you want to learn how a function is implemented, you may find the macros `@edit` or `@less` useful to locate the source code.
1515
- If you run into an issue (bug) with a Julia package, you can share this issue (bug report) on the package's github page and it will often be fixed promptly. To open an issue with ControlSystems.jl, [click here](https://github.com/JuliaControl/ControlSystems.jl/issues/new/choose). Thank you for helping out improving open-source software!

Diff for: docs/src/man/introduction.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ T = P/(1+P)
3232
3333
# output
3434
35-
TransferFunction{Continuous, ControlSystems.SisoRational{Float64}}
35+
TransferFunction{Continuous, ControlSystemsBase.SisoRational{Float64}}
3636
1.0s + 1.0
3737
-------------------
3838
1.0s^2 + 3.0s + 2.0
@@ -46,7 +46,7 @@ minreal(T)
4646
4747
# output
4848
49-
TransferFunction{Continuous, ControlSystems.SisoRational{Float64}}
49+
TransferFunction{Continuous, ControlSystemsBase.SisoRational{Float64}}
5050
1.0
5151
----------
5252
1.0s + 2.0

Diff for: docs/src/man/numerical.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ w = exp10.(LinRange(-2, 2, 20000))
4747
# 55.120 ms (517957 allocations: 24.42 MiB)
4848
@btime bode($G, $w, unwrap=false); # phase unwrapping is slow
4949
# 3.624 ms (7 allocations: 2.44 MiB)
50-
ws = ControlSystems.BodemagWorkspace(G, w)
50+
ws = ControlSystemsBase.BodemagWorkspace(G, w)
5151
@btime bodemag!($ws, $G, $w);
5252
# 2.991 ms (1 allocation: 64 bytes)
5353
```

Diff for: lib/ControlSystemsBase/src/ControlSystemsBase.jl

+10
Original file line numberDiff line numberDiff line change
@@ -212,4 +212,14 @@ end
212212
# The path has to be evaluated upon initial import
213213
const __ControlSystemsBase_SOURCE_DIR__ = dirname(Base.source_path())
214214

215+
function __init__()
216+
Base.Experimental.register_error_hint(MethodError) do io, exc, argtypes, kwargs
217+
if exc.f (lsim, step, impulse) && argtypes[1] <: LTISystem{Continuous}
218+
print(io, "\n$(exc.f) with continuous-time systems, including delay systems and nonlinear systems, require the user to first ")
219+
printstyled(io, "install and load ControlSystems.jl, or pass the keyword method = :zoh", color=:green, bold=true)
220+
print(io, " for automatic discretization (applicable to systems without delays or nonlinearities only).")
221+
end
222+
end
223+
end
224+
215225
end

Diff for: lib/ControlSystemsBase/src/delay_systems.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ i.e. of z^-N where N = τ / Ts.
4444
τ must be a multiple of Ts.
4545
"""
4646
function delayd_ss::Number, Ts::Number)
47-
n = Int(round/ Ts))
47+
n = round(Int, τ / Ts)
4848
if !- n*Ts 0)
4949
error("The delay τ must be a multiple of the sample time Ts")
5050
end

Diff for: lib/ControlSystemsBase/src/timeresp.jl

+6-4
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,9 @@ Calculate the step response of system `sys`. If the final time `tfinal` or time
4141
vector `t` is not provided, one is calculated based on the system pole
4242
locations. The return value is a structure of type `SimResult` that can be plotted or destructured as `y, t, x = result`.
4343
44-
`y` has size `(ny, length(t), nu)`, `x` has size `(nx, length(t), nu)`"""
45-
function Base.step(sys::AbstractStateSpace, t::AbstractVector; method=:cont, kwargs...)
44+
`y` has size `(ny, length(t), nu)`, `x` has size `(nx, length(t), nu)`
45+
"""
46+
function Base.step(sys::AbstractStateSpace, t::AbstractVector; method=:zoh, kwargs...)
4647
T = promote_type(eltype(sys.A), Float64)
4748
ny, nu = size(sys)
4849
nx = nstates(sys)
@@ -122,7 +123,7 @@ plot(result, plotu=true, plotx=false)
122123
```
123124
`y`, `x`, `u` have time in the second dimension. Initial state `x0` defaults to zero.
124125
125-
Continuous-time systems are simulated using an ODE solver if `u` is a function. If `u` is an array, the system is discretized (with `method=:zoh` by default) before simulation. For a lower-level inteface, see `?Simulator` and `?solve`
126+
Continuous-time systems are simulated using an ODE solver if `u` is a function (requires using ControlSystems). If `u` is an array, the system is discretized (with `method=:zoh` by default) before simulation. For a lower-level inteface, see `?Simulator` and `?solve`
126127
127128
`u` can be a function or a matrix/vector of precalculated control signals.
128129
If `u` is a function, then `u(x,i)` (`u(x,t)`) is called to calculate the control signal every iteration (time instance used by solver). This can be used to provide a control law such as state feedback `u(x,t) = -L*x` calculated by `lqr`.
@@ -132,6 +133,7 @@ For maximum performance, see function [`lsim!`](@ref), avaialable for discrete-t
132133
133134
Usage example:
134135
```julia
136+
using ControlSystems
135137
using LinearAlgebra # For identity matrix I
136138
using Plots
137139
@@ -253,7 +255,7 @@ function lsim(sys::AbstractStateSpace, u::Function, t::AbstractVector;
253255
end
254256
x,uout = ltitr(simsys.A, simsys.B, u, t, T.(x0))
255257
else
256-
error("Continuous-time simulation requires using ControlSystems")
258+
throw(MethodError(lsim, (sys, u, t)))
257259
end
258260
y = sys.C*x
259261
if !iszero(sys.D)

Diff for: lib/ControlSystemsBase/test/test_analysis.jl

+2-3
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,6 @@ sol_k = [0.0 3.0; 1.0 2.0]
139139
z, p, k = zpkdata(H)
140140

141141
@test_array_vecs_eps z sol_z 2*eps(Float64)
142-
@test_broken true == false # order of poles changed below, should probably be consistent
143142
#@test_array_vecs_eps p sol_p 2*eps(Float64)
144143
@test k == sol_k
145144
z, p, k = zpkdata(G)
@@ -183,11 +182,11 @@ sys = s*(s + 1)*(s^2 + 1)*(s - 3)/((s + 1)*(s + 4)*(s - 4))
183182

184183
@test sprint(dampreport, 1/(s+1+2im)/(s+2+3im)) == "| Pole | Damping | Frequency | Frequency | Time Constant |\n| | Ratio | (rad/sec) | (Hz) | (sec) |\n+--------------------+---------------+---------------+---------------+---------------+\n| -1 -2im | 0.447 | 2.24 | 0.356 | 1 |\n| -2 -3im | 0.555 | 3.61 | 0.574 | 0.5 |\n"
185184

186-
# Example 5.5 from http://www.control.lth.se/media/Education/EngineeringProgram/FRTN10/2017/e05_both.pdf
185+
# Example 5.5 from https://www.control.lth.se/fileadmin/control/Education/EngineeringProgram/FRTN10/2019/e05_both.pdf
187186
G = [1/(s+2) -1/(s+2); 1/(s+2) (s+1)/(s+2)]
188187
@test_broken length(poles(G)) == 1
189188
@test length(tzeros(G)) == 1
190-
@test_broken size(minreal(ss(G)).A) == (2,2)
189+
@test minreal(ss(G)).A [-2]
191190

192191

193192
## MARGIN ##

Diff for: lib/ControlSystemsBase/test/test_timeresp.jl

+11-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ L = lqr(sys,Q,R)
1313
u1(x,i) = -L*x # Form control law
1414
t=0:0.1:50
1515
x0 = [1.,0]
16-
@test_throws ErrorException lsim(sys,u1,t,x0=x0) # Continuous time simulation not loaded
16+
@test_throws MethodError lsim(sys,u1,t,x0=x0) # Continuous time simulation not loaded
1717

1818

1919
th = 1e-6
@@ -194,4 +194,14 @@ sysd = tf(1, [1, 1], 0.01)
194194
@test ControlSystemsBase._default_dt(sysint) == 0.05
195195
@test ControlSystemsBase._default_dt(sysd) == 0.01
196196

197+
198+
# Test error hints
199+
if VERSION >= v"1.7"
200+
# If ControlSystems is not loaded, the
201+
G = ssrand(1,1,1)
202+
@test_throws "install and load ControlSystems.jl" lsim(G, (u,t)->[1], 10)
203+
@test_throws "install and load ControlSystems.jl" lsim(G*delay(1), (u,t)->[1], 10)
204+
@test_throws "step with continuous-time systems" step(G*delay(1), 10)
205+
end
206+
197207
end

Diff for: test/runtests.jl

+3-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ dev_subpkg("ControlSystemsBase") # Always dev this package to test with the late
1919

2020
const GROUP = get(ENV, "GROUP", "All") # Get the GROUP attribute from the test.yml file, default to "All" for testing locally
2121

22+
@show GROUP
23+
2224
if GROUP ("ControlSystems", "All")
2325
include("runtests_controlsystems.jl")
2426
end
@@ -28,7 +30,7 @@ if GROUP == "All"
2830
subpkg_path = joinpath(dirname(@__DIR__), "lib", GROUP)
2931
Pkg.test(PackageSpec(name = GROUP, path = subpkg_path))
3032
end
31-
else
33+
elseif GROUP != "ControlSystems"
3234
# dev_subpkg(GROUP) # Do this if more sub packages are added, don't forget to avoid doing it if GROUP is CSBase
3335
subpkg_path = joinpath(dirname(@__DIR__), "lib", GROUP)
3436
Pkg.test(PackageSpec(name = GROUP, path = subpkg_path))

Diff for: test/test_timeresp.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ xreal[3,:,2] = exp.(-t).*t
9696
x0 = [0.,0]
9797
tspan = (0.0,tfinal)
9898
sol = solve(s, x0, tspan, OrdinaryDiffEq.Tsit5())
99-
@test step(P,t)[3] reduce(hcat,sol.(t))
99+
@test step(P,t)[3] reduce(hcat,sol.(t)) rtol=1e-4
100100
end
101101

102102
# test fwd euler

0 commit comments

Comments
 (0)