Skip to content

Commit add7d76

Browse files
committed
add concatenation for SimResult
1 parent b39d0e8 commit add7d76

File tree

3 files changed

+44
-4
lines changed

3 files changed

+44
-4
lines changed

lib/ControlSystemsBase/src/timeresp.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ Continuous-time systems are simulated using an ODE solver if `u` is a function (
144144
If `u` is a function, then `u(x,i)` (for discrete systems) or `u(x,t)` (for continuous ones) is called to calculate the control signal at 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`.
145145
To simulate a unit step at `t=t₀`, use `(x,t)-> t ≥ t₀`, for a ramp, use `(x,t)-> t`, for a step at `t=5`, use `(x,t)-> (t >= 5)` etc.
146146
147-
*Note:* The function `u` will be called once before simulating to verify that it returns an array of the correct dimensions. This can cause problems if `u` is stateful. You can disable this check by passing `check_u = false`.
147+
*Note:* The function `u` will be called once before simulating to verify that it returns an array of the correct dimensions. This can cause problems if `u` is stateful or has other side effects. You can disable this check by passing `check_u = false`.
148148
149149
For maximum performance, see function [`lsim!`](@ref), available for discrete-time systems only.
150150
@@ -206,7 +206,7 @@ function lsim(sys::AbstractStateSpace, u::AbstractVecOrMat, t::AbstractVector;
206206
error("Unsupported discretization method: $method")
207207
end
208208
else
209-
if sys.Ts != dt
209+
if !(sys.Ts dt)
210210
error("Time vector must match the sample time of the discrete-time system, $(sys.Ts): got $dt")
211211
end
212212
dsys = sys
@@ -284,8 +284,8 @@ function lsim(sys::AbstractStateSpace, u::Function, t::AbstractVector;
284284
if iscontinuous(sys)
285285
simsys = c2d(sys, dt, :zoh)
286286
else
287-
if sys.Ts != dt
288-
error("Time vector must match sample time for discrete system")
287+
if !(sys.Ts dt)
288+
error("Time vector interval ($dt) must match sample time for discrete system ($(sys.Ts))")
289289
end
290290
simsys = sys
291291
end

lib/ControlSystemsBase/src/types/result_types.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ y, t, x, u = result
2424
- `x::Tx`
2525
- `u::Tu`
2626
- `sys::Ts`
27+
28+
## Concatenation of SimResults
29+
30+
Two SimResults can be concatenated in time using `hcat`, or `[res1 res2]`, the rules for this are as follows:
31+
- If the start time of the second result is one sample interval after the end time of the first result, the results are concatenated and the length of the result is the sum of the lengths of the two results.
32+
- If the start time of the second result is equal to the end time of the first result, _and_ the initial state of the second result is equal to the final state of the first result, the results are concatenated omitting the initial point from the second result, which would otherwise have been repeated. The length of the result is the sum of the lengths of the two results minus one.
33+
If none of the above holds, a warning is issued and the result has the length of the sum of the lengths of the two results.
34+
- If the sample intervals of the two results are different, an error is thrown.
2735
"""
2836
struct SimResult{Ty, Tt, Tx, Tu, Ts} <: AbstractSimResult # Result of lsim
2937
y::Ty
@@ -42,6 +50,18 @@ function Base.getindex(r::SimResult, v::AbstractVector)
4250
return getfield.((r,), v)
4351
end
4452

53+
function Base.hcat(r1::SimResult, r2::SimResult)
54+
r1.sys.Ts == r2.sys.Ts || throw(ArgumentError("Sampling-time mismatch"))
55+
if r1.x[:, end] == r2.x[:, 1] && r1.t[end] == r2.t[1]
56+
r1.u[:, end] == r2.u[:, 1] || @warn "Concatenated SimResults have different inputs at the join"
57+
# This is a common case when r2 starts with initial conditions from r1
58+
return SimResult(hcat(r1.y, r2.y[:, 2:end]), vcat(r1.t, r2.t[2:end]), hcat(r1.x, r2.x[:, 2:end]), hcat(r1.u, r2.u[:, 2:end]), r1.sys)
59+
elseif !(r1.t[end] + r1.sys.Ts r2.t[1])
60+
@warn "Concatenated SimResults do not appear to be continuous in time, the first ends at t=$(r1.t[end]) and the second starts at t=$(r2.t[1]). With sample interval Ts=$(r1.sys.Ts), the second simulation was expected to start at t=$(r1.t[end] + r1.sys.Ts) To start a simulation at a non-zero time, pass a time vector to lsim."
61+
end
62+
SimResult(hcat(r1.y, r2.y), vcat(r1.t, r2.t), hcat(r1.x, r2.x), hcat(r1.u, r2.u), r1.sys)
63+
end
64+
4565
issiso(r::SimResult) = issiso(r.sys)
4666

4767
# to allow destructuring, e.g., y,t,x = lsim(sys, u)

lib/ControlSystemsBase/test/test_timeresp.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,4 +255,24 @@ si = stepinfo(res)
255255
@test si.undershoot 27.98 rtol=0.01
256256
plot(si)
257257

258+
# Test concatenation of SimResults
259+
u = ones(1, 100)
260+
sysd = c2d(sys,0.1)
261+
res1 = lsim(sysd,u)
262+
res2 = lsim(sysd,u; x0 = res1.x[:, end])
263+
@test_logs (:warn, r"Concatenated SimResults do not appear to be continuous in time") [res1 res2]
264+
265+
res2 = lsim(sysd,u,res1.t[end]:0.1:res1.t[end]+9.9; x0 = res1.x[:, end])
266+
res12 = [res1 res2]
267+
@test length(res12.t) == length(res1.t) + length(res2.t) - 1 # -1 since we do not include the initial time point from the second result which overlaps with the first
268+
269+
res2 = lsim(sysd,u)
270+
@test_logs (:warn, r"Concatenated SimResults do not appear to be continuous in time") [res1 res2]
271+
res12 = [res1 res2]
272+
@test length(res12.t) == length(res1.t) + length(res2.t) # not -1 since we do include the initial time point from the second result if they do not appear to be continuous in time
273+
274+
res2 = lsim(sysd,u, res1.t[end]+0.1:0.1:res1.t[end]+10)
275+
@test_nowarn res12 = [res1 res2]
276+
@test length(res12.t) == length(res1.t) + length(res2.t) # not -1 since we do do include the initial time point from the second result if they do not appear to be continuous in time
277+
258278
end

0 commit comments

Comments
 (0)