Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
updated sphinx documentation + changed to LinearICSystem
  • Loading branch information
murrayrm committed Jan 2, 2021
commit 4f04f485431cb96a00e71d3eba8ab6597c6199db
7 changes: 7 additions & 0 deletions control/bdalg.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,13 @@ def connect(sys, Q, inputv, outputv):
>>> Q = [[1, 2], [2, -1]] # negative feedback interconnection
>>> sysc = connect(sys, Q, [2], [1, 2])

Notes
-----
The :func:`~control.interconnect` function in the
:ref:`input/output systems <iosys-module>` module allows the use
of named signals and provides an alternative method for
interconnecting multiple systems.

"""
inputv, outputv, Q = np.asarray(inputv), np.asarray(outputv), np.asarray(Q)
# check indices
Expand Down
97 changes: 50 additions & 47 deletions control/iosys.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@
from . import config

__all__ = ['InputOutputSystem', 'LinearIOSystem', 'NonlinearIOSystem',
'InterconnectedSystem', 'input_output_response', 'find_eqpt',
'linearize', 'ss2io', 'tf2io', 'interconnect']
'InterconnectedSystem', 'LinearICSystem', 'input_output_response',
'find_eqpt', 'linearize', 'ss2io', 'tf2io', 'interconnect']

# Define module default parameter values
_iosys_defaults = {
Expand Down Expand Up @@ -110,8 +110,8 @@ class for a set of subclasses that are used to implement specific

Notes
-----
The `InputOuputSystem` class (and its subclasses) makes use of two special
methods for implementing much of the work of the class:
The :class:`~control.InputOuputSystem` class (and its subclasses) makes
use of two special methods for implementing much of the work of the class:

* _rhs(t, x, u): compute the right hand side of the differential or
difference equation for the system. This must be specified by the
Expand All @@ -137,8 +137,8 @@ def __init__(self, inputs=None, outputs=None, states=None, params={},
The InputOutputSystem contructor is used to create an input/output
object with the core information required for all input/output
systems. Instances of this class are normally created by one of the
input/output subclasses: :class:`~control.LinearIOSystem`,
:class:`~control.NonlinearIOSystem`,
input/output subclasses: :class:`~control.LinearICSystem`,
:class:`~control.LinearIOSystem`, :class:`~control.NonlinearIOSystem`,
:class:`~control.InterconnectedSystem`.

Parameters
Expand Down Expand Up @@ -242,10 +242,10 @@ def __mul__(sys2, sys1):
np.zeros((sys2.ninputs, sys2.noutputs))]]
))

# If both systems are linear, create LinearInterconnectedSystem
# If both systems are linear, create LinearICSystem
if isinstance(sys1, StateSpace) and isinstance(sys2, StateSpace):
ss_sys = StateSpace.__mul__(sys2, sys1)
return LinearInterconnectedSystem(newsys, ss_sys)
return LinearICSystem(newsys, ss_sys)

# Return the newly created InterconnectedSystem
return newsys
Expand Down Expand Up @@ -294,10 +294,10 @@ def __add__(sys1, sys2):
newsys = InterconnectedSystem(
(sys1, sys2), inplist=inplist, outlist=outlist)

# If both systems are linear, create LinearInterconnectedSystem
# If both systems are linear, create LinearICSystem
if isinstance(sys1, StateSpace) and isinstance(sys2, StateSpace):
ss_sys = StateSpace.__add__(sys2, sys1)
return LinearInterconnectedSystem(newsys, ss_sys)
return LinearICSystem(newsys, ss_sys)

# Return the newly created InterconnectedSystem
return newsys
Expand All @@ -315,10 +315,10 @@ def __neg__(sys):
newsys = InterconnectedSystem(
(sys,), dt=sys.dt, inplist=inplist, outlist=outlist)

# If the system is linear, create LinearInterconnectedSystem
# If the system is linear, create LinearICSystem
if isinstance(sys, StateSpace):
ss_sys = StateSpace.__neg__(sys)
return LinearInterconnectedSystem(newsys, ss_sys)
return LinearICSystem(newsys, ss_sys)

# Return the newly created system
return newsys
Expand Down Expand Up @@ -502,7 +502,7 @@ def feedback(self, other=1, sign=-1, params={}):
if isinstance(self, StateSpace) and isinstance(other, StateSpace):
# Special case: maintain linear systems structure
ss_sys = StateSpace.feedback(self, other, sign=sign)
return LinearInterconnectedSystem(newsys, ss_sys)
return LinearICSystem(newsys, ss_sys)

# Return the newly created system
return newsys
Expand Down Expand Up @@ -697,10 +697,10 @@ def __init__(self, updfcn, outfcn=None, inputs=None, outputs=None,
name=None, **kwargs):
"""Create a nonlinear I/O system given update and output functions.

Creates an `InputOutputSystem` for a nonlinear system by specifying a
state update function and an output function. The new system can be a
continuous or discrete time system (Note: discrete-time systems not
yet supported by most function.)
Creates an :class:`~control.InputOutputSystem` for a nonlinear system
by specifying a state update function and an output function. The new
system can be a continuous or discrete time system (Note:
discrete-time systems not yet supported by most function.)

Parameters
----------
Expand Down Expand Up @@ -1284,15 +1284,16 @@ def set_output_map(self, output_map):
self.noutputs = output_map.shape[0]


class LinearInterconnectedSystem(InterconnectedSystem, LinearIOSystem):
class LinearICSystem(InterconnectedSystem, LinearIOSystem):
"""Interconnection of a set of linear input/output systems.

This class is used to implement a system that is an interconnection of
linear input/output systems. It has all of the structure of an
:class:`InterconnectedSystem`, but also maintains the requirement elements
of :class:`LinearIOSystem`, including the :class:`StateSpace` class
structure, allowing it to be passed to functions that expect a
:class:`StateSpace` system.
:class:`~control.InterconnectedSystem`, but also maintains the requirement
elements of :class:`~control.LinearIOSystem`, including the
:class:`StateSpace` class structure, allowing it to be passed to functions
that expect a :class:`StateSpace` system.

"""

def __init__(self, io_sys, ss_sys=None):
Expand Down Expand Up @@ -1755,7 +1756,7 @@ def linearize(sys, xeq, ueq=[], t=0, params={}, **kw):
"""Linearize an input/output system at a given state and input.

This function computes the linearization of an input/output system at a
given state and input value and returns a :class:`control.StateSpace`
given state and input value and returns a :class:`~control.StateSpace`
object. The eavaluation point need not be an equilibrium point.

Parameters
Expand Down Expand Up @@ -1840,10 +1841,11 @@ def interconnect(syslist, connections=[], inplist=[], outlist=[],

This function creates a new system that is an interconnection of a set of
input/output systems. If all of the input systems are linear I/O systems
(type `LinearIOSystem`) then the resulting system will be a linear
interconnected I/O system (type `LinearInterconnectedSystem`) with the
appropriate inputs, outputs, and states. Otherwise, an interconnected I/O
system (type `InterconnectedSystem`) will be created.
(type :class:`~control.LinearIOSystem`) then the resulting system will be
a linear interconnected I/O system (type :class:`~control.LinearICSystem`)
with the appropriate inputs, outputs, and states. Otherwise, an
interconnected I/O system (type :class:`~control.InterconnectedSystem`)
will be created.

Parameters
----------
Expand Down Expand Up @@ -1895,8 +1897,8 @@ def interconnect(syslist, connections=[], inplist=[], outlist=[],
the system input connects to only one subsystem input, a single input
specification can be given (without the inner list).

If omitted, the input map can be specified using the `set_input_map`
method.
If omitted, the input map can be specified using the
:func:`~control.InterconnectedSystem.set_input_map` method.

outlist : list of output connections, optional
List of connections for how the outputs from the subsystems are mapped
Expand All @@ -1910,8 +1912,8 @@ def interconnect(syslist, connections=[], inplist=[], outlist=[],
then those signals are added together (multiplying by the any gain
term) to form the system output.

If omitted, the output map can be specified using the `set_output_map`
method.
If omitted, the output map can be specified using the
:func:`~control.InterconnectedSystem.set_output_map` method.

inputs : int, list of str or None, optional
Description of the system inputs. This can be given as an integer
Expand Down Expand Up @@ -1951,17 +1953,17 @@ def interconnect(syslist, connections=[], inplist=[], outlist=[],

Example
-------
P = control.LinearIOSystem(
ct.rss(2, 2, 2, strictly_proper=True), name='P')
C = control.LinearIOSystem(control.rss(2, 2, 2), name='C')
S = control.InterconnectedSystem(
[P, C],
connections = [
['P.u[0]', 'C.y[0]'], ['P.u[1]', 'C.y[0]'],
['C.u[0]', '-P.y[0]'], ['C.u[1]', '-P.y[1]']],
inplist = ['C.u[0]', 'C.u[1]'],
outlist = ['P.y[0]', 'P.y[1]'],
)
>>> P = control.LinearIOSystem(
>>> ct.rss(2, 2, 2, strictly_proper=True), name='P')
>>> C = control.LinearIOSystem(control.rss(2, 2, 2), name='C')
>>> S = control.InterconnectedSystem(
>>> [P, C],
>>> connections = [
>>> ['P.u[0]', 'C.y[0]'], ['P.u[1]', 'C.y[0]'],
>>> ['C.u[0]', '-P.y[0]'], ['C.u[1]', '-P.y[1]']],
>>> inplist = ['C.u[0]', 'C.u[1]'],
>>> outlist = ['P.y[0]', 'P.y[1]'],
>>> )

Notes
-----
Expand All @@ -1973,10 +1975,11 @@ def interconnect(syslist, connections=[], inplist=[], outlist=[],
check your use of tuples.

In addition to its use for general nonlinear I/O systems, the
`interconnect` function allows linear systems to be interconnected using
named signals (compared with the `connect` function, which uses signal
indicies) and to be treated as both a `StateSpace` system as well as an
`InputOutputSystem`.
:func:`~control.interconnect` function allows linear systems to be
interconnected using named signals (compared with the
:func:`~control.connect` function, which uses signal indicies) and to be
treated as both a :class:`~control.StateSpace` system as well as an
:class:`~control.InputOutputSystem`.

"""
newsys = InterconnectedSystem(
Expand All @@ -1986,6 +1989,6 @@ def interconnect(syslist, connections=[], inplist=[], outlist=[],

# If all subsystems are linear systems, maintain linear structure
if all([isinstance(sys, LinearIOSystem) for sys in syslist]):
return LinearInterconnectedSystem(newsys, None)
return LinearICSystem(newsys, None)

return newsys
4 changes: 2 additions & 2 deletions control/tests/iosys_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1293,7 +1293,7 @@ def test_linear_interconnection():
['sys2.y[1]'],
['sys2.u[1]']])
assert isinstance(nl_connect, ios.InterconnectedSystem)
assert not isinstance(nl_connect, ios.LinearInterconnectedSystem)
assert not isinstance(nl_connect, ios.LinearICSystem)

# Now take its linearization
ss_connect = nl_connect.linearize(0, 0)
Expand All @@ -1313,7 +1313,7 @@ def test_linear_interconnection():
['sys2.y[1]'],
['sys2.u[1]']])
assert isinstance(io_connect, ios.InterconnectedSystem)
assert isinstance(io_connect, ios.LinearInterconnectedSystem)
assert isinstance(io_connect, ios.LinearICSystem)
assert isinstance(io_connect, ios.LinearIOSystem)
assert isinstance(io_connect, ct.StateSpace)

Expand Down
2 changes: 1 addition & 1 deletion control/xferfcn.py
Original file line number Diff line number Diff line change
Expand Up @@ -809,7 +809,7 @@ def returnScipySignalLTI(self, strict=True):
continuous (0) or discrete (True or > 0).
False:
if `tfobject.dt` is None, continuous time
:class:`scipy.signal.lti`objects are returned
:class:`scipy.signal.lti` objects are returned

Returns
-------
Expand Down
7 changes: 3 additions & 4 deletions doc/classes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,17 @@ these directly.
TransferFunction
StateSpace
FrequencyResponseData
~iosys.InputOutputSystem
InputOutputSystem

Input/output system subclasses
==============================
.. currentmodule:: control.iosys

Input/output systems are accessed primarily via a set of subclasses
that allow for linear, nonlinear, and interconnected elements:

.. autosummary::
:toctree: generated/

InterconnectedSystem
LinearICSystem
LinearIOSystem
NonlinearIOSystem
InterconnectedSystem
11 changes: 6 additions & 5 deletions doc/control.rst
Original file line number Diff line number Diff line change
Expand Up @@ -139,11 +139,12 @@ Nonlinear system support
.. autosummary::
:toctree: generated/

~iosys.find_eqpt
~iosys.linearize
~iosys.input_output_response
~iosys.ss2io
~iosys.tf2io
find_eqpt
interconnect
linearize
input_output_response
ss2io
tf2io
flatsys.point_to_point

.. _utility-and-conversions:
Expand Down
62 changes: 30 additions & 32 deletions doc/iosys.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@
Input/output systems
********************

.. automodule:: control.iosys
:no-members:
:no-inherited-members:

Module usage
============

Expand Down Expand Up @@ -40,16 +36,16 @@ equation) and and output function (computes the outputs from the state)::
io_sys = NonlinearIOSystem(updfcn, outfcn, inputs=M, outputs=P, states=N)

More complex input/output systems can be constructed by using the
:class:`~control.InterconnectedSystem` class, which allows a collection of
input/output subsystems to be combined with internal connections between the
subsystems and a set of overall system inputs and outputs that link to the
subsystems::
:func:`~control.interconnect` function, which allows a collection of
input/output subsystems to be combined with internal connections
between the subsystems and a set of overall system inputs and outputs
that link to the subsystems::

steering = ct.InterconnectedSystem(
(plant, controller), name='system',
connections=(('controller.e', '-plant.y')),
inplist=('controller.e'), inputs='r',
outlist=('plant.y'), outputs='y')
steering = ct.interconnect(
[plant, controller], name='system',
connections=[['controller.e', '-plant.y']],
inplist=['controller.e'], inputs='r',
outlist=['plant.y'], outputs='y')

Interconnected systems can also be created using block diagram manipulations
such as the :func:`~control.series`, :func:`~control.parallel`, and
Expand Down Expand Up @@ -160,19 +156,19 @@ The input to the controller is `u`, consisting of the vector of hare and lynx
populations followed by the desired lynx population.

To connect the controller to the predatory-prey model, we create an
`InterconnectedSystem`:
:class:`~control.InterconnectedSystem`:

.. code-block:: python

io_closed = control.InterconnectedSystem(
(io_predprey, io_controller), # systems
connections=(
('predprey.u', 'control.y[0]'),
('control.u1', 'predprey.H'),
('control.u2', 'predprey.L')
),
inplist=('control.Ld'),
outlist=('predprey.H', 'predprey.L', 'control.y[0]')
io_closed = control.interconnect(
[io_predprey, io_controller], # systems
connections=[
['predprey.u', 'control.y[0]'],
['control.u1', 'predprey.H'],
['control.u2', 'predprey.L']
],
inplist=['control.Ld'],
outlist=['predprey.H', 'predprey.L', 'control.y[0]']
)

Finally, we simulate the closed loop system:
Expand Down Expand Up @@ -200,18 +196,20 @@ Input/output system classes
---------------------------
.. autosummary::

InputOutputSystem
InterconnectedSystem
LinearIOSystem
NonlinearIOSystem
~control.InputOutputSystem
~control.InterconnectedSystem
~control.LinearICSystem
~control.LinearIOSystem
~control.NonlinearIOSystem

Input/output system functions
-----------------------------
.. autosummary::

find_eqpt
linearize
input_output_response
ss2io
tf2io
~control.find_eqpt
~control.linearize
~control.input_output_response
~control.interconnect
~control.ss2io
~control.tf2io