@@ -723,35 +723,72 @@ def nyquist_plot(syslist, omega=None, plot=True, omega_limits=None,
723723 and indent_direction != 'none' :
724724 if sys .isctime ():
725725 splane_poles = sys .poles ()
726+ splane_cl_poles = sys .feedback ().poles ()
726727 else :
727728 # map z-plane poles to s-plane, ignoring any at the origin
728729 # because we don't need to indent for them
729730 zplane_poles = sys .poles ()
730731 zplane_poles = zplane_poles [~ np .isclose (abs (zplane_poles ), 0. )]
731732 splane_poles = np .log (zplane_poles )/ sys .dt
732733
734+ zplane_cl_poles = sys .feedback ().poles ()
735+ zplane_cl_poles = zplane_cl_poles [
736+ ~ np .isclose (abs (zplane_poles ), 0. )]
737+ splane_cl_poles = np .log (zplane_cl_poles )/ sys .dt
738+
739+ #
740+ # Check to make sure indent radius is small enough
741+ #
742+ # If there is a closed loop pole that is near the imaginary access
743+ # at a point that is near an open loop pole, it is possible that
744+ # indentation might skip or create an extraneous encirclement.
745+ # We check for that situation here and generate a warning if that
746+ # could happen.
747+ #
748+ for p_cl in splane_cl_poles :
749+ # See if any closed loop poles are near the imaginary axis
750+ if abs (p_cl .real ) <= indent_radius :
751+ # See if any open loop poles are close to closed loop poles
752+ p_ol = splane_poles [
753+ (np .abs (splane_poles - p_cl )).argmin ()]
754+
755+ if abs (p_ol - p_cl ) <= indent_radius :
756+ warnings .warn (
757+ "indented contour may miss closed loop pole; "
758+ "consider reducing indent_radius to be less than "
759+ f"{ abs (p_ol - p_cl ):5.2g} " , stacklevel = 2 )
760+
761+ # See if we should add some frequency points near the origin
733762 if splane_contour [1 ].imag > indent_radius \
734763 and np .any (np .isclose (abs (splane_poles ), 0 )) \
735764 and not omega_range_given :
736765 # add some points for quarter circle around poles at origin
766+ # (these will get indented left or right below)
737767 splane_contour = np .concatenate (
738768 (1j * np .linspace (0. , indent_radius , 50 ),
739769 splane_contour [1 :]))
770+
740771 for i , s in enumerate (splane_contour ):
741772 # Find the nearest pole
742773 p = splane_poles [(np .abs (splane_poles - s )).argmin ()]
774+
743775 # See if we need to indent around it
744776 if abs (s - p ) < indent_radius :
777+ # Figure out how much to offset (simple trigonometry)
778+ offset = np .sqrt (indent_radius ** 2 - (s - p ).imag ** 2 ) \
779+ - (s - p ).real
780+
781+ # Figure out which way to offset the contour point
745782 if p .real < 0 or (np .isclose (p .real , 0 ) \
746783 and indent_direction == 'right' ):
747784 # Indent to the right
748- splane_contour [i ] += \
749- np . sqrt ( indent_radius ** 2 - ( s - p ). imag ** 2 )
785+ splane_contour [i ] += offset
786+
750787 elif p .real > 0 or (np .isclose (p .real , 0 ) \
751788 and indent_direction == 'left' ):
752789 # Indent to the left
753- splane_contour [i ] -= \
754- np . sqrt ( indent_radius ** 2 - ( s - p ). imag ** 2 )
790+ splane_contour [i ] -= offset
791+
755792 else :
756793 ValueError ("unknown value for indent_direction" )
757794
0 commit comments