@@ -723,35 +723,72 @@ def nyquist_plot(syslist, omega=None, plot=True, omega_limits=None,
723
723
and indent_direction != 'none' :
724
724
if sys .isctime ():
725
725
splane_poles = sys .poles ()
726
+ splane_cl_poles = sys .feedback ().poles ()
726
727
else :
727
728
# map z-plane poles to s-plane, ignoring any at the origin
728
729
# because we don't need to indent for them
729
730
zplane_poles = sys .poles ()
730
731
zplane_poles = zplane_poles [~ np .isclose (abs (zplane_poles ), 0. )]
731
732
splane_poles = np .log (zplane_poles )/ sys .dt
732
733
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
+
8000
"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
733
762
if splane_contour [1 ].imag > indent_radius \
734
763
and np .any (np .isclose (abs (splane_poles ), 0 )) \
735
764
and not omega_range_given :
736
765
# add some points for quarter circle around poles at origin
766
+ # (these will get indented left or right below)
737
767
splane_contour = np .concatenate (
738
768
(1j * np .linspace (0. , indent_radius , 50 ),
739
769
splane_contour [1 :]))
770
+
740
771
for i , s in enumerate (splane_contour ):
741
772
# Find the nearest pole
742
773
p = splane_poles [(np .abs (splane_poles - s )).argmin ()]
774
+
743
775
# See if we need to indent around it
744
776
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
745
782
if p .real < 0 or (np .isclose (p .real , 0 ) \
746
783
and indent_direction == 'right' ):
747
784
# Indent to the right
748
- splane_contour [i ] += \
749
- np . sqrt ( indent_radius ** 2 - ( s - p ). imag ** 2 )
785
+ splane_contour [i ] += offset
786
+
750
787
elif p .real > 0 or (np .isclose (p .real , 0 ) \
751
788
and indent_direction == 'left' ):
752
789
# Indent to the left
753
- splane_contour [i ] -= \
754
- np . sqrt ( indent_radius ** 2 - ( s - p ). imag ** 2 )
790
+ splane_contour [i ] -= offset
791
+
755
792
else :
756
793
ValueError ("unknown value for indent_direction" )
757
794
0 commit comments