Skip to content

Commit 7e116f0

Browse files
committed
clean up siso processing, remove internal property calls
1 parent bab117d commit 7e116f0

3 files changed

Lines changed: 51 additions & 55 deletions

File tree

control/iosys.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -908,7 +908,8 @@ def __call__(sys, u, params=None, squeeze=None):
908908

909909
# Evaluate the function on the argument
910910
out = sys._out(0, np.array((0,)), np.asarray(u))
911-
_, out = _process_time_response(sys, None, out, None, squeeze=squeeze)
911+
_, out = _process_time_response(
912+
None, out, issiso=sys.issiso(), squeeze=squeeze)
912913
return out
913914

914915
def _update_params(self, params, warning=False):
@@ -1572,7 +1573,7 @@ def input_output_response(
15721573
u = U[i] if len(U.shape) == 1 else U[:, i]
15731574
y[:, i] = sys._out(T[i], [], u)
15741575
return TimeResponseData(
1575-
T, y, None, None, sys=sys,
1576+
T, y, None, None, issiso=sys.issiso(),
15761577
transpose=transpose, return_x=return_x, squeeze=squeeze)
15771578

15781579
# create X0 if not given, test if X0 has correct shape
@@ -1668,7 +1669,7 @@ def ivp_rhs(t, x):
16681669
raise TypeError("Can't determine system type")
16691670

16701671
return TimeResponseData(
1671-
soln.t, y, soln.y, U, sys=sys,
1672+
soln.t, y, soln.y, U, issiso=sys.issiso(),
16721673
transpose=transpose, return_x=return_x, squeeze=squeeze)
16731674

16741675

control/optimal.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -828,7 +828,7 @@ def __init__(
828828

829829
# Process data as a time response (with "outputs" = inputs)
830830
response = TimeResponseData(
831-
ocp.timepts, inputs, states, sys=ocp.system,
831+
ocp.timepts, inputs, states, issiso=ocp.system.issiso(),
832832
transpose=transpose, return_x=return_states, squeeze=squeeze)
833833

834834
self.time = response.time

control/timeresp.py

Lines changed: 46 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,10 @@ class TimeResponseData:
157157
compatibility with MATLAB and :func:`scipy.signal.lsim`). Default
158158
value is False.
159159
160-
sys : InputOutputSystem or LTI, optional
161-
If present, stores the system used to generate the response.
160+
issiso : bool, optional
161+
Set to ``True`` if the system generating the data is single-input,
162+
single-output. If passed as ``None`` (default), the input data
163+
will be used to set the value.
162164
163165
ninputs, noutputs, nstates : int
164166
Number of inputs, outputs, and states of the underlying system.
@@ -198,7 +200,7 @@ class TimeResponseData:
198200
"""
199201

200202
def __init__(
201-
self, time, outputs, states=None, inputs=None, sys=None, dt=None,
203+
self, time, outputs, states=None, inputs=None, issiso=None,
202204
transpose=False, return_x=False, squeeze=None,
203205
multi_trace=False, input_index=None, output_index=None
204206
):
@@ -369,15 +371,22 @@ def __init__(
369371
if self.t.shape[-1] != self.u.shape[-1]:
370372
raise ValueError("Input vector does not match time vector")
371373

372-
# If the system was specified, make sure it is compatible
373-
if sys is not None:
374-
if sys.noutputs != self.noutputs:
375-
ValueError("System outputs do not match response data")
376-
if self.x is not None and sys.nstates != self.nstates:
377-
ValueError("System states do not match response data")
378-
if self.u is not None and sys.ninputs != self.ninputs:
379-
ValueError("System inputs do not match response data")
380-
self.sys = sys
374+
# Figure out if the system is SISO
375+
if issiso is None:
376+
# Figure out based on the data
377+
if self.ninputs == 1:
378+
issiso = self.noutputs == 1
379+
elif self.niinputs > 1:
380+
issiso = False
381+
else:
382+
# Missing input data => can't resolve
383+
raise ValueError("Can't determine if system is SISO")
384+
elif issiso is True and (self.ninputs > 1 or self.noutputs > 1):
385+
raise ValueError("Keyword `issiso` does not match data")
386+
387+
# Set the value to be used for future processing
388+
self.issiso = issiso or \
389+
(input_index is not None and output_index is not None)
381390

382391
# Keep track of whether to squeeze inputs, outputs, and states
383392
if not (squeeze is True or squeeze is None or squeeze is False):
@@ -397,9 +406,8 @@ def time(self):
397406
@property
398407
def outputs(self):
399408
t, y = _process_time_response(
400-
self.sys, self.t, self.y,
401-
transpose=self.transpose, squeeze=self.squeeze,
402-
input=self.input_index, output=self.output_index)
409+
self.t, self.y, issiso=self.issiso,
410+
transpose=self.transpose, squeeze=self.squeeze)
403411
return y
404412

405413
# Getter for state (implements non-standard squeeze processing)
@@ -430,9 +438,8 @@ def inputs(self):
430438
return None
431439

432440
t, u = _process_time_response(
433-
self.sys, self.t, self.u,
434-
transpose=self.transpose, squeeze=self.squeeze,
435-
input=self.input_index, output=self.output_index)
441+
self.t, self.u, issiso=self.issiso,
442+
transpose=self.transpose, squeeze=self.squeeze)
436443
return u
437444

438445
# Implement iter to allow assigning to a tuple
@@ -571,7 +578,7 @@ def shape_matches(s_legal, s_actual):
571578

572579

573580
# Forced response of a linear system
574-
def forced_response(sys, T=None, U=0., X0=0., transpose=False,
581+
def forced_response(sys, T=None, U=0., X0=0., issiso=False, transpose=False,
575582
interpolate=False, return_x=None, squeeze=None):
576583
"""Simulate the output of a linear system.
577584
@@ -860,24 +867,20 @@ def forced_response(sys, T=None, U=0., X0=0., transpose=False,
860867
yout = np.transpose(yout)
861868

862869
return TimeResponseData(
863-
tout, yout, xout, U, sys=sys,
870+
tout, yout, xout, U, issiso=sys.issiso(),
864871
transpose=transpose, return_x=return_x, squeeze=squeeze)
865872

866873

867874
# Process time responses in a uniform way
868875
def _process_time_response(
869-
sys, tout, yout, transpose=None,
870-
squeeze=None, input=None, output=None):
876+
tout, yout, issiso=False, transpose=None, squeeze=None):
871877
"""Process time response signals.
872878
873-
This function processes the outputs of the time response functions and
874-
processes the transpose and squeeze keywords.
879+
This function processes the outputs (or inputs) of time response
880+
functions and processes the transpose and squeeze keywords.
875881
876882
Parameters
877883
----------
878-
sys : LTI or InputOutputSystem
879-
System that generated the data (used to check if SISO/MIMO).
880-
881884
T : 1D array
882885
Time values of the output. Ignored if None.
883886
@@ -887,6 +890,10 @@ def _process_time_response(
887890
systems with no input indexing, such as initial_response or forced
888891
response) or a 3D array indexed by output, input, and time.
889892
893+
issiso : bool, optional
894+
If ``True``, process data as single-input, single-output data.
895+
Default is ``False``.
896+
890897
transpose : bool, optional
891898
If True, transpose all input and output arrays (for backward
892899
compatibility with MATLAB and :func:`scipy.signal.lsim`). Default
@@ -901,12 +908,6 @@ def _process_time_response(
901908
the system is SISO. The default value can be set using
902909
config.defaults['control.squeeze_time_response'].
903910
904-
input : int, optional
905-
If present, the response represents only the listed input.
906-
907-
output : int, optional
908-
If present, the response represents only the listed output.
909-
910911
Returns
911912
-------
912913
T : 1D array
@@ -923,9 +924,6 @@ def _process_time_response(
923924
if squeeze is None:
924925
squeeze = config.defaults['control.squeeze_time_response']
925926

926-
# Determine if the system is SISO
927-
issiso = sys.issiso() or (input is not None and output is not None)
928-
929927
# Figure out whether and how to squeeze output data
930928
if squeeze is True: # squeeze all dimensions
931929
yout = np.squeeze(yout)
@@ -1116,16 +1114,15 @@ def step_response(sys, T=None, X0=0., input=None, output=None, T_num=None,
11161114
# Create a set of single inputs system for simulation
11171115
squeeze, simo = _get_ss_simo(sys, i, output, squeeze=squeeze)
11181116

1119-
out = forced_response(simo, T, U, X0, transpose=False,
1120-
return_x=return_x, squeeze=True)
1117+
response = forced_response(simo, T, U, X0, squeeze=True)
11211118
inpidx = i if input is None else 0
1122-
yout[:, inpidx, :] = out[1]
1123-
xout[:, inpidx, :] = out[2]
1119+
yout[:, inpidx, :] = response.y
1120+
xout[:, inpidx, :] = response.x
11241121
uout[:, inpidx, :] = U
11251122

11261123
return TimeResponseData(
1127-
out[0], yout, xout, uout, sys=sys, transpose=transpose,
1128-
return_x=return_x, squeeze=squeeze,
1124+
response.time, yout, xout, uout, issiso=sys.issiso(),
1125+
transpose=transpose, return_x=return_x, squeeze=squeeze,
11291126
input_index=input, output_index=output)
11301127

11311128

@@ -1441,12 +1438,11 @@ def initial_response(sys, T=None, X0=0., input=0, output=None, T_num=None,
14411438
T = _default_time_vector(sys, N=T_num, tfinal=T, is_step=False)
14421439

14431440
# Compute the forced response
1444-
res = forced_response(sys, T, 0, X0, transpose=transpose,
1445-
return_x=return_x, squeeze=squeeze)
1441+
response = forced_response(sys, T, 0, X0)
14461442

14471443
# Store the response without an input
14481444
return TimeResponseData(
1449-
res.t, res.y, res.x, None, sys=sys,
1445+
response.t, response.y, response.x, None, issiso=sys.issiso(),
14501446
transpose=transpose, return_x=return_x, squeeze=squeeze)
14511447

14521448

@@ -1593,17 +1589,16 @@ def impulse_response(sys, T=None, X0=0., input=None, output=None, T_num=None,
15931589
U[0] = 1./simo.dt # unit area impulse
15941590

15951591
# Simulate the impulse response fo this input
1596-
out = forced_response(simo, T, U, new_X0, transpose=False,
1597-
return_x=True, squeeze=squeeze)
1592+
response = forced_response(simo, T, U, new_X0)
15981593

15991594
# Store the output (and states)
16001595
inpidx = i if input is None else 0
1601-
yout[:, inpidx, :] = out[1]
1602-
xout[:, inpidx, :] = out[2]
1596+
yout[:, inpidx, :] = response.y
1597+
xout[:, inpidx, :] = response.x
16031598

16041599
return TimeResponseData(
1605-
out[0], yout, xout, uout, sys=sys, transpose=transpose,
1606-
return_x=return_x, squeeze=squeeze,
1600+
response.time, yout, xout, uout, issiso=sys.issiso(),
1601+
transpose=transpose, return_x=return_x, squeeze=squeeze,
16071602
input_index=input, output_index=output)
16081603

16091604

0 commit comments

Comments
 (0)