@@ -1585,11 +1585,17 @@ def input_output_response(
15851585 T : array-like
15861586 Time steps at which the input is defined; values must be evenly spaced.
15871587
1588- U : array-like or number, optional
1589- Input array giving input at each time `T` (default = 0).
1590-
1591- X0 : array-like or number, optional
1592- Initial condition (default = 0).
1588+ U : array-like, list, or number, optional
1589+ Input array giving input at each time `T` (default = 0). If a list
1590+ is specified, each element in the list will be treated as a portion
1591+ of the input and broadcast (if necessary) to match the time vector.
1592+
1593+ X0 : array-like, list, or number, optional
1594+ Initial condition (default = 0). If a list is given, each element
1595+ in the list will be flattened and stacked into the initial
1596+ condition. If a smaller number of elements are given that the
1597+ number of states in the system, the initial condition will be padded
1598+ with zeros.
15931599
15941600 return_x : bool, optional
15951601 If True, return the state vector when assigning to a tuple (default =
@@ -1641,6 +1647,16 @@ def input_output_response(
16411647 ValueError
16421648 If time step does not match sampling time (for discrete time systems).
16431649
1650+ Notes
1651+ -----
1652+ 1. If a smaller number of initial conditions are given than the number of
1653+ states in the system, the initial conditions will be padded with
1654+ zeros. This is often useful for interconnected control systems where
1655+ the process dynamics are the first system and all other components
1656+ start with zero initial condition since this can be specified as
1657+ [xsys_0, 0]. A warning is issued if the initial conditions are padded
1658+ and and the final listed initial state is not zero.
1659+
16441660 """
16451661 #
16461662 # Process keyword arguments
@@ -1669,15 +1685,57 @@ def input_output_response(
16691685 n_steps = len (T )
16701686
16711687 # Check and convert the input, if needed
1672- # TODO: improve MIMO ninputs check (choose from U)
16731688 if sys .ninputs is None or sys .ninputs == 1 :
16741689 legal_shapes = [(n_steps ,), (1 , n_steps )]
16751690 else :
16761691 legal_shapes = [(sys .ninputs , n_steps )]
1692+
1693+ # If we were passed a list of input, concatenate them (w/ broadcast)
1694+ if isinstance (U , (tuple , list )):
1695+ U_elements = []
1696+ for i , u in enumerate (U ):
1697+ u = np .array (u ) # convert everyting to an array
1698+ # Process this input
1699+ if u .ndim == 0 or (u .ndim == 1 and u .shape [0 ] != T .shape [0 ]):
1700+ # Broadcast array to the length of the time input
1701+ u = np .outer (u , np .ones_like (T ))
1702+
1703+ elif (u .ndim == 1 and u .shape [0 ] == T .shape [0 ]) or \
1704+ (u .ndim == 2 and u .shape [1 ] == T .shape [0 ]):
1705+ # No processing necessary; just stack
1706+ pass
1707+
1708+ else :
1709+ raise ValueError (f"Input element { i } has inconsistent shape" )
1710+
1711+ # Append this input to our list
1712+ U_elements .append (u )
1713+
1714+ # Save the newly created input vector
1715+ U = np .vstack (U_elements )
1716+
1717+ # Make sure the input has the right shape
16771718 U = _check_convert_array (U , legal_shapes ,
16781719 'Parameter ``U``: ' , squeeze = False )
16791720 U = U .reshape (- 1 , n_steps )
16801721
1722+ # If we were passed a list of initial states, concatenate them
1723+ if isinstance (X0 , (tuple , list )):
1724+ X0_list = []
1725+ for i , x0 in enumerate (X0 ):
1726+ x0 = np .array (x0 ).reshape (- 1 ) # convert everyting to 1D array
1727+ X0_list += x0 .tolist () # add elements to initial state
1728+
1729+ # Save the newly created input vector
1730+ X0 = np .array (X0_list )
1731+
1732+ # If the initial state is too short, make it longer (NB: sys.nstates
1733+ # could be None if nstates comes from size of initial condition)
1734+ if sys .nstates and isinstance (X0 , np .ndarray ) and X0 .size < sys .nstates :
1735+ if X0 [- 1 ] != 0 :
1736+ warn ("initial state too short; padding with zeros" )
1737+ X0 = np .hstack ([X0 , np .zeros (sys .nstates - X0 .size )])
1738+
16811739 # Check to make sure this is not a static function
16821740 nstates = _find_size (sys .nstates , X0 )
16831741 if nstates == 0 :
0 commit comments