diff --git a/control/nlsys.py b/control/nlsys.py index 358c4b125..a14de1d9c 100644 --- a/control/nlsys.py +++ b/control/nlsys.py @@ -2086,10 +2086,8 @@ def interconnect( inplist : list of input connections, optional List of connections for how the inputs for the overall system are - mapped to the subsystem inputs. The input specification is similar to - the form defined in the connection specification, except that - connections do not specify an input-spec, since these are the system - inputs. The entries for a connection are thus of the form: + mapped to the subsystem inputs. The entries for a connection are + of the form: [input-spec1, input-spec2, ...] @@ -2102,11 +2100,10 @@ def interconnect( outlist : list of output connections, optional List of connections for how the outputs from the subsystems are - mapped to overall system outputs. The output connection - description is the same as the form defined in the inplist - specification (including the optional gain term). Numbered outputs - must be chosen from the list of subsystem outputs, but named - outputs can also be contained in the list of subsystem inputs. + mapped to overall system outputs. The entries for a connection are + of the form: + + [output-spec1, output-spec2, ...] If an output connection contains more than one signal specification, then those signals are added together (multiplying by the any gain @@ -2219,7 +2216,7 @@ def interconnect( ... inplist=['C'], outlist=['P']) A feedback system can also be constructed using the - :func:`~control.summing_block` function and the ability to + :func:`~control.summing_junction` function and the ability to automatically interconnect signals with the same names: >>> P = ct.tf(1, [1, 0], inputs='u', outputs='y') @@ -2425,15 +2422,22 @@ def interconnect( elif not found_system: raise ValueError("could not find signal %s" % sname) else: - # Regular signal specification - if not isinstance(connection, list): - dprint(f" converting item to list") - connection = [connection] - for spec in connection: - isys, indices, gain = _parse_spec(syslist, spec, 'input') + if isinstance(connection, list): + # Passed a list => create input map + dprint(f" detected input list") + signal_list = [] + for spec in connection: + isys, indices, gain = _parse_spec(syslist, spec, 'input') + for isig in indices: + signal_list.append((isys, isig, gain)) + dprint(f" adding input {(isys, isig, gain)} to list") + new_inplist.append(signal_list) + else: + # Passed a single signal name => add individual input(s) + isys, indices, gain = _parse_spec(syslist, connection, 'input') for isig in indices: - dprint(f" adding input {(isys, isig, gain)}") new_inplist.append((isys, isig, gain)) + dprint(f" adding input {(isys, isig, gain)}") inplist, inputs = new_inplist, new_inputs dprint(f" {inplist=}\n {inputs=}") @@ -2499,17 +2503,15 @@ def interconnect( elif not found_system: raise ValueError("could not find signal %s" % sname) else: - # Regular signal specification - if not isinstance(connection, list): - dprint(f" converting item to list") - connection = [connection] - for spec in connection: + # Utility function to find named output or input signal + def _find_output_or_input_signal(spec): + signal_list = [] try: # First trying looking in the output signals osys, indices, gain = _parse_spec(syslist, spec, 'output') for osig in indices: dprint(f" adding output {(osys, osig, gain)}") - new_outlist.append((osys, osig, gain)) + signal_list.append((osys, osig, gain)) except ValueError: # If not, see if we can find it in inputs isys, indices, gain = _parse_spec( @@ -2518,9 +2520,21 @@ def interconnect( for isig in indices: # Use string form to allow searching input list dprint(f" adding input {(isys, isig, gain)}") - new_outlist.append( + signal_list.append( (syslist[isys].name, syslist[isys].input_labels[isig], gain)) + return signal_list + + if isinstance(connection, list): + # Passed a list => create input map + dprint(f" detected output list") + signal_list = [] + for spec in connection: + signal_list += _find_output_or_input_signal(spec) + new_outlist.append(signal_list) + else: + new_outlist += _find_output_or_input_signal(connection) + outlist, outputs = new_outlist, new_outputs dprint(f" {outlist=}\n {outputs=}") diff --git a/control/tests/interconnect_test.py b/control/tests/interconnect_test.py index f4b0c59a8..604488ca5 100644 --- a/control/tests/interconnect_test.py +++ b/control/tests/interconnect_test.py @@ -689,3 +689,19 @@ def test_interconnect_params(): timepts = np.linspace(0, 10) resp = ct.input_output_response(sys, timepts, 0, params={'a': -1}) assert resp.states[0, -1].item() < 2 * math.exp(-10) + + +# Bug identified in issue #1015 +def test_parallel_interconnect(): + sys1 = ct.rss(2, 1, 1, name='S1') + sys2 = ct.rss(2, 1, 1, name='S2') + + sys_bd = sys1 + sys2 + sys_ic = ct.interconnect( + [sys1, sys2], + inplist=[['S1.u[0]', 'S2.u[0]']], + outlist=[['S1.y[0]', 'S2.y[0]']]) + np.testing.assert_allclose(sys_bd.A, sys_ic.A) + np.testing.assert_allclose(sys_bd.B, sys_ic.B) + np.testing.assert_allclose(sys_bd.C, sys_ic.C) + np.testing.assert_allclose(sys_bd.D, sys_ic.D)