@@ -1100,13 +1100,14 @@ def gen_zero_centered_series(val_min, val_max, period):
1100
1100
_nyquist_defaults = {
1101
1101
'nyquist.primary_style' : ['-' , '-.' ], # style for primary curve
1102
1102
'nyquist.mirror_style' : ['--' , ':' ], # style for mirror curve
1103
- 'nyquist.arrows' : 2 , # number of arrows around curve
1103
+ 'nyquist.arrows' : 3 , # number of arrows around curve
1104
1104
'nyquist.arrow_size' : 8 , # pixel size for arrows
1105
1105
'nyquist.encirclement_threshold' : 0.05 , # warning threshold
1106
1106
'nyquist.indent_radius' : 1e-4 , # indentation radius
1107
1107
'nyquist.indent_direction' : 'right' , # indentation direction
1108
- 'nyquist.indent_points' : 50 , # number of points to insert
1109
- 'nyquist.max_curve_magnitude' : 20 , # clip large values
1108
+ 'nyquist.indent_points' : 200 , # number of points to insert
1109
+ 'nyquist.max_curve_magnitude' : 15 , # rescale large values
1110
+ 'nyquist.blend_fraction' : 0.15 , # when to start scaling
1110
1111
'nyquist.max_curve_offset' : 0.02 , # offset of primary/mirror
1111
1112
'nyquist.start_marker' : 'o' , # marker at start of curve
1112
1113
'nyquist.start_marker_size' : 4 , # size of the marker
@@ -1638,6 +1639,10 @@ def nyquist_plot(
1638
1639
The matplotlib axes to draw the figure on. If not specified and
1639
1640
the current figure has a single axes, that axes is used.
1640
1641
Otherwise, a new figure is created.
1642
+ blend_fraction : float, optional
1643
+ For portions of the Nyquist curve that are scaled to have a maximum
1644
+ magnitude of `max_curve_magnitude`, begin a smooth rescaling at
1645
+ this fraction of `max_curve_magnitude`. Default value is 0.15.
1641
1646
encirclement_threshold : float, optional
1642
1647
Define the threshold for generating a warning if the number of net
1643
1648
encirclements is a non-integer value. Default value is 0.05 and can
@@ -1784,6 +1789,8 @@ def nyquist_plot(
1784
1789
ax_user = ax
1785
1790
max_curve_magnitude = config ._get_param (
1786
1791
'nyquist' , 'max_curve_magnitude' , kwargs , _nyquist_defaults , pop = True )
1792
+ blend_fraction = config ._get_param (
1793
+ 'nyquist' , 'blend_fraction' , kwargs , _nyquist_defaults , pop = True )
1787
1794
max_curve_offset = config ._get_param (
1788
1795
'nyquist' , 'max_curve_offset' , kwargs , _nyquist_defaults , pop = True )
1789
1796
rcParams = config ._get_param ('ctrlplot' , 'rcParams' , kwargs , pop = True )
@@ -1891,21 +1898,36 @@ def _parse_linestyle(style_name, allow_false=False):
1891
1898
splane_contour = np .log (response .contour ) / response .dt
1892
1899
1893
1900
# Find the different portions of the curve (with scaled pts marked)
1901
+ if blend_fraction < 0 or blend_fraction > 1 :
1902
+ raise ValueError ("blend_fraction must be between 0 and 1" )
1903
+ blend_curve_start = (1 - blend_fraction ) * max_curve_magnitude
1894
1904
reg_mask = np .logical_or (
1895
- np .abs (resp ) > max_curve_magnitude ,
1896
- splane_contour .real != 0 )
1897
- # reg_mask = np.logical_or(
1898
- # np.abs(resp.real) > max_curve_magnitude,
1899
- # np.abs(resp.imag) > max_curve_magnitude)
1905
+ np .abs (resp ) > blend_curve_start ,
1906
+ np .logical_not (np .isclose (splane_contour .real , 0 )))
1900
1907
1901
1908
scale_mask = ~ reg_mask \
1902
1909
& np .concatenate ((~ reg_mask [1 :], ~ reg_mask [- 1 :])) \
1903
1910
& np .concatenate ((~ reg_mask [0 :1 ], ~ reg_mask [:- 1 ]))
1904
1911
1905
1912
# Rescale the points with large magnitude
1906
- rescale = np .logical_and (
1907
- reg_mask , abs (resp ) > max_curve_magnitude )
1908
- resp [rescale ] *= max_curve_magnitude / abs (resp [rescale ])
1913
+ rescale_idx = (np .abs (resp ) > blend_curve_start )
1914
+
1915
+ if np .any (rescale_idx ): # Only process if rescaling is needed
1916
+ subset = resp [rescale_idx ]
1917
+ abs_subset = np .abs (subset )
1918
+ unit_vectors = subset / abs_subset # Preserve phase/direction
1919
+
1920
+ if blend_curve_start is None or \
1921
+ blend_curve_start == max_curve_magnitude :
1922
+ # Clip at max_curve_magnitude
1923
+ resp [rescale_idx ] = max_curve_magnitude * unit_vectors
1924
+ else :
1925
+ # Logistic scaling
1926
+ newmag = blend_curve_start + \
1927
+ (max_curve_magnitude - blend_curve_start ) * \
1928
+ (abs_subset - blend_curve_start ) / \
1929
+ (abs_subset + max_curve_magnitude - 2 * blend_curve_start )
1930
+ resp [rescale_idx ] = newmag * unit_vectors
1909
1931
1910
1932
# Get the label to use for the line
1911
1933
label = response .sysname if line_labels is None else line_labels [idx ]
0 commit comments