Skip to content

Commit a44305b

Browse files
committed
BugFix: tf2ss now handles static gains
Check for static-gain (i.e., constant) transfer function matrix, and handle specially. Added unit test for a SISO and a MIMO plant.
1 parent 32f13bc commit a44305b

File tree

2 files changed

+62
-12
lines changed

2 files changed

+62
-12
lines changed

control/statesp.py

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
# Python 3 compatibility (needs to go here)
1212
from __future__ import print_function
13+
from __future__ import division # for _convertToStateSpace
1314

1415
"""Copyright (c) 2010 by California Institute of Technology
1516
All rights reserved.
@@ -638,6 +639,7 @@ def _convertToStateSpace(sys, **kw):
638639
"""
639640

640641
from .xferfcn import TransferFunction
642+
import itertools
641643
if isinstance(sys, StateSpace):
642644
if len(kw):
643645
raise TypeError("If sys is a StateSpace, _convertToStateSpace \
@@ -657,18 +659,45 @@ def _convertToStateSpace(sys, **kw):
657659
num, den = sys._common_den()
658660
# Make a list of the orders of the denominator polynomials.
659661
index = [len(den) - 1 for i in range(sys.outputs)]
660-
# Repeat the common denominator along the rows.
661-
den = array([den for i in range(sys.outputs)])
662-
#! TODO: transfer function to state space conversion is still buggy!
663-
#print num
664-
#print shape(num)
665-
ssout = td04ad('R',sys.inputs, sys.outputs, index, den, num,tol=0.0)
666-
667-
states = ssout[0]
668-
return StateSpace(ssout[1][:states, :states],
669-
ssout[2][:states, :sys.inputs],
670-
ssout[3][:sys.outputs, :states],
671-
ssout[4], sys.dt)
662+
663+
if index.count(0):
664+
# all TFM entries are static gains
665+
# if any entry in index is 0, td0ad raises:
666+
# ValueError: failed to create
667+
# intent(cache|hide)|optional array-- must have
668+
# defined dimensions but got (*,0,)
669+
# (where "*" varies according to the system size). This
670+
# suggests it's a problem in Slycot rather than
671+
# SLICOT.
672+
# Regardless, as of 2017-01-01 (commit 32f13bc), all
673+
# entries of index have the same value. The check
674+
# immediately below is in case this ever changes,
675+
# e.g., common denominator found per row or per column
676+
# (which seems like the right thing to do). If such a
677+
# change *IS* made, then we might have a row (or
678+
# column) of static gains, and either some SS assembly
679+
# gymnastics will be required here, or td0ad must be
680+
# fixed.
681+
if index.count(0) != len(index):
682+
raise RuntimeError("require all or no index entries to be 0")
683+
684+
D = empty((sys.outputs,sys.inputs),dtype=float)
685+
for i,j in itertools.product(range(sys.outputs),range(sys.inputs)):
686+
D[i,j] = sys.num[i][j][0] / sys.den[i][j][0]
687+
return StateSpace([], [], [], D, sys.dt)
688+
else:
689+
# Repeat the common denominator along the rows.
690+
den = array([den for i in range(sys.outputs)])
691+
#! TODO: transfer function to state space conversion is still buggy!
692+
#print num
693+
#print shape(num)
694+
ssout = td04ad('R',sys.inputs, sys.outputs, index, den, num,tol=0.0)
695+
696+
states = ssout[0]
697+
return StateSpace(ssout[1][:states, :states],
698+
ssout[2][:states, :sys.inputs],
699+
ssout[3][:sys.outputs, :states],
700+
ssout[4], sys.dt)
672701
except ImportError:
673702
# If slycot is not available, use signal.lti (SISO only)
674703
if (sys.inputs != 1 or sys.outputs != 1):

control/tests/convert_test.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,27 @@ def testConvertMIMO(self):
184184
if (not slycot_check()):
185185
self.assertRaises(TypeError, control.tf2ss, tfcn)
186186

187+
def testTf2ssStatic(self):
188+
"""Regression: tf2ss for static gains"""
189+
import control
190+
191+
gsiso = control.tf2ss(control.tf(23, 46))
192+
self.assertEqual(0, gsiso.states)
193+
self.assertEqual(1, gsiso.inputs)
194+
self.assertEqual(1, gsiso.outputs)
195+
# in all cases ratios are exactly representable, so assert_array_equal is fine
196+
np.testing.assert_array_equal([[0.5]], gsiso.D)
197+
198+
if slycot_check():
199+
# 2x3 TFM
200+
gmimo = control.tf2ss(control.tf([[ [23], [3], [5] ], [ [-1], [0.125], [101.3] ]],
201+
[[ [46], [0.1], [80] ], [ [2], [-0.1], [1] ]]))
202+
self.assertEqual(0, gmimo.states)
203+
self.assertEqual(3, gmimo.inputs)
204+
self.assertEqual(2, gmimo.outputs)
205+
d = np.matrix([[0.5, 30, 0.0625], [-0.5, -1.25, 101.3]])
206+
np.testing.assert_array_equal(d, gmimo.D)
207+
187208

188209
def suite():
189210
return unittest.TestLoader().loadTestsFromTestCase(TestConvert)

0 commit comments

Comments
 (0)