@@ -1100,13 +1100,14 @@ def gen_zero_centered_series(val_min, val_max, period):
11001100_nyquist_defaults = {
11011101 'nyquist.primary_style' : ['-' , '-.' ], # style for primary curve
11021102 '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
11041104 'nyquist.arrow_size' : 8 , # pixel size for arrows
11051105 'nyquist.encirclement_threshold' : 0.05 , # warning threshold
11061106 'nyquist.indent_radius' : 1e-4 , # indentation radius
11071107 '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
11101111 'nyquist.max_curve_offset' : 0.02 , # offset of primary/mirror
11111112 'nyquist.start_marker' : 'o' , # marker at start of curve
11121113 'nyquist.start_marker_size' : 4 , # size of the marker
@@ -1638,6 +1639,10 @@ def nyquist_plot(
16381639 The matplotlib axes to draw the figure on. If not specified and
16391640 the current figure has a single axes, that axes is used.
16401641 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.
16411646 encirclement_threshold : float, optional
16421647 Define the threshold for generating a warning if the number of net
16431648 encirclements is a non-integer value. Default value is 0.05 and can
@@ -1784,6 +1789,8 @@ def nyquist_plot(
17841789 ax_user = ax
17851790 max_curve_magnitude = config ._get_param (
17861791 'nyquist' , 'max_curve_magnitude' , kwargs , _nyquist_defaults , pop = True )
1792+ blend_fraction = config ._get_param (
1793+ 'nyquist' , 'blend_fraction' , kwargs , _nyquist_defaults , pop = True )
17871794 max_curve_offset = config ._get_param (
17881795 'nyquist' , 'max_curve_offset' , kwargs , _nyquist_defaults , pop = True )
17891796 rcParams = config ._get_param ('ctrlplot' , 'rcParams' , kwargs , pop = True )
@@ -1891,21 +1898,36 @@ def _parse_linestyle(style_name, allow_false=False):
18911898 splane_contour = np .log (response .contour ) / response .dt
18921899
18931900 # 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
18941904 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 )))
19001907
19011908 scale_mask = ~ reg_mask \
19021909 & np .concatenate ((~ reg_mask [1 :], ~ reg_mask [- 1 :])) \
19031910 & np .concatenate ((~ reg_mask [0 :1 ], ~ reg_mask [:- 1 ]))
19041911
19051912 # 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
19091931
19101932 # Get the label to use for the line
19111933 label = response .sysname if line_labels is None else line_labels [idx ]
0 commit comments