From f17c9194456b659c390c0190d14d2c38f5bc932c Mon Sep 17 00:00:00 2001 From: Richard Murray Date: Wed, 3 Aug 2022 21:34:27 -0700 Subject: [PATCH 1/3] fix timebase bug in InterconnectedSystem (issue #754) --- control/iosys.py | 7 +++++-- control/tests/timebase_test.py | 37 ++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 control/tests/timebase_test.py diff --git a/control/iosys.py b/control/iosys.py index 6008cf43d..ce717c0fb 100644 --- a/control/iosys.py +++ b/control/iosys.py @@ -871,9 +871,12 @@ def __init__(self, syslist, connections=[], inplist=[], outlist=[], if not isinstance(outlist, (list, tuple)): outlist = [outlist] - # Process keyword arguments + # Check if dt argument was given; if not, pull from systems + dt = kwargs.pop('dt', None) + + # Process keyword arguments (except dt) defaults = {'inputs': len(inplist), 'outputs': len(outlist)} - name, inputs, outputs, states, dt = _process_namedio_keywords( + name, inputs, outputs, states, _ = _process_namedio_keywords( kwargs, defaults, end=True) # Initialize the system list and index diff --git a/control/tests/timebase_test.py b/control/tests/timebase_test.py new file mode 100644 index 000000000..50d69fb88 --- /dev/null +++ b/control/tests/timebase_test.py @@ -0,0 +1,37 @@ +import pytest +import inspect +import numpy as np +import control as ct + +@pytest.mark.parametrize( + "dt1, dt2, dt3", [ + (0, 0, 0), + (0, 0.1, ValueError), + (0, None, 0), + (0.1, 0, ValueError), + (0.1, 0.1, 0.1), + (0.1, None, 0.1), + (None, 0, 0), + (None, 0.1, 0.1), + (None, None, None), + (0.2, None, 0.2), + (0.2, 0.1, ValueError), + ]) +@pytest.mark.parametrize("op", [ct.series, ct.parallel, ct.feedback]) +@pytest.mark.parametrize("type", [ct.StateSpace, ct.ss, ct.tf]) +def test_composition(dt1, dt2, dt3, op, type): + # Define the system + A, B, C, D = [[1, 1], [0, 1]], [[0], [1]], [[1, 0]], 0 + sys1 = ct.StateSpace(A, B, C, D, dt1) + sys2 = ct.StateSpace(A, B, C, D, dt2) + + # Convert to the desired form + sys1 = type(sys1) + sys2 = type(sys2) + + if inspect.isclass(dt3) and issubclass(dt3, Exception): + with pytest.raises(dt3, match="incompatible timebases"): + sys3 = op(sys1, sys2) + else: + sys3 = op(sys1, sys2) + assert sys3.dt == dt3 From 3c13f2c58eb17f37a95551a5171dc7c9ad45bd6b Mon Sep 17 00:00:00 2001 From: Richard Murray Date: Wed, 3 Aug 2022 21:54:06 -0700 Subject: [PATCH 2/3] add additional checks for overriding timebase --- control/tests/timebase_test.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/control/tests/timebase_test.py b/control/tests/timebase_test.py index 50d69fb88..f245f625f 100644 --- a/control/tests/timebase_test.py +++ b/control/tests/timebase_test.py @@ -35,3 +35,28 @@ def test_composition(dt1, dt2, dt3, op, type): else: sys3 = op(sys1, sys2) assert sys3.dt == dt3 + + +@pytest.mark.parametrize("dt", [None, 0, 0.1]) +def test_composition_override(dt): + # Define the system + A, B, C, D = [[1, 1], [0, 1]], [[0], [1]], [[1, 0]], 0 + sys1 = ct.ss(A, B, C, D, None, inputs='u1', outputs='y1') + sys2 = ct.ss(A, B, C, D, None, inputs='y1', outputs='y2') + + # Show that we can override the type + sys3 = ct.interconnect([sys1, sys2], inputs='u1', outputs='y2', dt=dt) + assert sys3.dt == dt + + # Overriding the type with an inconsistent type generates an error + sys1 = ct.StateSpace(A, B, C, D, 0.1, inputs='u1', outputs='y1') + if dt != 0.1 and dt is not None: + with pytest.raises(ValueError, match="incompatible timebases"): + sys3 = ct.interconnect( + [sys1, sys2], inputs='u1', outputs='y2', dt=dt) + + sys1 = ct.StateSpace(A, B, C, D, 0, inputs='u1', outputs='y1') + if dt != 0 and dt is not None: + with pytest.raises(ValueError, match="incompatible timebases"): + sys3 = ct.interconnect( + [sys1, sys2], inputs='u1', outputs='y2', dt=dt) From cf304794745baf4aa246df30feccdc3b0c43a490 Mon Sep 17 00:00:00 2001 From: Richard Murray Date: Wed, 3 Aug 2022 22:56:47 -0700 Subject: [PATCH 3/3] added tests for dt=True case per @sawyerbfuller suggestion --- control/tests/timebase_test.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/control/tests/timebase_test.py b/control/tests/timebase_test.py index f245f625f..a391d2fe7 100644 --- a/control/tests/timebase_test.py +++ b/control/tests/timebase_test.py @@ -8,12 +8,19 @@ (0, 0, 0), (0, 0.1, ValueError), (0, None, 0), + (0, True, ValueError), (0.1, 0, ValueError), (0.1, 0.1, 0.1), (0.1, None, 0.1), + (0.1, True, 0.1), (None, 0, 0), (None, 0.1, 0.1), (None, None, None), + (None, True, True), + (True, 0, ValueError), + (True, 0.1, 0.1), + (True, None, True), + (True, True, True), (0.2, None, 0.2), (0.2, 0.1, ValueError), ])