Skip to content
Closed
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
672f229
Made sure interconnectedsystems keeps names of parts.
samlaf May 14, 2020
15a442a
Update control/iosys.py
samlaf May 18, 2020
77f9441
Update control/iosys.py
samlaf May 18, 2020
d45a6c0
Fixed signal names for InterconnectedSystems.
samlaf May 20, 2020
b688729
Merge branch 'interconnectedsystems' of https://github.com/samlaf/pyt…
samlaf May 20, 2020
c8fba97
Update control/iosys.py
samlaf Jul 15, 2020
bd38dea
Update control/iosys.py
samlaf Jul 15, 2020
35676f2
Fixed some naming convention problems and added unit tests for naming…
samlaf Jul 16, 2020
90705d4
Merge branch 'interconnectedsystems' of https://github.com/samlaf/pyt…
samlaf Jul 16, 2020
0113f99
set default dt to be 0 instead of None. Now need to fix resulting uni…
sawyerbfuller Jul 17, 2020
9aed7cc
changes so that a default dt=0 passes unit tests. fixed code everywhe…
sawyerbfuller Jul 17, 2020
7a9bcd4
small bugfix to xferfcn._evalfr to correctly give it dt value
sawyerbfuller Jul 18, 2020
d59e57a
removed a brittle unit test dependency that is already tested elsewhere
sawyerbfuller Jul 18, 2020
69cc8c5
Removed for loops from tests to get better error messages
samlaf Jul 19, 2020
e76a4f8
Update control/iosys.py
samlaf Jul 19, 2020
abb1f19
Changed a lot of naming convention code/unit test following pull-requ…
samlaf Jul 19, 2020
f955b3a
Merge branch 'interconnectedsystems' of https://github.com/samlaf/pyt…
samlaf Jul 19, 2020
3ec2f02
Fixed system naming conventions and unit tests. Still need to do sign…
samlaf Jul 19, 2020
8c957b2
Merge branch 'master' into interconnectedsystems
murrayrm Jul 20, 2020
c96465d
improved unit test suggestd by ben greiner
sawyerbfuller Jul 20, 2020
2ec1653
re-incorporated lti.timebaseEqual, with a pendingDeprecationWarning
sawyerbfuller Jul 20, 2020
1e001ee
changes to enable package-wide default, 'control.default_dt')
sawyerbfuller Jul 20, 2020
b2c8d44
Finished test_signals_naming_convention unit test
samlaf Jul 20, 2020
f5c3fac
fixes to docstrings and conventions.rst to match convention of dt=0 a…
sawyerbfuller Jul 20, 2020
c6787cc
remove extraneous file
sawyerbfuller Jul 21, 2020
606975d
new method is_static_gain() for state space and transfer function sys…
sawyerbfuller Jul 22, 2020
2bd912d
added keyword arguments to xferfcn.tf and statesp.ss to set dt
sawyerbfuller Jul 22, 2020
dc53eb9
unit tests for is_static_gain system method and dt keyword in system …
sawyerbfuller Jul 23, 2020
9da5295
Merge branch 'pr431' into mergetest-prs-400-431-433
bnavigator Jul 23, 2020
d1ac4bb
common_timebase will already raise
bnavigator Jul 23, 2020
e3ff725
Merge branch 'pr433' into mergetest-prs-400-431-433
bnavigator Jul 23, 2020
35f6202
Merge branch 'pr433' into merge-400-431-433-before438
bnavigator Jul 26, 2020
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
6 changes: 5 additions & 1 deletion control/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

# Package level default values
_control_defaults = {
# No package level defaults (yet)
'control.default_dt':0
}
defaults = dict(_control_defaults)

Expand Down Expand Up @@ -59,6 +59,9 @@ def reset_defaults():
from .statesp import _statesp_defaults
defaults.update(_statesp_defaults)

from .iosys import _iosys_defaults
defaults.update(_iosys_defaults)


def _get_param(module, param, argval=None, defval=None, pop=False):
"""Return the default value for a configuration option.
Expand Down Expand Up @@ -170,5 +173,6 @@ def use_legacy_defaults(version):
"""
if version == '0.8.3':
use_numpy_matrix(True) # alternatively: set_defaults('statesp', use_numpy_matrix=True)
set_defaults('control', default_dt=None)
else:
raise ValueError('''version number not recognized. Possible values are: ['0.8.3']''')
64 changes: 33 additions & 31 deletions control/dtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,21 +53,19 @@

# Sample a continuous time system
def sample_system(sysc, Ts, method='zoh', alpha=None, prewarp_frequency=None):
"""Convert a continuous time system to discrete time

Creates a discrete time system from a continuous time system by
sampling. Multiple methods of conversion are supported.
"""
Convert a continuous time system to discrete time by sampling

Parameters
----------
sysc : linsys
sysc : LTI (StateSpace or TransferFunction)
Continuous time system to be converted
Ts : real
Ts : real > 0
Sampling period
method : string
Method to use for conversion: 'matched', 'tustin', 'zoh' (default)
Method to use for conversion, e.g. 'bilinear', 'zoh' (default)

prewarp_frequency : float within [0, infinity)
prewarp_frequency : real within [0, infinity)
The frequency [rad/s] at which to match with the input continuous-
time system's magnitude and phase

Expand All @@ -78,13 +76,13 @@ def sample_system(sysc, Ts, method='zoh', alpha=None, prewarp_frequency=None):

Notes
-----
See `TransferFunction.sample` and `StateSpace.sample` for
See :meth:`StateSpace.sample` or :meth:`TransferFunction.sample`` for
further details.

Examples
--------
>>> sysc = TransferFunction([1], [1, 2, 1])
>>> sysd = sample_system(sysc, 1, method='matched')
>>> sysd = sample_system(sysc, 1, method='bilinear')
"""

# Make sure we have a continuous time system
Expand All @@ -95,35 +93,39 @@ def sample_system(sysc, Ts, method='zoh', alpha=None, prewarp_frequency=None):


def c2d(sysc, Ts, method='zoh', prewarp_frequency=None):
'''
Return a discrete-time system
"""
Convert a continuous time system to discrete time by sampling

Parameters
----------
sysc: LTI (StateSpace or TransferFunction), continuous
System to be converted
sysc : LTI (StateSpace or TransferFunction)
Continuous time system to be converted
Ts : real > 0
Sampling period
method : string
Method to use for conversion, e.g. 'bilinear', 'zoh' (default)

Ts: number
Sample time for the conversion
prewarp_frequency : real within [0, infinity)
The frequency [rad/s] at which to match with the input continuous-
time system's magnitude and phase

method: string, optional
Method to be applied,
'zoh' Zero-order hold on the inputs (default)
'foh' First-order hold, currently not implemented
'impulse' Impulse-invariant discretization, currently not implemented
'tustin' Bilinear (Tustin) approximation, only SISO
'matched' Matched pole-zero method, only SISO
Returns
-------
sysd : linsys
Discrete time system, with sampling rate Ts

Notes
-----
See :meth:`StateSpace.sample` or :meth:`TransferFunction.sample`` for
further details.

prewarp_frequency : float within [0, infinity)
The frequency [rad/s] at which to match with the input continuous-
time system's magnitude and phase
Examples
--------
>>> sysc = TransferFunction([1], [1, 2, 1])
>>> sysd = sample_system(sysc, 1, method='bilinear')
"""

'''
# Call the sample_system() function to do the work
sysd = sample_system(sysc, Ts, method, prewarp_frequency)

# TODO: is this check needed? If sysc is StateSpace, sysd is too?
if isinstance(sysc, StateSpace) and not isinstance(sysd, StateSpace):
return _convertToStateSpace(sysd) # pragma: no cover

return sysd
93 changes: 51 additions & 42 deletions control/iosys.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,15 @@

from .statesp import StateSpace, tf2ss
from .timeresp import _check_convert_array
from .lti import isctime, isdtime, _find_timebase
from .lti import isctime, isdtime, common_timebase
from . import config

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

# Define module default parameter values
_iosys_defaults = {}

class InputOutputSystem(object):
"""A class for representing input/output systems.
Expand All @@ -69,9 +72,11 @@ class for a set of subclasses that are used to implement specific
states : int, list of str, or None
Description of the system states. Same format as `inputs`.
dt : None, True or float, optional
System timebase. None (default) indicates continuous time, True
indicates discrete time with undefined sampling time, positive number
is discrete time with specified sampling time.
System timebase. 0 (default) indicates continuous
time, True indicates discrete time with unspecified sampling
time, positive number is discrete time with specified
sampling time, None indicates unspecified timebase (either
continuous or discrete time).
params : dict, optional
Parameter values for the systems. Passed to the evaluation functions
for the system as default values, overriding internal defaults.
Expand All @@ -87,9 +92,11 @@ class for a set of subclasses that are used to implement specific
Dictionary of signal names for the inputs, outputs and states and the
index of the corresponding array
dt : None, True or float
System timebase. None (default) indicates continuous time, True
indicates discrete time with undefined sampling time, positive number
is discrete time with specified sampling time.
System timebase. 0 (default) indicates continuous
time, True indicates discrete time with unspecified sampling
time, positive number is discrete time with specified
sampling time, None indicates unspecified timebase (either
continuous or discrete time).
params : dict, optional
Parameter values for the systems. Passed to the evaluation functions
for the system as default values, overriding internal defaults.
Expand Down Expand Up @@ -118,7 +125,7 @@ def name_or_default(self, name=None):
return name

def __init__(self, inputs=None, outputs=None, states=None, params={},
dt=None, name=None):
name=None, **kwargs):
"""Create an input/output system.

The InputOutputSystem contructor is used to create an input/output
Expand All @@ -143,10 +150,11 @@ def __init__(self, inputs=None, outputs=None, states=None, params={},
states : int, list of str, or None
Description of the system states. Same format as `inputs`.
dt : None, True or float, optional
System timebase. None (default) indicates continuous
time, True indicates discrete time with undefined sampling
System timebase. 0 (default) indicates continuous
time, True indicates discrete time with unspecified sampling
time, positive number is discrete time with specified
sampling time.
sampling time, None indicates unspecified timebase (either
continuous or discrete time).
params : dict, optional
Parameter values for the systems. Passed to the evaluation
functions for the system as default values, overriding internal
Expand All @@ -161,10 +169,10 @@ def __init__(self, inputs=None, outputs=None, states=None, params={},
Input/output system object

"""
# Store the input arguments
self.params = params.copy() # default parameters
self.dt = dt # timebase
self.name = self.name_or_default(name) # system name
# Store the input arguments: default parameter, timebase, system name
self.params = params.copy()
self.dt = kwargs.get('dt', config.defaults['control.default_dt'])
self.name = self.name_or_default(name)

# Parse and store the number of inputs, outputs, and states
self.set_inputs(inputs)
Expand Down Expand Up @@ -210,14 +218,16 @@ def __mul__(sys2, sys1):
"inputs and outputs")

# Make sure timebase are compatible
dt = _find_timebase(sys1, sys2)
dt = common_timebase(sys1.dt, sys2.dt)
if dt is False:
raise ValueError("System timebases are not compabile")

inplist = [(0,i) for i in range(sys1.ninputs)]
outlist = [(1,i) for i in range(sys2.noutputs)]
inplist = [(0, i) for i in range(sys1.ninputs)]
outlist = [(1, i) for i in range(sys2.noutputs)]

# Return the series interconnection between the systems
newsys = InterconnectedSystem((sys1, sys2), inplist=inplist, outlist=outlist)
newsys = InterconnectedSystem((sys1, sys2),
inplist=inplist, outlist=outlist)

# Set up the connection map manually
newsys.set_connect_map(np.block(
Expand Down Expand Up @@ -464,14 +474,16 @@ def feedback(self, other=1, sign=-1, params={}):
"inputs and outputs")

# Make sure timebases are compatible
dt = _find_timebase(self, other)
dt = common_timebase(self.dt, other.dt)
if dt is False:
raise ValueError("System timebases are not compabile")

inplist = [(0,i) for i in range(self.ninputs)]
outlist = [(0,i) for i in range(self.noutputs)]
inplist = [(0, i) for i in range(self.ninputs)]
outlist = [(0, i) for i in range(self.noutputs)]

# Return the series interconnection between the systems
newsys = InterconnectedSystem((self, other), inplist=inplist, outlist=outlist,
newsys = InterconnectedSystem((self, other),
inplist=inplist, outlist=outlist,
params=params, dt=dt)

# Set up the connecton map manually
Expand Down Expand Up @@ -580,10 +592,11 @@ def __init__(self, linsys, inputs=None, outputs=None, states=None,
states : int, list of str, or None, optional
Description of the system states. Same format as `inputs`.
dt : None, True or float, optional
System timebase. None (default) indicates continuous
time, True indicates discrete time with undefined sampling
System timebase. 0 (default) indicates continuous
time, True indicates discrete time with unspecified sampling
time, positive number is discrete time with specified
sampling time.
sampling time, None indicates unspecified timebase (either
continuous or discrete time).
params : dict, optional
Parameter values for the systems. Passed to the evaluation
functions for the system as default values, overriding internal
Expand Down Expand Up @@ -650,7 +663,8 @@ class NonlinearIOSystem(InputOutputSystem):

"""
def __init__(self, updfcn, outfcn=None, inputs=None, outputs=None,
states=None, params={}, dt=None, name=None):
states=None, params={},
name=None, **kwargs):
"""Create a nonlinear I/O system given update and output functions.

Creates an `InputOutputSystem` for a nonlinear system by specifying a
Expand Down Expand Up @@ -702,10 +716,10 @@ def __init__(self, updfcn, outfcn=None, inputs=None, outputs=None,
operating in continuous or discrete time. It can have the
following values:

* dt = None No timebase specified
* dt = 0 Continuous time system
* dt > 0 Discrete time system with sampling time dt
* dt = True Discrete time with unspecified sampling time
* dt = 0: continuous time system (default)
* dt > 0: discrete time system with sampling period 'dt'
* dt = True: discrete time with unspecified sampling period
* dt = None: no timebase specified

name : string, optional
System name (used for specifying signals). If unspecified, a generic
Expand All @@ -722,6 +736,7 @@ def __init__(self, updfcn, outfcn=None, inputs=None, outputs=None,
self.outfcn = outfcn

# Initialize the rest of the structure
dt = kwargs.get('dt', config.defaults['control.default_dt'])
super(NonlinearIOSystem, self).__init__(
inputs=inputs, outputs=outputs, states=states,
params=params, dt=dt, name=name
Expand Down Expand Up @@ -871,10 +886,10 @@ def __init__(self, syslist, connections=[], inplist=[], outlist=[],
operating in continuous or discrete time. It can have the
following values:

* dt = None No timebase specified
* dt = 0 Continuous time system
* dt > 0 Discrete time system with sampling time dt
* dt = True Discrete time with unspecified sampling time
* dt = 0: continuous time system (default)
* dt > 0: discrete time system with sampling period 'dt'
* dt = True: discrete time with unspecified sampling period
* dt = None: no timebase specified

name : string, optional
System name (used for specifying signals). If unspecified, a generic
Expand All @@ -888,20 +903,14 @@ def __init__(self, syslist, connections=[], inplist=[], outlist=[],
# Check to make sure all systems are consistent
self.syslist = syslist
self.syslist_index = {}
dt = None
nstates = 0; self.state_offset = []
ninputs = 0; self.input_offset = []
noutputs = 0; self.output_offset = []
sysobj_name_dct = {}
sysname_count_dct = {}
for sysidx, sys in enumerate(syslist):
# Make sure time bases are consistent
# TODO: Use lti._find_timebase() instead?
if dt is None and sys.dt is not None:
# Timebase was not specified; set to match this system
dt = sys.dt
elif dt != sys.dt:
raise TypeError("System timebases are not compatible")
dt = common_timebase(dt, sys.dt)

# Make sure number of inputs, outputs, states is given
if sys.ninputs is None or sys.noutputs is None or \
Expand Down
Loading