@@ -628,7 +628,7 @@ def nyquist_plot(syslist, omega=None, plot=True, omega_limits=None,
628
628
629
629
2. If a continuous-time system contains poles on or near the imaginary
630
630
axis, a small indentation will be used to avoid the pole. The radius
631
- of the indentation is given by `indent_radius` and it is taken the the
631
+ of the indentation is given by `indent_radius` and it is taken to the
632
632
right of stable poles and the left of unstable poles. If a pole is
633
633
exactly on the imaginary axis, the `indent_direction` parameter can be
634
634
used to set the direction of indentation. Setting `indent_direction`
@@ -683,26 +683,11 @@ def nyquist_plot(syslist, omega=None, plot=True, omega_limits=None,
683
683
if not hasattr (syslist , '__iter__' ):
684
684
syslist = (syslist ,)
685
685
686
- # Decide whether to go above Nyquist frequency
687
- omega_range_given = True if omega is not None else False
688
-
689
- # Figure out the frequency limits
690
- if omega is None :
691
- if omega_limits is None :
692
- # Select a default range if none is provided
693
- omega = _default_frequency_range (
694
- syslist , number_of_samples = omega_num )
695
-
696
- # Replace first point with the origin
697
- omega [0 ] = 0
698
- else :
699
- omega_range_given = True
700
- omega_limits = np .asarray (omega_limits )
701
- if len (omega_limits ) != 2 :
702
- raise ValueError ("len(omega_limits) must be 2" )
703
- omega = np .logspace (np .log10 (omega_limits [0 ]),
704
- np .log10 (omega_limits [1 ]), num = omega_num ,
705
- endpoint = True )
686
+ omega , omega_range_given = _determine_omega_vector (
687
+ syslist , omega , omega_limits , omega_num )
688
+ if not omega_range_given :
689
+ # Start contour at zero frequency
690
+ omega [0 ] = 0.
706
691
707
692
# Go through each system and keep track of the results
708
693
counts , contours = [], []
@@ -734,9 +719,15 @@ def nyquist_plot(syslist, omega=None, plot=True, omega_limits=None,
734
719
contour = 1j * omega_sys
735
720
736
721
# Bend the contour around any poles on/near the imaginary axis
737
- if isinstance (sys , (StateSpace , TransferFunction )) and \
738
- sys .isctime () and indent_direction != 'none' :
722
+ if isinstance (sys , (StateSpace , TransferFunction )) \
723
+ and sys .isctime () and indent_direction != 'none' :
739
724
poles = sys .pole ()
725
+ if contour [1 ].imag > indent_radius \
726
+ and 0. in poles and not omega_range_given :
727
+ # add some points for quarter circle around poles at origin
728
+ contour = np .concatenate (
729
+ (1j * np .linspace (0. , indent_radius , 50 ),
730
+ contour [1 :]))
740
731
for i , s in enumerate (contour ):
741
732
# Find the nearest pole
742
733
p = poles [(np .abs (poles - s )).argmin ()]
@@ -1242,17 +1233,15 @@ def _determine_omega_vector(syslist, omega_in, omega_limits, omega_num):
1242
1233
omega_in or through omega_limits. False if both omega_in
1243
1234
and omega_limits are None.
1244
1235
"""
1245
-
1246
- # Decide whether to go above Nyquist frequency
1247
- omega_range_given = True if omega_in is not None else False
1236
+ omega_range_given = True
1248
1237
1249
1238
if omega_in is None :
1250
1239
if omega_limits is None :
1240
+ omega_range_given = False
1251
1241
# Select a default range if none is provided
1252
1242
omega_out = _default_frequency_range (syslist ,
1253
1243
number_of_samples = omega_num )
1254
1244
else :
1255
- omega_range_given = True
1256
1245
omega_limits = np .asarray (omega_limits )
1257
1246
if len (omega_limits ) != 2 :
1258
1247
raise ValueError ("len(omega_limits) must be 2" )
@@ -1267,11 +1256,12 @@ def _determine_omega_vector(syslist, omega_in, omega_limits, omega_num):
1267
1256
# Compute reasonable defaults for axes
1268
1257
def _default_frequency_range (syslist , Hz = None , number_of_samples = None ,
1269
1258
feature_periphery_decades = None ):
1270
- """Compute a reasonable default frequency range for frequency
1271
- domain plots.
1259
+ """Compute a default frequency range for frequency domain plots.
1272
1260
1273
- Finds a reasonable default frequency range by examining the features
1274
- (poles and zeros) of the systems in syslist.
1261
+ This code looks at the poles and zeros of all of the systems that
1262
+ we are plotting and sets the frequency range to be one decade above
1263
+ and below the min and max feature frequencies, rounded to the nearest
1264
+ integer. If no features are found, it returns logspace(-1, 1)
1275
1265
1276
1266
Parameters
1277
1267
----------
@@ -1302,12 +1292,6 @@ def _default_frequency_range(syslist, Hz=None, number_of_samples=None,
1302
1292
>>> omega = _default_frequency_range(sys)
1303
1293
1304
1294
"""
1305
- # This code looks at the poles and zeros of all of the systems that
1306
- # we are plotting and sets the frequency range to be one decade above
1307
- # and below the min and max feature frequencies, rounded to the nearest
1308
- # integer. It excludes poles and zeros at the origin. If no features
1309
- # are found, it turns logspace(-1, 1)
1310
-
1311
1295
# Set default values for options
1312
1296
number_of_samples = config ._get_param (
1313
1297
'freqplot' , 'number_of_samples' , number_of_samples )
@@ -1329,30 +1313,31 @@ def _default_frequency_range(syslist, Hz=None, number_of_samples=None,
1329
1313
features_ = np .concatenate ((np .abs (sys .pole ()),
1330
1314
np .abs (sys .zero ())))
1331
1315
# Get rid of poles and zeros at the origin
1332
- features_ = features_ [features_ != 0.0 ]
1333
- features = np .concatenate ((features , features_ ))
1316
+ toreplace = features_ == 0.0
1317
+ if np .any (toreplace ):
1318
+ features_ = features_ [~ toreplace ]
1334
1319
elif sys .isdtime (strict = True ):
1335
1320
fn = math .pi * 1. / sys .dt
1336
1321
# TODO: What distance to the Nyquist frequency is appropriate?
1337
1322
freq_interesting .append (fn * 0.9 )
1338
1323
1339
1324
features_ = np .concatenate ((sys .pole (),
1340
1325
sys .zero ()))
1341
- # Get rid of poles and zeros
1342
- # * at the origin and real <= 0 & imag==0: log!
1326
+ # Get rid of poles and zeros on the real axis (imag==0)
1327
+ # * origin and real < 0
1343
1328
# * at 1.: would result in omega=0. (logaritmic plot!)
1344
- features_ = features_ [
1345
- ( features_ . imag != 0.0 ) | (features_ .real > 0. )]
1346
- features_ = features_ [
1347
- np .bitwise_not (( features_ . imag == 0.0 ) &
1348
- ( np . abs ( features_ . real - 1.0 ) < 1.e-10 )) ]
1329
+ toreplace = ( features_ . imag == 0.0 ) & (
1330
+ (features_ .real <= 0. ) |
1331
+ ( np . abs ( features_ . real - 1.0 ) < 1.e-10 ))
1332
+ if np .any ( toreplace ):
1333
+ features_ = features_ [ ~ toreplace ]
1349
1334
# TODO: improve
1350
- features__ = np .abs (np .log (features_ ) / (1.j * sys .dt ))
1351
- features = np .concatenate ((features , features__ ))
1335
+ features_ = np .abs (np .log (features_ ) / (1.j * sys .dt ))
1352
1336
else :
1353
1337
# TODO
1354
1338
raise NotImplementedError (
1355
1339
"type of system in not implemented now" )
1340
+ features = np .concatenate ((features , features_ ))
1356
1341
except NotImplementedError :
1357
1342
pass
1358
1343
0 commit comments