@@ -522,7 +522,7 @@ def gen_zero_centered_series(val_min, val_max, period):
522522 'nyquist.mirror_style' : '--' ,
523523 'nyquist.arrows' : 2 ,
524524 'nyquist.arrow_size' : 8 ,
525- 'nyquist.indent_radius' : 1e-2 ,
525+ 'nyquist.indent_radius' : 1e-6 ,
526526 'nyquist.maximum_magnitude' : 5 ,
527527 'nyquist.indent_direction' : 'right' ,
528528}
@@ -552,7 +552,9 @@ def nyquist_plot(syslist, omega=None, plot=True, omega_limits=None,
552552 If True, plot magnitude
553553
554554 omega : array_like
555- Set of frequencies to be evaluated, in rad/sec.
555+ Set of frequencies to be evaluated, in rad/sec. Remark: if omega is
556+ specified, you may need to specify specify a larger `indent_radius`
557+ than the default to get reasonable contours.
556558
557559 omega_limits : array_like of two values
558560 Limits to the range of frequencies. Ignored if omega is provided, and
@@ -725,7 +727,7 @@ def nyquist_plot(syslist, omega=None, plot=True, omega_limits=None,
725727 # do indentations in s-plane where it is more convenient
726728 splane_contour = 1j * omega_sys
727729
728- # Bend the contour around any poles on/near the imaginary axis
730+ # indent the contour around any poles on/near the imaginary axis
729731 if isinstance (sys , (StateSpace , TransferFunction )) \
730732 and indent_direction != 'none' :
731733 if sys .isctime ():
@@ -738,30 +740,49 @@ def nyquist_plot(syslist, omega=None, plot=True, omega_limits=None,
738740 zplane_poles = zplane_poles [~ np .isclose (abs (zplane_poles ), 0. )]
739741 splane_poles = np .log (zplane_poles )/ sys .dt
740742
741- if splane_contour [1 ].imag > indent_radius \
742- and np .any (np .isclose (abs (splane_poles ), 0 )) \
743- and not omega_range_given :
744- # add some points for quarter circle around poles at origin
745- splane_contour = np .concatenate (
746- (1j * np .linspace (0. , indent_radius , 50 ),
747- splane_contour [1 :]))
748- for i , s in enumerate (splane_contour ):
749- # Find the nearest pole
750- p = splane_poles [(np .abs (splane_poles - s )).argmin ()]
751- # See if we need to indent around it
752- if abs (s - p ) < indent_radius :
753- if p .real < 0 or (np .isclose (p .real , 0 ) \
754- and indent_direction == 'right' ):
755- # Indent to the right
756- splane_contour [i ] += \
757- np .sqrt (indent_radius ** 2 - (s - p ).imag ** 2 )
758- elif p .real > 0 or (np .isclose (p .real , 0 ) \
759- and indent_direction == 'left' ):
760- # Indent to the left
761- splane_contour [i ] -= \
762- np .sqrt (indent_radius ** 2 - (s - p ).imag ** 2 )
763- else :
764- ValueError ("unknown value for indent_direction" )
743+ if omega_range_given :
744+ # indent given contour
745+ for i , s in enumerate (splane_contour ):
746+ # Find the nearest pole
747+ p = splane_poles [(np .abs (splane_poles - s )).argmin ()]
748+ # See if we need to indent around it
749+ if abs (s - p ) < indent_radius :
750+ if p .real < 0 or (np .isclose (p .real , 0 ) \
751+ and indent_direction == 'right' ):
752+ # Indent to the right
753+ splane_contour [i ] += \
754+ np .sqrt (indent_radius ** 2 - (s - p ).imag ** 2 )
755+ elif p .real > 0 or (np .isclose (p .real , 0 ) \
756+ and indent_direction == 'left' ):
757+ # Indent to the left
758+ splane_contour [i ] -= \
759+ np .sqrt (indent_radius ** 2 - (s - p ).imag ** 2 )
760+ else :
761+ ValueError ("unknown value for indent_direction" )
762+ else :
763+ # find poles that are near imag axis and replace contour
764+ # near there with indented contour
765+ if indent_direction == 'right' :
766+ indent = indent_radius * \
767+ np .exp (1j * np .linspace (- np .pi / 2 , np .pi / 2 , 51 ))
768+ elif indent_direction == 'left' :
769+ indent = - indent_radius * \
770+ np .exp (1j * np .linspace (np .pi / 2 , - np .pi / 2 , 51 ))
771+ else :
772+ ValueError ("unknown value for indent_direction" )
773+ for p in splane_poles [np .isclose (splane_poles .real , 0 ) \
774+ & (splane_poles .imag >= 0 )]:
775+ start = np .searchsorted (
776+ splane_contour .imag , p .imag - indent_radius )
777+ end = np .searchsorted (
778+ splane_contour .imag , p .imag + indent_radius )
779+ # only keep indent parts that are > 0 and within contour
780+ indent_mask = ((indent + p ).imag >= 0 ) \
781+ & ((indent + p ).imag <= splane_contour [- 1 ].imag )
782+ splane_contour = np .concatenate ((
783+ splane_contour [:start ],
784+ indent [indent_mask ] + p ,
785+ splane_contour [end :]))
765786
766787 # map contour to z-plane if necessary
767788 if sys .isctime ():
@@ -816,7 +837,7 @@ def nyquist_plot(syslist, omega=None, plot=True, omega_limits=None,
816837 _add_arrows_to_line2D (
817838 ax , p [0 ], arrow_pos , arrowstyle = arrow_style , dir = 1 )
818839 # plot large magnitude contour
819- p = plt .plot (x_lg , y_lg , ':' , color = 'red' , * args , ** kwargs )
840+ p = plt .plot (x_lg , y_lg , color = 'red' , * args , ** kwargs )
820841 _add_arrows_to_line2D (
821842 ax , p [0 ], arrow_pos , arrowstyle = arrow_style , dir = 1 )
822843
@@ -825,7 +846,8 @@ def nyquist_plot(syslist, omega=None, plot=True, omega_limits=None,
825846 p = plt .plot (x , - y , mirror_style , color = c , * args , ** kwargs )
826847 _add_arrows_to_line2D (
827848 ax , p [0 ], arrow_pos , arrowstyle = arrow_style , dir = - 1 )
828- p = plt .plot (x_lg , - y_lg , ':' , color = 'red' , * args , ** kwargs )
849+ p = plt .plot (x_lg , - y_lg , mirror_style ,
850+ color = 'red' , * args , ** kwargs )
829851 _add_arrows_to_line2D (
830852 ax , p [0 ], arrow_pos , arrowstyle = arrow_style , dir = - 1 )
831853
0 commit comments