@@ -152,7 +152,42 @@ Return a named tuple `(; x, u)` containing the operating point of the system `P`
152
152
"""
153
153
operating_point (P:: NamedStateSpace ) = get_extra (P, :operating_point , (x= zeros (P. nx), u= zeros (P. nu)))
154
154
operating_point (P:: AbstractStateSpace ) = (x= zeros (P. nx), u= zeros (P. nu))
155
- set_operating_point! (P:: NamedStateSpace , xu:: NamedTuple{(:x,:u)} ) = set_extra! (P, :operating_point , xu)
155
+ function set_operating_point! (P:: NamedStateSpace , xu:: NamedTuple{(:x,:u)} )
156
+ length (xu. x) == P. nx ||
157
+ throw (ArgumentError (" Operating point x must have length $(P. nx) , got length $(length (xu. x)) " ))
158
+ length (xu. u) == P. nu ||
159
+ throw (ArgumentError (" Operating point u must have length $(P. nu) , got length $(length (xu. u)) " ))
160
+ set_extra! (P, :operating_point , xu)
161
+ end
162
+ has_operating_point (P:: NamedStateSpace ) = haskey (P. extra, :operating_point )
163
+
164
+
165
+ """
166
+ infer_operating_point(P1, P2, method = :obsv)
167
+
168
+ Return the operating point of `P1` inferred from the operating point of `P2` and a similarity transform between the two systems. The method for finding the similarity transform can be specified, default is `:obsv`.
169
+
170
+ ```
171
+ s1 = named_ss(ssrand(1,1,2), "P", x = :x, u = :u1, y=:y1)
172
+ opx = randn(s1.nx)
173
+ opu = randn(s1.nu)
174
+ RobustAndOptimalControl.set_operating_point!(s1, (x = opx, u = opu))
175
+
176
+ s2, _ = balance_statespace(s1) # s1 and s2 are similar systems
177
+ op2 = RobustAndOptimalControl.infer_operating_point(s2, s1)
178
+ @test RobustAndOptimalControl.operating_point(s2) == op2
179
+ ```
180
+ """
181
+ function infer_operating_point (P1, P2, method = :obsv )
182
+ T = ControlSystemsBase. find_similarity_transform (P1, P2, method)
183
+ op2 = operating_point (P2)
184
+ op1 = (x = T* op2. x, u = op2. u)
185
+ end
186
+
187
+ function infer_operating_point! (P1, P2, method = :obsv )
188
+ op = infer_operating_point (P1, P2, method)
189
+ set_operating_point! (P1, op)
190
+ end
156
191
157
192
const NamedIndex = Union{Symbol, Vector{Symbol}, Colon}
158
193
@@ -265,29 +300,43 @@ iterable(v) = v
265
300
function Base. getindex (sys:: NamedStateSpace{T,S} , i:: NamedIndex , j:: NamedIndex ) where {T,S}
266
301
i,j = iterable .((i, j))
267
302
ii = i isa Colon ? i : names2indices (i, sys. y)
268
- jj = j isa Colon ? j : names2indices (j, sys. u)
303
+ jj = j isa Colon ? j : names2indices (j, sys. u)
269
304
270
- return NamedStateSpace {T,S} (
305
+ newsys = NamedStateSpace {T,S} (
271
306
sys. sys[ii, jj],
272
307
sys. x,
273
308
sys. u[jj],
274
309
sys. y[ii],
275
310
sys. name,
311
+ sys. extra,
276
312
) # |> sminreal
313
+ if has_operating_point (sys)
314
+ op = operating_point (sys)
315
+ op = (; op. x, u= op. u[jj])
316
+ set_operating_point! (newsys, op)
317
+ end
318
+ return newsys
277
319
end
278
320
279
321
function Base. getindex (sys:: NamedStateSpace{T,S} , inds... ) where {T,S}
280
322
if size (inds, 1 ) != 2
281
323
error (" Must specify 2 indices to index statespace model" )
282
324
end
283
325
rows, cols = CS. index2range (inds... )
284
- return NamedStateSpace {T,S} (
326
+ newsys = NamedStateSpace {T,S} (
285
327
sys. sys[rows, cols],
286
328
sys. x,
287
329
sys. u[cols],
288
330
sys. y[rows],
289
331
sys. name,
332
+ sys. extra,
290
333
) # |> sminreal
334
+ if has_operating_point (sys)
335
+ op = operating_point (sys)
336
+ op = (; op. x, u= op. u[cols])
337
+ set_operating_point! (newsys, op)
338
+ end
339
+ return newsys
291
340
end
292
341
293
342
function Base. show (io:: IO , G:: NamedStateSpace )
@@ -300,6 +349,10 @@ function Base.show(io::IO, G::NamedStateSpace)
300
349
length (G. x) < 50 && (print (io, " \n With state names: " ); println (io, join (G. x, ' ' )))
301
350
length (G. u) < 50 && (print (io, " input names: " ); println (io, join (G. u, ' ' )))
302
351
length (G. y) < 50 && (print (io, " output names: " ); println (io, join (G. y, ' ' )))
352
+ if has_operating_point (G)
353
+ op = operating_point (G)
354
+ println (io, " Operating point: x = " , op. x, " , u = " , op. u)
355
+ end
303
356
end
304
357
305
358
function Base.:- (s1:: NamedStateSpace{T,S} ) where {T <: CS.TimeEvolution , S}
@@ -309,6 +362,7 @@ function Base.:-(s1::NamedStateSpace{T,S}) where {T <: CS.TimeEvolution, S}
309
362
s1. u,
310
363
s1. y,
311
364
s1. name,
365
+ s1. extra,
312
366
)
313
367
end
314
368
@@ -319,6 +373,7 @@ function Base.:+(s1::NamedStateSpace{T,S}, s2::NamedStateSpace{T,S}) where {T <:
319
373
s1. u,
320
374
s1. y,
321
375
" " ,
376
+ s1. extra,
322
377
)
323
378
end
324
379
@@ -336,6 +391,7 @@ function Base.:*(s1::NamedStateSpace{T}, s2::NamedStateSpace{T}) where {T <: CS.
336
391
s2. u,
337
392
s1. y,
338
393
" " ,
394
+ s1. extra,
339
395
)
340
396
end
341
397
@@ -346,6 +402,7 @@ function Base.:*(s1::Number, s2::NamedStateSpace{T, S}) where {T <: CS.TimeEvolu
346
402
s2. u,
347
403
[Symbol (string (y)* " _scaled" ) for y in s2. y],
348
404
isempty (s2. name) ? " " : s2. name* " _scaled" ,
405
+ s2. extra,
349
406
)
350
407
end
351
408
@@ -356,6 +413,7 @@ function Base.:*(s1::NamedStateSpace{T, S}, s2::Number) where {T <: CS.TimeEvolu
356
413
[Symbol (string (u)* " _scaled" ) for u in s1. u],
357
414
s1. y,
358
415
isempty (s1. name) ? " " : s1. name* " _scaled" ,
416
+ s1. extra,
359
417
)
360
418
end
361
419
@@ -367,6 +425,7 @@ function Base.:*(s1::AbstractMatrix, s2::NamedStateSpace{T, S}) where {T <: CS.T
367
425
s2. u,
368
426
[Symbol (string (y)* " _scaled" ) for y in s2. y],
369
427
isempty (s2. name) ? " " : s2. name* " _scaled" ,
428
+ s2. extra,
370
429
)
371
430
else
372
431
return * (promote (s1, s2)... )
@@ -381,6 +440,7 @@ function Base.:*(s1::NamedStateSpace{T, S}, s2::AbstractMatrix) where {T <: CS.T
381
440
[Symbol (string (u)* " _scaled" ) for u in s1. u],
382
441
s1. y,
383
442
isempty (s1. name) ? " " : s1. name* " _scaled" ,
443
+ s1. extra,
384
444
)
385
445
else
386
446
return * (promote (s1, s2)... )
@@ -405,25 +465,45 @@ function Base.hcat(systems::NamedStateSpace{T,S}...) where {T,S}
405
465
x = generate_unique_x_names (systems... )
406
466
u = reduce (vcat, getproperty .(systems, :u ))
407
467
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
408
476
return NamedStateSpace {T,S} (
409
477
hcat (getproperty .(systems, :sys )... ),
410
478
x,
411
479
u,
412
480
systems[1 ]. y,
413
481
" " ,
482
+ extra
414
483
)
415
484
end
416
485
417
486
function Base. vcat (systems:: NamedStateSpace{T,S} ...) where {T,S}
418
487
x = generate_unique_x_names (systems... )
419
488
y = reduce (vcat, getproperty .(systems, :y ))
420
489
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
421
500
return NamedStateSpace {T,S} (
422
501
vcat (getproperty .(systems, :sys )... ),
423
502
x,
424
503
systems[1 ]. u,
425
504
y,
426
505
" " ,
506
+ extra,
427
507
)
428
508
end
429
509
@@ -443,7 +523,7 @@ function measure(s::NamedStateSpace, names)
443
523
C[i, inds[i]] = 1
444
524
end
445
525
s2 = ss (A,B,C,0 , s. timeevol)
446
- sminreal (named_ss (s2; s. x, s. u, y= names, name= s. name))
526
+ sminreal (named_ss (s2; s. x, s. u, y= names, name= s. name, s . extra ))
447
527
end
448
528
449
529
"""
@@ -541,7 +621,7 @@ function ControlSystemsBase.feedback(s1::NamedStateSpace{T}, s2::NamedStateSpace
541
621
@assert sys. nu == length (W1) + length (W2)
542
622
@assert sys. ny == length (Z1) + length (Z2)
543
623
@assert sys. nx == length (x1)
544
- nsys = NamedStateSpace {T,typeof(sys)} (sys, x1, [s1. u[W1]; s2. u[W2]], [s1. y[Z1]; s2. y[Z2]], " " )
624
+ nsys = NamedStateSpace {T,typeof(sys)} (sys, x1, [s1. u[W1]; s2. u[W2]], [s1. y[Z1]; s2. y[Z2]], " " , Dict {Symbol, Any} () )
545
625
# sminreal(nsys)
546
626
end
547
627
@@ -838,7 +918,7 @@ function named_ss(sys::ExtendedStateSpace{T}, name="";
838
918
throw (ArgumentError (" Length of performance names must match sys.nz ($(sys. nz) )" ))
839
919
840
920
sys2 = ss (sys)
841
- NamedStateSpace {T, typeof(sys2)} (sys2, x, [w; u], [z; y], name)
921
+ NamedStateSpace {T, typeof(sys2)} (sys2, x, [w; u], [z; y], name, Dict {Symbol, Any} () )
842
922
end
843
923
844
924
893
973
894
974
function CS. c2d (s:: NamedStateSpace{Continuous} , Ts:: Real , method:: Symbol = :zoh , args... ;
895
975
kwargs... )
896
- named_ss (c2d (s. sys, Ts, method, args... ; kwargs... ); s. x, s. u, s. y, s. name)
976
+ named_ss (c2d (s. sys, Ts, method, args... ; kwargs... ); s. x, s. u, s. y, s. name, s . extra )
897
977
end
898
978
899
979
@@ -910,8 +990,13 @@ function CS.append(systems::NamedStateSpace...; kwargs...)
910
990
x = reduce (vcat, getproperty .(systems, :x ))
911
991
y = reduce (vcat, getproperty .(systems, :y ))
912
992
u = reduce (vcat, getproperty .(systems, :u ))
913
-
914
- return named_ss (systype (A, B, C, D, timeevol); x, y, u, kwargs... )
993
+
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
999
+ return newsys
915
1000
end
916
1001
917
1002
@@ -941,7 +1026,7 @@ function CS.minreal(sys::NamedStateSpace, args...; kwargs...)
941
1026
named_ss (msys; sys. u, sys. y, sys. name)
942
1027
end
943
1028
944
- for fun in [:baltrunc , :balreal , :balance_statespace ]
1029
+ for fun in [:baltrunc , :balreal ]
945
1030
@eval function CS. $ (fun)(sys:: NamedStateSpace , args... ; kwargs... )
946
1031
msys, rest... = CS.$ (fun)(sys. sys, args... ; kwargs... )
947
1032
named_ss (msys; sys. u, sys. y, sys. name), rest...
0 commit comments