Skip to content

Time response data class #649

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Nov 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 41 additions & 16 deletions control/iosys.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
from warnings import warn

from .statesp import StateSpace, tf2ss, _convert_to_statespace
from .timeresp import _check_convert_array, _process_time_response
from .timeresp import _check_convert_array, _process_time_response, \
TimeResponseData
from .lti import isctime, isdtime, common_timebase
from . import config

Expand Down Expand Up @@ -878,7 +879,7 @@ def __init__(self, updfcn, outfcn=None, inputs=None, outputs=None,
def __call__(sys, u, params=None, squeeze=None):
"""Evaluate a (static) nonlinearity at a given input value

If a nonlinear I/O system has not internal state, then evaluating the
If a nonlinear I/O system has no internal state, then evaluating the
system at an input `u` gives the output `y = F(u)`, determined by the
output function.

Expand Down Expand Up @@ -907,7 +908,8 @@ def __call__(sys, u, params=None, squeeze=None):

# Evaluate the function on the argument
out = sys._out(0, np.array((0,)), np.asarray(u))
_, out = _process_time_response(sys, None, out, None, squeeze=squeeze)
_, out = _process_time_response(
None, out, issiso=sys.issiso(), squeeze=squeeze)
return out

def _update_params(self, params, warning=False):
Expand Down Expand Up @@ -1484,14 +1486,21 @@ def input_output_response(
----------
sys : InputOutputSystem
Input/output system to simulate.

T : array-like
Time steps at which the input is defined; values must be evenly spaced.

U : array-like or number, optional
Input array giving input at each time `T` (default = 0).

X0 : array-like or number, optional
Initial condition (default = 0).

return_x : bool, optional
If True, return the state vector when assigning to a tuple (default =
False). See :func:`forced_response` for more details.
If True, return the values of the state at each time (default = False).

squeeze : bool, optional
If True and if the system has a single output, return the system
output as a 1D array rather than a 2D array. If False, return the
Expand All @@ -1500,15 +1509,27 @@ def input_output_response(

Returns
-------
T : array
Time values of the output.
yout : array
Response of the system. If the system is SISO and squeeze is not
True, the array is 1D (indexed by time). If the system is not SISO or
squeeze is False, the array is 2D (indexed by the output number and
time).
xout : array
Time evolution of the state vector (if return_x=True).
results : TimeResponseData
Time response represented as a :class:`TimeResponseData` object
containing the following properties:

* time (array): Time values of the output.

* outputs (array): Response of the system. If the system is SISO and
`squeeze` is not True, the array is 1D (indexed by time). If the
system is not SISO or `squeeze` is False, the array is 2D (indexed
by output and time).

* states (array): Time evolution of the state vector, represented as
a 2D array indexed by state and time.

* inputs (array): Input(s) to the system, indexed by input and time.

The return value of the system can also be accessed by assigning the
function to a tuple of length 2 (time, output) or of length 3 (time,
output, state) if ``return_x`` is ``True``. If the input/output
system signals are named, these names will be used as labels for the
time response.

Other parameters
----------------
Expand Down Expand Up @@ -1570,8 +1591,9 @@ def input_output_response(
for i in range(len(T)):
u = U[i] if len(U.shape) == 1 else U[:, i]
y[:, i] = sys._out(T[i], [], u)
return _process_time_response(
sys, T, y, np.array((0, 0, np.asarray(T).size)),
return TimeResponseData(
T, y, None, U, issiso=sys.issiso(),
output_labels=sys.output_index, input_labels=sys.input_index,
transpose=transpose, return_x=return_x, squeeze=squeeze)

# create X0 if not given, test if X0 has correct shape
Expand Down Expand Up @@ -1666,8 +1688,11 @@ def ivp_rhs(t, x):
else: # Neither ctime or dtime??
raise TypeError("Can't determine system type")

return _process_time_response(sys, soln.t, y, soln.y, transpose=transpose,
return_x=return_x, squeeze=squeeze)
return TimeResponseData(
soln.t, y, soln.y, U, issiso=sys.issiso(),
output_labels=sys.output_index, input_labels=sys.input_index,
state_labels=sys.state_index,
transpose=transpose, return_x=return_x, squeeze=squeeze)


def find_eqpt(sys, x0, u0=[], y0=None, t=0, params={},
Expand Down
13 changes: 7 additions & 6 deletions control/optimal.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import logging
import time

from .timeresp import _process_time_response
from .timeresp import TimeResponseData

__all__ = ['find_optimal_input']

Expand Down Expand Up @@ -826,13 +826,14 @@ def __init__(
else:
states = None

retval = _process_time_response(
ocp.system, ocp.timepts, inputs, states,
# Process data as a time response (with "outputs" = inputs)
response = TimeResponseData(
ocp.timepts, inputs, states, issiso=ocp.system.issiso(),
transpose=transpose, return_x=return_states, squeeze=squeeze)

self.time = retval[0]
self.inputs = retval[1]
self.states = None if states is None else retval[2]
self.time = response.time
self.inputs = response.outputs
self.states = response.states


# Compute the input for a nonlinear, (constrained) optimal control problem
Expand Down
4 changes: 2 additions & 2 deletions control/statesp.py
Original file line number Diff line number Diff line change
Expand Up @@ -1932,10 +1932,10 @@ def rss(states=1, outputs=1, inputs=1, strictly_proper=False):
----------
states : int
Number of state variables
inputs : int
Number of system inputs
outputs : int
Number of system outputs
inputs : int
Number of system inputs
strictly_proper : bool, optional
If set to 'True', returns a proper system (no direct term).

Expand Down
2 changes: 1 addition & 1 deletion control/tests/timeresp_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1117,7 +1117,7 @@ def test_squeeze(self, fcn, nstate, nout, ninp, squeeze, shape1, shape2):
@pytest.mark.parametrize("fcn", [ct.ss, ct.tf, ct.ss2io])
def test_squeeze_exception(self, fcn):
sys = fcn(ct.rss(2, 1, 1))
with pytest.raises(ValueError, match="unknown squeeze value"):
with pytest.raises(ValueError, match="Unknown squeeze value"):
step_response(sys, squeeze=1)

@pytest.mark.usefixtures("editsdefaults")
Expand Down
Loading