Skip to content

Commit bb12598

Browse files
committed
Update timeresp, iosys to return InputOutputResponse, with properties
1 parent ab59657 commit bb12598

2 files changed

Lines changed: 77 additions & 13 deletions

File tree

control/iosys.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@
3232
from warnings import warn
3333

3434
from .statesp import StateSpace, tf2ss, _convert_to_statespace
35-
from .timeresp import _check_convert_array, _process_time_response
35+
from .timeresp import _check_convert_array, _process_time_response, \
36+
InputOutputResponse
3637
from .lti import isctime, isdtime, common_timebase
3738
from . import config
3839

@@ -1666,8 +1667,9 @@ def ivp_rhs(t, x):
16661667
else: # Neither ctime or dtime??
16671668
raise TypeError("Can't determine system type")
16681669

1669-
return _process_time_response(sys, soln.t, y, soln.y, transpose=transpose,
1670-
return_x=return_x, squeeze=squeeze)
1670+
return InputOutputResponse(
1671+
soln.t, y, soln.y, U, sys=sys,
1672+
transpose=transpose, return_x=return_x, squeeze=squeeze)
16711673

16721674

16731675
def find_eqpt(sys, x0, u0=[], y0=None, t=0, params={},

control/timeresp.py

Lines changed: 72 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -137,13 +137,62 @@ class InputOutputResponse:
137137

138138
def __init__(
139139
self, t, y, x, u, sys=None, dt=None,
140-
return_x=False, squeeze=None # for legacy interface
140+
transpose=False, return_x=False, squeeze=None
141141
):
142-
# Store response attributes
143-
self.t, self.y, self.x = t, y, x
142+
#
143+
# Process and store the basic input/output elements
144+
#
145+
t, y, x = _process_time_response(
146+
sys, t, y, x,
147+
transpose=transpose, return_x=True, squeeze=squeeze)
148+
149+
# Time vector
150+
self.t = np.atleast_1d(t)
151+
if len(self.t.shape) != 1:
152+
raise ValueError("Time vector must be 1D array")
153+
154+
# Output vector
155+
self.yout = np.array(y)
156+
self.noutputs = 1 if len(self.yout.shape) < 2 else self.yout.shape[0]
157+
self.ninputs = 1 if len(self.yout.shape) < 3 else self.yout.shape[-2]
158+
# TODO: Check to make sure time points match
159+
160+
# State vector
161+
self.xout = np.array(x)
162+
self.nstates = self.xout.shape[0]
163+
# TODO: Check to make sure time points match
164+
165+
# Input vector
166+
self.uout = np.array(u)
167+
# TODO: Check to make sure input shape is OK
168+
# TODO: Check to make sure time points match
169+
170+
# If the system was specified, make sure it is compatible
171+
if sys is not None:
172+
if sys.ninputs != self.ninputs:
173+
ValueError("System inputs do not match response data")
174+
if sys.noutputs != self.noutputs:
175+
ValueError("System outputs do not match response data")
176+
if sys.nstates != self.nstates:
177+
ValueError("System states do not match response data")
178+
self.sys = sys
179+
180+
# Keep track of whether to squeeze inputs, outputs, and states
181+
self.squeeze = squeeze
144182

145183
# Store legacy keyword values (only used for legacy interface)
146-
self.return_x, self.squeeze = return_x, squeeze
184+
self.transpose = transpose
185+
self.return_x = return_x
186+
187+
# Getter for output (implements squeeze processing)
188+
@property
189+
def y(self):
190+
return self.yout
191+
192+
# Getter for state (implements squeeze processing)
193+
@property
194+
def x(self):
195+
return self.xout
147196

148197
# Implement iter to allow assigning to a tuple
149198
def __iter__(self):
@@ -565,8 +614,9 @@ def forced_response(sys, T=None, U=0., X0=0., transpose=False,
565614
xout = np.transpose(xout)
566615
yout = np.transpose(yout)
567616

568-
return _process_time_response(sys, tout, yout, xout, transpose=transpose,
569-
return_x=return_x, squeeze=squeeze)
617+
return InputOutputResponse(
618+
tout, yout, xout, U, sys=sys,
619+
transpose=transpose, return_x=return_x, squeeze=squeeze)
570620

571621

572622
# Process time responses in a uniform way
@@ -623,8 +673,21 @@ def _process_time_response(
623673
624674
Returns
625675
-------
626-
response: InputOutputResponse
627-
The input/output response of the system.
676+
T : 1D array
677+
Time values of the output
678+
679+
yout : ndarray
680+
Response of the system. If the system is SISO and squeeze is not
681+
True, the array is 1D (indexed by time). If the system is not SISO or
682+
squeeze is False, the array is either 2D (indexed by output and time)
683+
or 3D (indexed by input, output, and time).
684+
685+
xout : array, optional
686+
Individual response of each x variable (if return_x is True). For a
687+
SISO system (or if a single input is specified), xout is a 2D array
688+
indexed by the state index and time. For a non-SISO system, xout is a
689+
3D array indexed by the state, the input, and time. The shape of xout
690+
is not affected by the ``squeeze`` keyword.
628691
629692
"""
630693
# If squeeze was not specified, figure out the default (might remain None)
@@ -663,8 +726,7 @@ def _process_time_response(
663726
xout = np.transpose(xout, np.roll(range(xout.ndim), 1))
664727

665728
# Return time, output, and (optionally) state
666-
return InputOutputResponse(
667-
tout, yout, xout, None, return_x=return_x, squeeze=squeeze)
729+
return (tout, yout, xout) if return_x else (tout, yout)
668730

669731

670732
def _get_ss_simo(sys, input=None, output=None, squeeze=None):

0 commit comments

Comments
 (0)