@@ -628,7 +628,7 @@ def nyquist_plot(syslist, omega=None, plot=True, omega_limits=None,
628628
629629 2. If a continuous-time system contains poles on or near the imaginary
630630 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
632632 right of stable poles and the left of unstable poles. If a pole is
633633 exactly on the imaginary axis, the `indent_direction` parameter can be
634634 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,
683683 if not hasattr (syslist , '__iter__' ):
684684 syslist = (syslist ,)
685685
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.
706691
707692 # Go through each system and keep track of the results
708693 counts , contours = [], []
@@ -734,9 +719,15 @@ def nyquist_plot(syslist, omega=None, plot=True, omega_limits=None,
734719 contour = 1j * omega_sys
735720
736721 # 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' :
739724 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 :]))
740731 for i , s in enumerate (contour ):
741732 # Find the nearest pole
742733 p = poles [(np .abs (poles - s )).argmin ()]
@@ -1242,17 +1233,15 @@ def _determine_omega_vector(syslist, omega_in, omega_limits, omega_num):
12421233 omega_in or through omega_limits. False if both omega_in
12431234 and omega_limits are None.
12441235 """
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
12481237
12491238 if omega_in is None :
12501239 if omega_limits is None :
1240+ omega_range_given = False
12511241 # Select a default range if none is provided
12521242 omega_out = _default_frequency_range (syslist ,
12531243 number_of_samples = omega_num )
12541244 else :
1255- omega_range_given = True
12561245 omega_limits = np .asarray (omega_limits )
12571246 if len (omega_limits ) != 2 :
12581247 raise ValueError ("len(omega_limits) must be 2" )
@@ -1267,11 +1256,12 @@ def _determine_omega_vector(syslist, omega_in, omega_limits, omega_num):
12671256# Compute reasonable defaults for axes
12681257def _default_frequency_range (syslist , Hz = None , number_of_samples = None ,
12691258 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.
12721260
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)
12751265
12761266 Parameters
12771267 ----------
@@ -1302,12 +1292,6 @@ def _default_frequency_range(syslist, Hz=None, number_of_samples=None,
13021292 >>> omega = _default_frequency_range(sys)
13031293
13041294 """
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-
13111295 # Set default values for options
13121296 number_of_samples = config ._get_param (
13131297 'freqplot' , 'number_of_samples' , number_of_samples )
@@ -1329,30 +1313,31 @@ def _default_frequency_range(syslist, Hz=None, number_of_samples=None,
13291313 features_ = np .concatenate ((np .abs (sys .pole ()),
13301314 np .abs (sys .zero ())))
13311315 # 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 ]
13341319 elif sys .isdtime (strict = True ):
13351320 fn = math .pi * 1. / sys .dt
13361321 # TODO: What distance to the Nyquist frequency is appropriate?
13371322 freq_interesting .append (fn * 0.9 )
13381323
13391324 features_ = np .concatenate ((sys .pole (),
13401325 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
13431328 # * 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 ]
13491334 # 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 ))
13521336 else :
13531337 # TODO
13541338 raise NotImplementedError (
13551339 "type of system in not implemented now" )
1340+ features = np .concatenate ((features , features_ ))
13561341 except NotImplementedError :
13571342 pass
13581343
0 commit comments