Skip to content

Commit a09d059

Browse files
authored
handle non proper tf in _common_den() (#370)
1 parent 874d52e commit a09d059

File tree

2 files changed

+54
-2
lines changed

2 files changed

+54
-2
lines changed

control/tests/xferfcn_test.py

+28
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,26 @@ def test_common_den(self):
546546
np.zeros((3, 5, 6)))
547547
np.testing.assert_array_almost_equal(den, denref)
548548

549+
def test_common_den_nonproper(self):
550+
""" Test _common_den with order(num)>order(den) """
551+
552+
tf1 = TransferFunction(
553+
[[[1., 2., 3.]], [[1., 2.]]],
554+
[[[1., -2.]], [[1., -3.]]])
555+
tf2 = TransferFunction(
556+
[[[1., 2.]], [[1., 2., 3.]]],
557+
[[[1., -2.]], [[1., -3.]]])
558+
559+
common_den_ref = np.array([[1., -5., 6.]])
560+
561+
np.testing.assert_raises(ValueError, tf1._common_den)
562+
np.testing.assert_raises(ValueError, tf2._common_den)
563+
564+
_, den1, _ = tf1._common_den(allow_nonproper=True)
565+
np.testing.assert_array_almost_equal(den1, common_den_ref)
566+
_, den2, _ = tf2._common_den(allow_nonproper=True)
567+
np.testing.assert_array_almost_equal(den2, common_den_ref)
568+
549569
@unittest.skipIf(not slycot_check(), "slycot not installed")
550570
def test_pole_mimo(self):
551571
"""Test for correct MIMO poles."""
@@ -557,6 +577,14 @@ def test_pole_mimo(self):
557577

558578
np.testing.assert_array_almost_equal(p, [-2., -2., -7., -3., -2.])
559579

580+
# non proper transfer function
581+
sys2 = TransferFunction(
582+
[[[1., 2., 3., 4.], [1.]], [[1.], [1.]]],
583+
[[[1., 2.], [1., 3.]], [[1., 4., 4.], [1., 9., 14.]]])
584+
p2 = sys2.pole()
585+
586+
np.testing.assert_array_almost_equal(p2, [-2., -2., -7., -3., -2.])
587+
560588
def test_double_cancelling_poles_siso(self):
561589

562590
H = TransferFunction([1, 1], [1, 2, 1])

control/xferfcn.py

+26-2
Original file line numberDiff line numberDiff line change
@@ -679,7 +679,7 @@ def freqresp(self, omega):
679679

680680
def pole(self):
681681
"""Compute the poles of a transfer function."""
682-
num, den, denorder = self._common_den()
682+
_, den, denorder = self._common_den(allow_nonproper=True)
683683
rts = []
684684
for d, o in zip(den, denorder):
685685
rts.extend(roots(d[:o + 1]))
@@ -797,7 +797,7 @@ def returnScipySignalLTI(self):
797797

798798
return out
799799

800-
def _common_den(self, imag_tol=None):
800+
def _common_den(self, imag_tol=None, allow_nonproper=False):
801801
"""
802802
Compute MIMO common denominators; return them and adjusted numerators.
803803
@@ -813,6 +813,9 @@ def _common_den(self, imag_tol=None):
813813
Threshold for the imaginary part of a root to use in detecting
814814
complex poles
815815
816+
allow_nonproper : boolean
817+
Do not enforce proper transfer functions
818+
816819
Returns
817820
-------
818821
num: array
@@ -822,6 +825,8 @@ def _common_den(self, imag_tol=None):
822825
gives the numerator coefficient array for the ith output and jth
823826
input; padded for use in td04ad ('C' option); matches the
824827
denorder order; highest coefficient starts on the left.
828+
If allow_nonproper=True and the order of a numerator exceeds the
829+
order of the common denominator, num will be returned as None
825830
826831
den: array
827832
sys.inputs by kd
@@ -906,6 +911,8 @@ def _common_den(self, imag_tol=None):
906911
dtype=float)
907912
denorder = zeros((self.inputs,), dtype=int)
908913

914+
havenonproper = False
915+
909916
for j in range(self.inputs):
910917
if not len(poles[j]):
911918
# no poles matching this input; only one or more gains
@@ -930,11 +937,28 @@ def _common_den(self, imag_tol=None):
930937
nwzeros.append(poles[j][ip])
931938

932939
numpoly = poleset[i][j][2] * np.atleast_1d(poly(nwzeros))
940+
941+
# td04ad expects a proper transfer function. If the
942+
# numerater has a higher order than the denominator, the
943+
# padding will fail
944+
if len(numpoly) > maxindex + 1:
945+
if allow_nonproper:
946+
havenonproper = True
947+
break
948+
raise ValueError(
949+
self.__str__() +
950+
"is not a proper transfer function. "
951+
"The degree of the numerators must not exceed "
952+
"the degree of the denominators.")
953+
933954
# numerator polynomial should be padded on left and right
934955
# ending at maxindex to line up with what td04ad expects.
935956
num[i, j, maxindex+1-len(numpoly):maxindex+1] = numpoly
936957
# print(num[i, j])
937958

959+
if havenonproper:
960+
num = None
961+
938962
return num, den, denorder
939963

940964
def sample(self, Ts, method='zoh', alpha=None):

0 commit comments

Comments
 (0)