Skip to content

Commit 732149c

Browse files
authored
allow single string as signal list for I/O system (#315)
* allow single string as signal list for I/O system * add __str__ for I/O systems + additional signal name fixes
1 parent 1de184e commit 732149c

File tree

1 file changed

+45
-24
lines changed

1 file changed

+45
-24
lines changed

control/iosys.py

Lines changed: 45 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,17 @@ def __init__(self, inputs=None, outputs=None, states=None, params={},
210210
def __repr__(self):
211211
return self.name if self.name is not None else str(type(self))
212212

213+
def __str__(self):
214+
"""String representation of an input/output system"""
215+
str = "System: " + (self.name if self.name else "(none)") + "\n"
216+
str += "Inputs (%d): " % self.ninputs
217+
for key in self.input_index: str += key + ", "
218+
str += "\nOutputs (%d): " % self.noutputs
219+
for key in self.output_index: str += key + ", "
220+
str += "\nStates (%d): " % self.nstates
221+
for key in self.state_index: str += key + ", "
222+
return str
223+
213224
def __mul__(sys2, sys1):
214225
"""Multiply two input/output systems (series interconnection)"""
215226

@@ -363,7 +374,11 @@ def _process_signal_list(self, signals, prefix='s'):
363374

364375
elif isinstance(signals, int):
365376
# Number of signals given; make up the names
366-
return signals, {'x[%d]' % i: i for i in range(signals)}
377+
return signals, {'%s[%d]' % (prefix, i): i for i in range(signals)}
378+
379+
elif isinstance(signals, str):
380+
# Single string given => single signal with given name
381+
return 1, {signals: 0}
367382

368383
elif all(isinstance(s, str) for s in signals):
369384
# Use the list of strings as the signal names
@@ -433,7 +448,7 @@ def set_outputs(self, outputs, prefix='y'):
433448
optional prefix parameter).
434449
prefix : string, optional
435450
If `outputs` is an integer, create the names of the states using
436-
the given prefix (default = 'u'). The names of the input will be
451+
the given prefix (default = 'y'). The names of the input will be
437452
of the form `prefix[i]`.
438453
439454
"""
@@ -453,7 +468,7 @@ def set_states(self, states, prefix='x'):
453468
optional prefix parameter).
454469
prefix : string, optional
455470
If `states` is an integer, create the names of the states using
456-
the given prefix (default = 'u'). The names of the input will be
471+
the given prefix (default = 'x'). The names of the input will be
457472
of the form `prefix[i]`.
458473
459474
"""
@@ -667,13 +682,16 @@ def __init__(self, linsys, inputs=None, outputs=None, states=None,
667682

668683
# Process input, output, state lists, if given
669684
# Make sure they match the size of the linear system
670-
ninputs, self.input_index = self._process_signal_list(inputs)
685+
ninputs, self.input_index = self._process_signal_list(
686+
inputs if inputs is not None else linsys.inputs, prefix='u')
671687
if ninputs is not None and linsys.inputs != ninputs:
672688
raise ValueError("Wrong number/type of inputs given.")
673-
noutputs, self.output_index = self._process_signal_list(outputs)
689+
noutputs, self.output_index = self._process_signal_list(
690+
outputs if outputs is not None else linsys.outputs, prefix='y')
674691
if noutputs is not None and linsys.outputs != noutputs:
675692
raise ValueError("Wrong number/type of outputs given.")
676-
nstates, self.state_index = self._process_signal_list(states)
693+
nstates, self.state_index = self._process_signal_list(
694+
states if states is not None else linsys.states, prefix='x')
677695
if nstates is not None and linsys.states != nstates:
678696
raise ValueError("Wrong number/type of states given.")
679697

@@ -868,20 +886,20 @@ def __init__(self, syslist, connections=[], inplist=[], outlist=[],
868886
869887
inplist : tuple of input specifications, optional
870888
List of specifications for how the inputs for the overall system
871-
are mapped to the subsystems. The input specification is the same
872-
as the form defined in the connection specification. Each system
873-
input is added to the input for the listed subsystem.
889+
are mapped to the subsystem inputs. The input specification is
890+
the same as the form defined in the connection specification.
891+
Each system input is added to the input for the listed subsystem.
874892
875893
If omitted, the input map can be specified using the
876894
`set_input_map` method.
877895
878896
outlist : tuple of output specifications, optional
879897
List of specifications for how the outputs for the subsystems are
880-
mapped to overall system. The output specification is the same as
881-
the form defined in the connection specification (including the
882-
optional gain term). Numbered outputs must be chosen from the
883-
list of subsystem outputs, but named outputs can also be contained
884-
in the list of subsystem inputs.
898+
mapped to overall system outputs. The output specification is the
899+
same as the form defined in the connection specification
900+
(including the optional gain term). Numbered outputs must be
901+
chosen from the list of subsystem outputs, but named outputs can
902+
also be contained in the list of subsystem inputs.
885903
886904
If omitted, the output map can be specified using the
887905
`set_output_map` method.
@@ -964,10 +982,12 @@ def __init__(self, syslist, connections=[], inplist=[], outlist=[],
964982
states=nstates, params=params, dt=dt)
965983

966984
# If input or output list was specified, update it
967-
nsignals, self.input_index = self._process_signal_list(inputs)
985+
nsignals, self.input_index = \
986+
self._process_signal_list(inputs, prefix='u')
968987
if nsignals is not None and len(inplist) != nsignals:
969988
raise ValueError("Wrong number/type of inputs given.")
970-
nsignals, self.output_index = self._process_signal_list(outputs)
989+
nsignals, self.output_index = \
990+
self._process_signal_list(outputs, prefix='y')
971991
if nsignals is not None and len(outlist) != nsignals:
972992
raise ValueError("Wrong number/type of outputs given.")
973993

@@ -981,8 +1001,10 @@ def __init__(self, syslist, connections=[], inplist=[], outlist=[],
9811001

9821002
# Convert the input list to a matrix: maps system to subsystems
9831003
self.input_map = np.zeros((ninputs, self.ninputs))
984-
for index in range(len(inplist)):
985-
self.input_map[self._parse_input_spec(inplist[index]), index] = 1
1004+
for index, inpspec in enumerate(inplist):
1005+
if isinstance(inpspec, (int, str, tuple)): inpspec = [inpspec]
1006+
for spec in inpspec:
1007+
self.input_map[self._parse_input_spec(spec), index] = 1
9861008

9871009
# Convert the output list to a matrix: maps subsystems to system
9881010
self.output_map = np.zeros((self.noutputs, noutputs + ninputs))
@@ -1116,10 +1138,9 @@ def _compute_static_io(self, t, x, u):
11161138
def _parse_input_spec(self, spec):
11171139
"""Parse an input specification and returns the index
11181140
1119-
This function parses a specification of an input of an
1120-
interconnected system component and returns the index of that
1121-
input in the internal input vector. Input specifications
1122-
are of one of the following forms:
1141+
This function parses a specification of an input of an interconnected
1142+
system component and returns the index of that input in the internal
1143+
input vector. Input specifications are of one of the following forms:
11231144
11241145
i first input for the ith system
11251146
(i,) first input for the ith system
@@ -1228,8 +1249,8 @@ def _parse_signal(self, spec, signame='input', dictname=None):
12281249
# For now, only allow signal level of system name
12291250
# TODO: expand to allow nested signal names
12301251
if len(namelist) != 2:
1231-
raise ValueError("Couldn't parse signal reference '%s'."
1232-
% spec)
1252+
raise ValueError("Couldn't parse %s signal reference '%s'."
1253+
% (signame, spec))
12331254

12341255
system_index = self._find_system(namelist[0])
12351256
if system_index is None:

0 commit comments

Comments
 (0)