Skip to content

Commit 40e6021

Browse files
committed
add ability to use signal base names in interconnect()
1 parent c307f5b commit 40e6021

File tree

3 files changed

+32
-16
lines changed

3 files changed

+32
-16
lines changed

control/iosys.py

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
import numpy as np
3030
import scipy as sp
3131
import copy
32-
import itertools
3332
from warnings import warn
3433

3534
from .lti import LTI
@@ -1011,7 +1010,7 @@ def __init__(self, syslist, connections=None, inplist=None, outlist=None,
10111010
output_indices, gain = self._parse_output_spec(output_spec)
10121011
if len(output_indices) != len(input_indices):
10131012
raise ValueError(
1014-
f"inconsistent number signals in connecting"
1013+
f"inconsistent number of signals in connecting"
10151014
f" '{output_spec}' to '{connection[0]}'")
10161015

10171016
for input_index, output_index in zip(
@@ -2541,7 +2540,8 @@ def interconnect(
25412540
signals are given names, then the forms 'sys.sig' or ('sys', 'sig')
25422541
are also recognized. Finally, for multivariable systems the signal
25432542
index can be given as a list, for example '(subsys_i, [inp_j1, ...,
2544-
inp_jn])' or as as a slice, for example, 'sys.sig[i:j]'.
2543+
inp_jn])', as as a slice, for example, 'sys.sig[i:j]', or as a base
2544+
name `sys.sig` (which matches `sys.sig[i]`).
25452545
25462546
Similarly, each output-spec should describe an output signal from
25472547
one of the subsystems. The lowest level representation is a tuple
@@ -2552,9 +2552,9 @@ def interconnect(
25522552
subsystem are used. If systems and signals are given names, then
25532553
the form 'sys.sig', ('sys', 'sig') or ('sys', 'sig', gain) are also
25542554
recognized, and the special form '-sys.sig' can be used to specify
2555-
a signal with gain -1. Lists and slices can also be used, as long
2556-
as the number of elements for each output spec mataches the input
2557-
spec.
2555+
a signal with gain -1. Lists, slices, and base names can also be
2556+
used, as long as the number of elements for each output spec
2557+
mataches the input spec.
25582558
25592559
If omitted, the `interconnect` function will attempt to create the
25602560
interconnection map by connecting all signals with the same base names
@@ -2685,11 +2685,12 @@ def interconnect(
26852685
... outlist=['P.y[0]', 'P.y[1]'],
26862686
... )
26872687
2688-
This expression can be simplified using slice notation:
2688+
This expression can be simplified using either slice notation or
2689+
just signal basenames:
26892690
26902691
>>> T = ct.interconnect(
2691-
... [P, C], connections=[['P.u[:]', 'C.y[:]'], ['C.u[:]', '-P.y[:]']],
2692-
... inplist='C.u[:]', outlist='P.y[:]')
2692+
... [P, C], connections=[['P.u[:]', 'C.y[:]'], ['C.u', '-P.y']],
2693+
... inplist='C.u', outlist='P.y[:]')
26932694
26942695
or further simplified by omitting the input and output signal
26952696
specifications (since all inputs and outputs are used):
@@ -2775,6 +2776,11 @@ def interconnect(
27752776
# Use an empty connections list
27762777
connections = []
27772778

2779+
elif isinstance(connections, list) and \
2780+
all([isinstance(cnxn, (str, tuple)) for cnxn in connections]):
2781+
# Special case where there is a single connection
2782+
connections = [connections]
2783+
27782784
# If inplist/outlist is not present, try using inputs/outputs instead
27792785
if inplist is None:
27802786
inplist = list(inputs or [])

control/namedio.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -113,19 +113,26 @@ def _find_signals(self, name_list, sigdict):
113113

114114
index_list = []
115115
for name in name_list:
116-
# Look for signal ranges (slice-like)
117-
m = re.match(r'([\w$]+)\[([\d]*):([\d]*)\]$', name)
118-
if m:
119-
base = m.group(1)
120-
start = None if m.group(2) == '' else int(m.group(2))
121-
stop = None if m.group(3) == '' else int(m.group(3))
116+
# Look for signal ranges (slice-like or base name)
117+
ms = re.match(r'([\w$]+)\[([\d]*):([\d]*)\]$', name) # slice
118+
mb = re.match(r'([\w$]+)$', name) # base
119+
if ms:
120+
base = ms.group(1)
121+
start = None if ms.group(2) == '' else int(ms.group(2))
122+
stop = None if ms.group(3) == '' else int(ms.group(3))
122123
for var in sigdict:
123124
# Find variables that match
124-
msig = re.match(r'([\w$]+)\[([\d]*)\]$', var)
125+
msig = re.match(r'([\w$]+)\[([\d]+)\]$', var)
125126
if msig.group(1) == base and \
126127
(start is None or int(msig.group(2)) >= start) and \
127128
(stop is None or int(msig.group(2)) < stop):
128129
index_list.append(int(msig.group(2)))
130+
elif mb and sigdict.get(name, None) is None:
131+
# Try to use name as a base name
132+
for var in sigdict:
133+
msig = re.match(name + r'\[([\d]+)\]$', var)
134+
if msig:
135+
index_list.append(int(msig.group(1)))
129136
else:
130137
index_list.append(sigdict.get(name, None))
131138

control/tests/iosys_test.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2066,6 +2066,9 @@ def test_iosys_sample():
20662066
[['sys2.u[0:3]', 'sys1.y[:]']],
20672067
'sys1.u[:]', ['sys2.y[0:3]'], None, None,
20682068
id="signal slices"),
2069+
pytest.param(
2070+
['sys2.u', 'sys1.y'], 'sys1.u', 'sys2.y', None, None,
2071+
id="signal basenames"),
20692072
pytest.param(
20702073
[[('sys2', [0, 1, 2]), ('sys1', [0, 1, 2])]],
20712074
[('sys1', [0, 1, 2])], [('sys2', [0, 1, 2])],

0 commit comments

Comments
 (0)