Skip to content

Commit b87b83d

Browse files
committed
expanded __str__ for IC systems (subsys list + connection map)
1 parent b94f381 commit b87b83d

4 files changed

Lines changed: 117 additions & 4 deletions

File tree

control/nlsys.py

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
from . import config
2828
from .iosys import InputOutputSystem, _parse_spec, _process_iosys_keywords, \
29-
_process_signal_list, common_timebase, isctime, isdtime
29+
_process_signal_list, common_timebase, iosys_repr, isctime, isdtime
3030
from .timeresp import _check_convert_array, _process_time_response, \
3131
TimeResponseData, TimeResponseList
3232

@@ -773,6 +773,71 @@ def outfcn(t, x, u, params):
773773
index + "; combining with previous entries")
774774
self.output_map[index + j, ylist_index] += gain
775775

776+
def __str__(self):
777+
import textwrap
778+
out = super().__str__()
779+
780+
out += f"\n\nSubsystems ({len(self.syslist)}):\n"
781+
for sys in self.syslist:
782+
out += "\n".join(textwrap.wrap(
783+
iosys_repr(sys, format='info'), width=78,
784+
initial_indent=" * ", subsequent_indent=" ")) + "\n"
785+
786+
# Build a list of input, output, and inpout signals
787+
input_list, output_list, inpout_list = [], [], []
788+
for sys in self.syslist:
789+
input_list += [sys.name + "." + lbl for lbl in sys.input_labels]
790+
output_list += [sys.name + "." + lbl for lbl in sys.output_labels]
791+
inpout_list = input_list + output_list
792+
793+
# Define a utility function to generate the signal
794+
def cxn_string(signal, gain, first):
795+
if gain == 1:
796+
return (" + " if not first else "") + f"{signal}"
797+
elif gain == -1:
798+
return (" - " if not first else "-") + f"{signal}"
799+
elif gain > 0:
800+
return (" + " if not first else "") + f"{gain} * {signal}"
801+
elif gain < 0:
802+
return (" - " if not first else "-") + \
803+
f"{abs(gain)} * {signal}"
804+
805+
out += f"\nConnections:\n"
806+
for i in range(len(input_list)):
807+
first = True
808+
cxn = f"{input_list[i]} <- "
809+
if np.any(self.connect_map[i]):
810+
for j in range(len(output_list)):
811+
if self.connect_map[i, j]:
812+
cxn += cxn_string(
813+
output_list[j], self.connect_map[i,j], first)
814+
first = False
815+
if np.any(self.input_map[i]):
816+
for j in range(len(self.input_labels)):
817+
if self.input_map[i, j]:
818+
cxn += cxn_string(
819+
self.input_labels[j], self.input_map[i, j], first)
820+
first = False
821+
out += "\n".join(textwrap.wrap(
822+
cxn, width=78, initial_indent=" * ",
823+
subsequent_indent=" ")) + "\n"
824+
825+
out += f"\nOutputs:\n"
826+
for i in range(len(self.output_labels)):
827+
first = True
828+
cxn = f"{self.output_labels[i]} <- "
829+
if np.any(self.output_map[i]):
830+
for j in range(len(inpout_list)):
831+
if self.output_map[i, j]:
832+
cxn += cxn_string(
833+
output_list[j], self.output_map[i, j], first)
834+
first = False
835+
out += "\n".join(textwrap.wrap(
836+
cxn, width=78, initial_indent=" * ",
837+
subsequent_indent=" ")) + "\n"
838+
839+
return out[:-1]
840+
776841
def _update_params(self, params, warning=False):
777842
for sys in self.syslist:
778843
local = sys.params.copy() # start with system parameters

control/statesp.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -379,8 +379,8 @@ def _remove_useless_states(self):
379379
def __str__(self):
380380
"""Return string representation of the state space system."""
381381
string = f"{InputOutputSystem.__str__(self)}\n\n"
382-
string += "\n".join([
383-
"{} = {}\n".format(Mvar,
382+
string += "\n\n".join([
383+
"{} = {}".format(Mvar,
384384
"\n ".join(str(M).splitlines()))
385385
for Mvar, M in zip(["A", "B", "C", "D"],
386386
[self.A, self.B, self.C, self.D])])

control/tests/nlsys_test.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,3 +196,51 @@ def test_ss2io():
196196
with pytest.raises(ValueError, match=r"new .* doesn't match"):
197197
kwargs = {attr: getattr(sys, 'n' + attr) - 1}
198198
nlsys = ct.nlsys(sys, **kwargs)
199+
200+
201+
def test_ICsystem_str():
202+
sys1 = ct.rss(2, 2, 3, name='sys1', strictly_proper=True)
203+
sys2 = ct.rss(2, 3, 2, name='sys2', strictly_proper=True)
204+
205+
with pytest.warns(UserWarning, match="Unused") as record:
206+
sys = ct.interconnect(
207+
[sys1, sys2], inputs=['r1', 'r2'], outputs=['y1', 'y2'],
208+
connections=[
209+
['sys1.u[0]', '-sys2.y[0]', 'sys2.y[1]'],
210+
['sys1.u[1]', 'sys2.y[0]', '-sys2.y[1]'],
211+
['sys2.u[0]', 'sys2.y[0]', (0, 0, -1)],
212+
['sys2.u[1]', (1, 1, -2), (0, 1, -2)],
213+
],
214+
inplist=['sys1.u[0]', 'sys1.u[1]'],
215+
outlist=['sys2.y[0]', 'sys2.y[1]'])
216+
assert len(record) == 2
217+
assert str(record[0].message).startswith("Unused input")
218+
assert str(record[1].message).startswith("Unused output")
219+
220+
ref = \
221+
r"<LinearICSystem>: sys\[[\d]+\]" + "\n" + \
222+
r"Inputs \(2\): \['r1', 'r2'\]" + "\n" + \
223+
r"Outputs \(2\): \['y1', 'y2'\]" + "\n" + \
224+
r"States \(4\): \['sys1_x\[0\].*'sys2_x\[1\]'\]" + "\n" + \
225+
"\n" + \
226+
r"A = \[\[.*\]\]" + "\n\n" + \
227+
r"B = \[\[.*\]\]" + "\n\n" + \
228+
r"C = \[\[.*\]\]" + "\n\n" + \
229+
r"D = \[\[.*\]\]" + "\n" + \
230+
"\n" + \
231+
r"Subsystems \(2\):" + "\n" + \
232+
r" \* <StateSpace sys1: \[.*\] -> \['y\[0\]', 'y\[1\]']>" + "\n" + \
233+
r" \* <StateSpace sys2: \['u\[0\]', 'u\[1\]'] -> \[.*\]>" + "\n" + \
234+
"\n" + \
235+
r"Connections:" + "\n" + \
236+
r" \* sys1.u\[0\] <- -sys2.y\[0\] \+ sys2.y\[1\] \+ r1" + "\n" + \
237+
r" \* sys1.u\[1\] <- sys2.y\[0\] - sys2.y\[1\] \+ r2" + "\n" + \
238+
r" \* sys1.u\[2\] <-" + "\n" + \
239+
r" \* sys2.u\[0\] <- -sys1.y\[0\] \+ sys2.y\[0\]" + "\n" + \
240+
r" \* sys2.u\[1\] <- -2.0 \* sys1.y\[1\] - 2.0 \* sys2.y\[1\]" + \
241+
"\n\n" + \
242+
r"Outputs:" + "\n" + \
243+
r" \* y1 <- sys2.y\[0\]" + "\n" + \
244+
r" \* y2 <- sys2.y\[1\]"
245+
246+
assert re.match(ref, str(sys), re.DOTALL)

control/tests/statesp_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -779,7 +779,7 @@ def test_str(self, sys322):
779779
" [ 1. 4. 3.]]\n"
780780
"\n"
781781
"D = [[-2. 4.]\n"
782-
" [ 0. 1.]]\n")
782+
" [ 0. 1.]]")
783783
assert str(tsys) == tref
784784
tsysdtunspec = StateSpace(
785785
tsys.A, tsys.B, tsys.C, tsys.D, True, name=tsys.name)

0 commit comments

Comments
 (0)