From c7ba6fd8f72581dc4739cecc61a058d7283b2516 Mon Sep 17 00:00:00 2001 From: Sawyer Fuller Date: Fri, 26 Jun 2020 11:47:31 -0700 Subject: [PATCH 1/3] add capability to switch to legacy defaults with config.use_legacy_defaults(version) --- control/config.py | 11 ++++++++++- control/statesp.py | 12 +++++++----- control/tests/config_test.py | 21 +++++++++++++++++++-- control/xferfcn.py | 9 +++++++-- 4 files changed, 43 insertions(+), 10 deletions(-) diff --git a/control/config.py b/control/config.py index 02028cfba..a7ce35d0f 100644 --- a/control/config.py +++ b/control/config.py @@ -11,7 +11,7 @@ __all__ = ['defaults', 'set_defaults', 'reset_defaults', 'use_matlab_defaults', 'use_fbs_defaults', - 'use_numpy_matrix'] + 'use_legacy_defaults', 'use_numpy_matrix'] # Package level default values _control_defaults = { @@ -53,6 +53,9 @@ def reset_defaults(): from .rlocus import _rlocus_defaults defaults.update(_rlocus_defaults) + from .xferfcn import _xferfcn_defaults + defaults.update(_xferfcn_defaults) + from .statesp import _statesp_defaults defaults.update(_statesp_defaults) @@ -156,3 +159,9 @@ class and functions. If flat is `False`, then matrices are warnings.warn("Return type numpy.matrix is soon to be deprecated.", stacklevel=2) set_defaults('statesp', use_numpy_matrix=flag) + +def use_legacy_defaults(version): + if version == '0.8.3': + use_numpy_matrix(True) # alternatively: set_defaults('statesp', use_numpy_matrix=True) + else: + raise ValueError('''version number not recognized. Possible values are: ['0.8.3']''') \ No newline at end of file diff --git a/control/statesp.py b/control/statesp.py index 1779dfbfd..30be8a630 100644 --- a/control/statesp.py +++ b/control/statesp.py @@ -70,8 +70,10 @@ # Define module default parameter values _statesp_defaults = { - 'statesp.use_numpy_matrix': True, -} + 'statesp.use_numpy_matrix': False, + 'statesp.default_dt': None, + 'statesp.remove_useless_states': True, + } def _ssmatrix(data, axis=1): @@ -171,7 +173,7 @@ def __init__(self, *args, **kw): if len(args) == 4: # The user provided A, B, C, and D matrices. (A, B, C, D) = args - dt = None + dt = config.defaults['statesp.default_dt'] elif len(args) == 5: # Discrete time system (A, B, C, D, dt) = args @@ -187,12 +189,12 @@ def __init__(self, *args, **kw): try: dt = args[0].dt except NameError: - dt = None + dt = config.defaults['statesp.default_dt'] else: raise ValueError("Needs 1 or 4 arguments; received %i." % len(args)) # Process keyword arguments - remove_useless = kw.get('remove_useless', True) + remove_useless = kw.get('remove_useless', config.defaults['statesp.remove_useless_states']) # Convert all matrices to standard form A = _ssmatrix(A) diff --git a/control/tests/config_test.py b/control/tests/config_test.py index 1d2a5437b..2fdae22e4 100644 --- a/control/tests/config_test.py +++ b/control/tests/config_test.py @@ -48,7 +48,7 @@ def test_get_param(self): def test_fbs_bode(self): - ct.use_fbs_defaults(); + ct.use_fbs_defaults() # Generate a Bode plot plt.figure() @@ -94,7 +94,7 @@ def test_fbs_bode(self): ct.reset_defaults() def test_matlab_bode(self): - ct.use_matlab_defaults(); + ct.use_matlab_defaults() # Generate a Bode plot plt.figure() @@ -211,6 +211,23 @@ def test_reset_defaults(self): self.assertEqual( ct.config.defaults['freqplot.feature_periphery_decades'], 1.0) + def test_legacy_defaults(self): + ct.use_legacy_defaults('0.8.3') + assert(isinstance(ct.ss(0,0,0,1).D, np.matrix)) + ct.reset_defaults() + assert(isinstance(ct.ss(0,0,0,1).D, np.ndarray)) + + def test_change_default_dt(self): + ct.set_defaults('statesp', default_dt=0) + self.assertEqual(ct.ss(0,0,0,1).dt, 0) + ct.set_defaults('statesp', default_dt=None) + self.assertEqual(ct.ss(0,0,0,1).dt, None) + ct.set_defaults('xferfcn', default_dt=0) + self.assertEqual(ct.tf(1, 1).dt, 0) + ct.set_defaults('xferfcn', default_dt=None) + self.assertEqual(ct.tf(1, 1).dt, None) + + def tearDown(self): # Get rid of any figures that we created plt.close('all') diff --git a/control/xferfcn.py b/control/xferfcn.py index cb351de0f..cf8c0f535 100644 --- a/control/xferfcn.py +++ b/control/xferfcn.py @@ -63,10 +63,15 @@ from itertools import chain from re import sub from .lti import LTI, timebaseEqual, timebase, isdtime +from . import config __all__ = ['TransferFunction', 'tf', 'ss2tf', 'tfdata'] +# Define module default parameter values +_xferfcn_defaults = { + 'xferfcn.default_dt': None} + class TransferFunction(LTI): """TransferFunction(num, den[, dt]) @@ -117,7 +122,7 @@ def __init__(self, *args): if len(args) == 2: # The user provided a numerator and a denominator. (num, den) = args - dt = None + dt = config.defaults['xferfcn.default_dt'] elif len(args) == 3: # Discrete time transfer function (num, den, dt) = args @@ -133,7 +138,7 @@ def __init__(self, *args): try: dt = args[0].dt except NameError: # pragma: no coverage - dt = None + dt = config.defaults['xferfcn.default_dt'] else: raise ValueError("Needs 1, 2 or 3 arguments; received %i." % len(args)) From 85b0f5eb2b6e3c579d277938f271a0a235149ab5 Mon Sep 17 00:00:00 2001 From: Sawyer Fuller Date: Fri, 26 Jun 2020 12:40:15 -0700 Subject: [PATCH 2/3] reverted to using matrix as default in statesp to pass tests --- control/statesp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/control/statesp.py b/control/statesp.py index 30be8a630..2b4285bb4 100644 --- a/control/statesp.py +++ b/control/statesp.py @@ -70,7 +70,7 @@ # Define module default parameter values _statesp_defaults = { - 'statesp.use_numpy_matrix': False, + 'statesp.use_numpy_matrix': True, 'statesp.default_dt': None, 'statesp.remove_useless_states': True, } From 68c7ea6267ea10119d4d7624a065db998f8b5a5e Mon Sep 17 00:00:00 2001 From: Sawyer Fuller Date: Sat, 27 Jun 2020 13:22:30 -0700 Subject: [PATCH 3/3] added documentation to statesp, xferfcn, and conventions.rst describing new config parameters default_dt and remove_useless_states --- control/config.py | 7 +++++++ control/statesp.py | 3 ++- control/xferfcn.py | 4 +++- doc/conventions.rst | 13 +++++++++++-- 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/control/config.py b/control/config.py index a7ce35d0f..27a5712a3 100644 --- a/control/config.py +++ b/control/config.py @@ -161,6 +161,13 @@ class and functions. If flat is `False`, then matrices are set_defaults('statesp', use_numpy_matrix=flag) def use_legacy_defaults(version): + """ Sets the defaults to whatever they were in a given release. + + Parameters + ---------- + version : string + version number of the defaults desired. Currently only supports `0.8.3`. + """ if version == '0.8.3': use_numpy_matrix(True) # alternatively: set_defaults('statesp', use_numpy_matrix=True) else: diff --git a/control/statesp.py b/control/statesp.py index 2b4285bb4..df936ec2d 100644 --- a/control/statesp.py +++ b/control/statesp.py @@ -149,7 +149,8 @@ class StateSpace(LTI): Setting dt = 0 specifies a continuous system, while leaving dt = None means the system timebase is not specified. If 'dt' is set to True, the system will be treated as a discrete time system with unspecified sampling - time. + time. The default value of 'dt' is None and can be changed by changing the + value of ``control.config.defaults['statesp.default_dt']``. """ diff --git a/control/xferfcn.py b/control/xferfcn.py index cf8c0f535..c76b9a9a2 100644 --- a/control/xferfcn.py +++ b/control/xferfcn.py @@ -93,7 +93,9 @@ class TransferFunction(LTI): instance variable and setting it to something other than 'None'. If 'dt' has a non-zero value, then it must match whenever two transfer functions are combined. If 'dt' is set to True, the system will be treated as a - discrete time system with unspecified sampling time. + discrete time system with unspecified sampling time. The default value of + 'dt' is None and can be changed by changing the value of + ``control.config.defaults['xferfcn.default_dt']``. The TransferFunction class defines two constants ``s`` and ``z`` that represent the differentiation and delay operators in continuous and diff --git a/doc/conventions.rst b/doc/conventions.rst index c535027be..f07b51238 100644 --- a/doc/conventions.rst +++ b/doc/conventions.rst @@ -98,7 +98,9 @@ the result will be a discrete time system with the sample time of the latter system. For continuous time systems, the :func:`sample_system` function or the :meth:`StateSpace.sample` and :meth:`TransferFunction.sample` methods can be used to create a discrete time system from a continuous time system. -See :ref:`utility-and-conversions`. +See :ref:`utility-and-conversions`. The default value of 'dt' can be changed by +changing the values of ``control.config.defaults['statesp.default_dt']`` and +``control.config.defaults['xferfcn.default_dt']``. Conversion between representations ---------------------------------- @@ -220,9 +222,15 @@ Selected variables that can be configured, along with their default values: * freqplot.feature_periphery_decade (1.0): How many decades to include in the frequency range on both sides of features (poles, zeros). - * statesp.use_numpy_matrix: set the return type for state space matrices to + * statesp.use_numpy_matrix (True): set the return type for state space matrices to `numpy.matrix` (verus numpy.ndarray) + * statesp.default_dt and xferfcn.default_dt (None): set the default value of dt when + constructing new LTI systems + + * statesp.remove_useless_states (True): remove states that have no effect on the + input-output dynamics of the system + Additional parameter variables are documented in individual functions Functions that can be used to set standard configurations: @@ -234,3 +242,4 @@ Functions that can be used to set standard configurations: use_fbs_defaults use_matlab_defaults use_numpy_matrix + use_legacy_defaults