57
57
polyadd , polymul , polyval , roots , sqrt , zeros , squeeze , exp , pi , \
58
58
where , delete , real , poly , nonzero
59
59
import scipy as sp
60
- from numpy .polynomial .polynomial import polyfromroots
61
60
from scipy .signal import lti , tf2zpk , zpk2tf , cont2discrete
62
61
from copy import deepcopy
63
62
from warnings import warn
@@ -803,8 +802,6 @@ def _common_den(self, imag_tol=None):
803
802
output numerator array num is modified to use the common
804
803
denominator for this input/column; the coefficient arrays are also
805
804
padded with zeros to be the same size for all num/den.
806
- num is an sys.outputs by sys.inputs
807
- by len(d) array.
808
805
809
806
Parameters
810
807
----------
@@ -815,17 +812,20 @@ def _common_den(self, imag_tol=None):
815
812
Returns
816
813
-------
817
814
num: array
818
- Multi-dimensional array of numerator coefficients. num[i][j]
819
- gives the numerator coefficient array for the ith input and jth
820
- output, also prepared for use in td04ad; matches the denorder
821
- order; highest coefficient starts on the left.
815
+ n by n by kd where n = max(sys.outputs,sys.inputs)
816
+ kd = max(denorder)+1
817
+ Multi-dimensional array of numerator coefficients. num[i,j]
818
+ gives the numerator coefficient array for the ith output and jth
819
+ input; padded for use in td04ad ('C' option); matches the
820
+ denorder order; highest coefficient starts on the left.
822
821
823
822
den: array
823
+ sys.inputs by kd
824
824
Multi-dimensional array of coefficients for common denominator
825
825
polynomial, one row per input. The array is prepared for use in
826
826
slycot td04ad, the first element is the highest-order polynomial
827
- coefficiend of s, matching the order in denorder, if denorder <
828
- number of columns in den, the den is padded with zeros
827
+ coefficient of s, matching the order in denorder. If denorder <
828
+ number of columns in den, the den is padded with zeros.
829
829
830
830
denorder: array of int, orders of den, one per input
831
831
@@ -839,16 +839,18 @@ def _common_den(self, imag_tol=None):
839
839
840
840
# Machine precision for floats.
841
841
eps = finfo (float ).eps
842
+ real_tol = sqrt (eps * self .inputs * self .outputs )
842
843
843
- # Decide on the tolerance to use in deciding if a pole is complex
844
+ # The tolerance to use in deciding if a pole is complex
844
845
if (imag_tol is None ):
845
- imag_tol = 1e-8 # TODO: figure out the right number to use
846
+ imag_tol = 2 * real_tol
846
847
847
848
# A list to keep track of cumulative poles found as we scan
848
849
# self.den[..][..]
849
850
poles = [[] for j in range (self .inputs )]
850
851
851
852
# RvP, new implementation 180526, issue #194
853
+ # BG, modification, issue #343, PR #354
852
854
853
855
# pre-calculate the poles for all num, den
854
856
# has zeros, poles, gain, list for pole indices not in den,
@@ -867,31 +869,34 @@ def _common_den(self, imag_tol=None):
867
869
poleset [- 1 ].append ([z , p , k , [], 0 ])
868
870
869
871
# collect all individual poles
870
- epsnm = eps * self .inputs * self .outputs
871
872
for j in range (self .inputs ):
872
873
for i in range (self .outputs ):
873
874
currentpoles = poleset [i ][j ][1 ]
874
875
nothave = ones (currentpoles .shape , dtype = bool )
875
876
for ip , p in enumerate (poles [j ]):
876
- idx , = nonzero (
877
- (abs (currentpoles - p ) < sqrt (epsnm )) * nothave )
878
- if len (idx ):
879
- nothave [idx [0 ]] = False
877
+ collect = ((abs (currentpoles .real - p .real ) < real_tol ) &
878
+ (abs (currentpoles .imag - p .imag ) < imag_tol ) &
879
+ nothave )
880
+ if np .any (collect ):
881
+ # mark first found pole as already collected
882
+ nothave [nonzero (collect )[0 ][0 ]] = False
880
883
else :
881
884
# remember id of pole not in tf
882
885
poleset [i ][j ][3 ].append (ip )
883
886
for h , c in zip (nothave , currentpoles ):
884
887
if h :
888
+ if abs (c .imag ) < imag_tol :
889
+ c = c .real
885
890
poles [j ].append (c )
886
891
# remember how many poles now known
887
892
poleset [i ][j ][4 ] = len (poles [j ])
888
893
889
894
# figure out maximum number of poles, for sizing the den
890
- npmax = max ([len (p ) for p in poles ])
891
- den = zeros ((self .inputs , npmax + 1 ), dtype = float )
895
+ maxindex = max ([len (p ) for p in poles ])
896
+ den = zeros ((self .inputs , maxindex + 1 ), dtype = float )
892
897
num = zeros ((max (1 , self .outputs , self .inputs ),
893
898
max (1 , self .outputs , self .inputs ),
894
- npmax + 1 ),
899
+ maxindex + 1 ),
895
900
dtype = float )
896
901
denorder = zeros ((self .inputs ,), dtype = int )
897
902
@@ -903,18 +908,10 @@ def _common_den(self, imag_tol=None):
903
908
num [i , j , 0 ] = poleset [i ][j ][2 ]
904
909
else :
905
910
# create the denominator matching this input
906
- # polyfromroots gives coeffs in opposite order from what we use
907
- # coefficients should be padded on right, ending at np
908
- np = len (poles [j ])
909
- denpoly = polyfromroots (poles [j ])
910
- if (abs (denpoly .imag ) > imag_tol ).any ():
911
- warn ("The denominator of the transfer function "
912
- "for input {j} to output {i} has a nontrivial "
913
- "imaginary part of {imag}, which was removed" .format (
914
- i = i , j = j , imag = max (denpoly .imag )))
915
- denpoly = denpoly .real
916
- den [j , np ::- 1 ] = denpoly
917
- denorder [j ] = np
911
+ # coefficients should be padded on right, ending at maxindex
912
+ maxindex = len (poles [j ])
913
+ den [j , :maxindex + 1 ] = poly (poles [j ])
914
+ denorder [j ] = maxindex
918
915
919
916
# now create the numerator, also padded on the right
920
917
for i in range (self .outputs ):
@@ -923,20 +920,17 @@ def _common_den(self, imag_tol=None):
923
920
# add all poles not found in the original denominator,
924
921
# and the ones later added from other denominators
925
922
for ip in chain (poleset [i ][j ][3 ],
926
- range (poleset [i ][j ][4 ], np )):
923
+ range (poleset [i ][j ][4 ], maxindex )):
927
924
nwzeros .append (poles [j ][ip ])
928
925
929
- numpoly = poleset [i ][j ][2 ] * polyfromroots (nwzeros ).real
930
- # print(numpoly, den[j])
931
- # polyfromroots gives coeffs in opposite order => invert
926
+ numpoly = poleset [i ][j ][2 ] * np .atleast_1d (poly (nwzeros ))
932
927
# numerator polynomial should be padded on left and right
933
- # ending at np to line up with what td04ad expects.. .
934
- num [i , j , np + 1 - len (numpoly ):np + 1 ] = numpoly [:: - 1 ]
928
+ # ending at maxindex to line up with what td04ad expects.
929
+ num [i , j , maxindex + 1 - len (numpoly ):maxindex + 1 ] = numpoly
935
930
# print(num[i, j])
936
931
937
932
return num , den , denorder
938
933
939
-
940
934
def sample (self , Ts , method = 'zoh' , alpha = None ):
941
935
"""Convert a continuous-time system to discrete time
942
936
0 commit comments