Skip to content

Commit 60de114

Browse files
committed
merging of ops
1 parent aa1a83e commit 60de114

File tree

1 file changed

+59
-40
lines changed

1 file changed

+59
-40
lines changed

src/named_systems2.jl

Lines changed: 59 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,40 @@ function set_operating_point!(P::NamedStateSpace, xu::NamedTuple{(:x,:u)})
161161
end
162162
has_operating_point(P::NamedStateSpace) = haskey(P.extra, :operating_point)
163163

164+
"""
165+
merge_ops(systems...)
166+
167+
Concatenate the operating points of the systems in `systems` into a single operating point. If any system has an operating point, the resulting system will have an operating point with the concatenated state and input vectors.
168+
"""
169+
function merge_ops(systems...)
170+
if any(has_operating_point, systems)
171+
opx = reduce(vcat, operating_point(sys).x for sys in systems)
172+
opu = reduce(vcat, operating_point(sys).u for sys in systems)
173+
op = (; x = opx, u = opu)
174+
extra = Dict(:operating_point => op)
175+
else
176+
extra = Dict{Symbol, Any}()
177+
end
178+
end
179+
180+
"""
181+
merge_ops_x(systems...)
182+
183+
Concatenate the operating points of the systems in `systems` into a single operating point, but only for the state vector `x`. The input vector `u` is taken from the first system's operating point, and all other systems are verified to have the same `u` operating point. If any system has an operating point, the resulting system will have an operating point with the concatenated state vector and the input vector from the first system.
184+
"""
185+
function merge_ops_x(systems...)
186+
if any(has_operating_point, systems)
187+
allequal(operating_point(sys).u for sys in systems) ||
188+
throw(ArgumentError("All systems must have the same input operating point u to be concatenated."))
189+
opx = reduce(vcat, operating_point(sys).x for sys in systems)
190+
opu = operating_point(systems[1]).u
191+
op = (; x = opx, u = opu)
192+
extra = Dict(:operating_point => op)
193+
else
194+
extra = Dict{Symbol, Any}()
195+
end
196+
end
197+
164198

165199
"""
166200
infer_operating_point(P1, P2, method = :obsv)
@@ -308,7 +342,7 @@ function Base.getindex(sys::NamedStateSpace{T,S}, i::NamedIndex, j::NamedIndex)
308342
sys.u[jj],
309343
sys.y[ii],
310344
sys.name,
311-
sys.extra,
345+
copy(sys.extra),
312346
) # |> sminreal
313347
if has_operating_point(sys)
314348
op = operating_point(sys)
@@ -329,7 +363,7 @@ function Base.getindex(sys::NamedStateSpace{T,S}, inds...) where {T,S}
329363
sys.u[cols],
330364
sys.y[rows],
331365
sys.name,
332-
sys.extra,
366+
copy(sys.extra),
333367
) # |> sminreal
334368
if has_operating_point(sys)
335369
op = operating_point(sys)
@@ -362,18 +396,19 @@ function Base.:-(s1::NamedStateSpace{T,S}) where {T <: CS.TimeEvolution, S}
362396
s1.u,
363397
s1.y,
364398
s1.name,
365-
s1.extra,
399+
copy(s1.extra),
366400
)
367401
end
368402

369403
function Base.:+(s1::NamedStateSpace{T,S}, s2::NamedStateSpace{T,S}) where {T <: CS.TimeEvolution, S}
404+
extra = merge_ops_x(s1, s2)
370405
return NamedStateSpace{T,S}(
371406
s1.sys+s2.sys,
372407
[s1.x; s2.x],
373408
s1.u,
374409
s1.y,
375410
"",
376-
s1.extra,
411+
extra,
377412
)
378413
end
379414

@@ -385,13 +420,14 @@ function Base.:*(s1::NamedStateSpace{T}, s2::NamedStateSpace{T}) where {T <: CS.
385420
end
386421
sys = s1.sys*s2.sys
387422
S = typeof(sys)
423+
extra = merge_ops_x(s1, s2)
388424
return NamedStateSpace{T,S}(
389425
sys,
390426
x_names,
391427
s2.u,
392428
s1.y,
393429
"",
394-
s1.extra,
430+
extra,
395431
)
396432
end
397433

@@ -402,7 +438,7 @@ function Base.:*(s1::Number, s2::NamedStateSpace{T, S}) where {T <: CS.TimeEvolu
402438
s2.u,
403439
[Symbol(string(y)*"_scaled") for y in s2.y],
404440
isempty(s2.name) ? "" : s2.name*"_scaled",
405-
s2.extra,
441+
copy(s2.extra),
406442
)
407443
end
408444

@@ -413,7 +449,7 @@ function Base.:*(s1::NamedStateSpace{T, S}, s2::Number) where {T <: CS.TimeEvolu
413449
[Symbol(string(u)*"_scaled") for u in s1.u],
414450
s1.y,
415451
isempty(s1.name) ? "" : s1.name*"_scaled",
416-
s1.extra,
452+
copy(s1.extra),
417453
)
418454
end
419455

@@ -425,7 +461,7 @@ function Base.:*(s1::AbstractMatrix, s2::NamedStateSpace{T, S}) where {T <: CS.T
425461
s2.u,
426462
[Symbol(string(y)*"_scaled") for y in s2.y],
427463
isempty(s2.name) ? "" : s2.name*"_scaled",
428-
s2.extra,
464+
copy(s2.extra),
429465
)
430466
else
431467
return *(promote(s1, s2)...)
@@ -440,7 +476,7 @@ function Base.:*(s1::NamedStateSpace{T, S}, s2::AbstractMatrix) where {T <: CS.T
440476
[Symbol(string(u)*"_scaled") for u in s1.u],
441477
s1.y,
442478
isempty(s1.name) ? "" : s1.name*"_scaled",
443-
s1.extra,
479+
copy(s1.extra),
444480
)
445481
else
446482
return *(promote(s1, s2)...)
@@ -465,14 +501,7 @@ function Base.hcat(systems::NamedStateSpace{T,S}...) where {T,S}
465501
x = generate_unique_x_names(systems...)
466502
u = reduce(vcat, getproperty.(systems, :u))
467503
check_unique(u, "u")
468-
if any(has_operating_point, systems)
469-
opx = reduce(vcat, operating_point(sys).x for sys in systems)
470-
opu = reduce(vcat, operating_point(sys).u for sys in systems)
471-
op = (; x = opx, u = opu)
472-
extra = Dict(:operating_point => op)
473-
else
474-
extra = Dict{Symbol, Any}()
475-
end
504+
extra = merge_ops(systems...)
476505
return NamedStateSpace{T,S}(
477506
hcat(getproperty.(systems, :sys)...),
478507
x,
@@ -487,16 +516,7 @@ function Base.vcat(systems::NamedStateSpace{T,S}...) where {T,S}
487516
x = generate_unique_x_names(systems...)
488517
y = reduce(vcat, getproperty.(systems, :y))
489518
check_unique(y, "y")
490-
if any(has_operating_point, systems)
491-
allequal(operating_point(sys).u for sys in systems) ||
492-
throw(ArgumentError("All systems must have the same input operating point u to be concatenated."))
493-
opx = reduce(vcat, operating_point(sys).x for sys in systems)
494-
opu = operating_point(systems[1]).u
495-
op = (; x = opx, u = opu)
496-
extra = Dict(:operating_point => op)
497-
else
498-
extra = Dict{Symbol, Any}()
499-
end
519+
extra = merge_ops_x(systems...)
500520
return NamedStateSpace{T,S}(
501521
vcat(getproperty.(systems, :sys)...),
502522
x,
@@ -523,7 +543,7 @@ function measure(s::NamedStateSpace, names)
523543
C[i, inds[i]] = 1
524544
end
525545
s2 = ss(A,B,C,0, s.timeevol)
526-
sminreal(named_ss(s2; s.x, s.u, y=names, name=s.name, s.extra))
546+
sminreal(named_ss(s2; s.x, s.u, y=names, name=s.name, extra=copy(s.extra)))
527547
end
528548

529549
"""
@@ -831,7 +851,7 @@ function ControlSystemsBase.sminreal(s::NamedStateSpace)
831851
_, _, _, inds = CS.struct_ctrb_obsv(s.sys) # we do this one more time to get the inds. This implies repeated calculations, but will allow inner systems of exotic types that have a special method for sminreal to keep their type.
832852
op = operating_point(s)
833853
op = (x = op.x[inds], u = op.u)
834-
newsys = named_ss(sys; x=s.x[inds], s.u, s.y, s.name, s.extra)
854+
newsys = named_ss(sys; x=s.x[inds], s.u, s.y, s.name, extra=copy(s.extra))
835855
set_operating_point!(newsys, op)
836856
return newsys
837857
end
@@ -973,7 +993,7 @@ end
973993

974994
function CS.c2d(s::NamedStateSpace{Continuous}, Ts::Real, method::Symbol = :zoh, args...;
975995
kwargs...)
976-
named_ss(c2d(s.sys, Ts, method, args...; kwargs...); s.x, s.u, s.y, s.name, s.extra)
996+
named_ss(c2d(s.sys, Ts, method, args...; kwargs...); s.x, s.u, s.y, s.name, copy(s.extra))
977997
end
978998

979999

@@ -991,11 +1011,8 @@ function CS.append(systems::NamedStateSpace...; kwargs...)
9911011
y = reduce(vcat, getproperty.(systems, :y))
9921012
u = reduce(vcat, getproperty.(systems, :u))
9931013

994-
newsys = named_ss(systype(A, B, C, D, timeevol); x, y, u, kwargs...)
995-
if any(has_operating_point, systems)
996-
op = (; x = reduce(vcat, operating_point(s).x for s in systems), u = reduce(vcat, operating_point(s).u for s in systems))
997-
set_operating_point!(newsys, op)
998-
end
1014+
extra = merge_ops(systems...)
1015+
newsys = named_ss(systype(A, B, C, D, timeevol); x, y, u, extra, kwargs...)
9991016
return newsys
10001017
end
10011018

@@ -1017,7 +1034,7 @@ function CS.add_output(sys::NamedStateSpace, C2::AbstractArray, D2=0; y = [Symbo
10171034
x = sys.x
10181035
u = sys.u
10191036
y = [sys.y; y]
1020-
named_ss(ss(A, B, [C; C2], [D; D3]), sys.timeevol; x, u, y, sys.extra)
1037+
named_ss(ss(A, B, [C; C2], [D; D3]), sys.timeevol; x, u, y, copy(sys.extra))
10211038
end
10221039

10231040

@@ -1042,9 +1059,11 @@ end
10421059

10431060
function CS.balance_statespace(sys::NamedStateSpace, args...; kwargs...)
10441061
msys, T, rest... = CS.balance_statespace(sys.sys, args...; kwargs...)
1045-
op = operating_point(sys)
1046-
op = (x = T*op.x, u = op.u)
1047-
newsys = named_ss(msys; sys.u, sys.y, sys.name)
1048-
set_operating_point!(newsys, op)
1062+
newsys = named_ss(msys; sys.u, sys.y, sys.name, extra=copy(sys.extra))
1063+
if has_operating_point(sys)
1064+
op = operating_point(sys)
1065+
op = (x = T*op.x, u = op.u)
1066+
set_operating_point!(newsys, op)
1067+
end
10491068
newsys, T, rest...
10501069
end

0 commit comments

Comments
 (0)