@@ -518,8 +518,8 @@ def gen_zero_centered_series(val_min, val_max, period):
518518
519519# Default values for module parameter variables
520520_nyquist_defaults = {
521- 'nyquist.primary_style' : ['-' , ': ' ], # style for primary curve
522- 'nyquist.mirror_style' : ['--' , '-. ' ], # style for mirror curve
521+ 'nyquist.primary_style' : ['-' , '-. ' ], # style for primary curve
522+ 'nyquist.mirror_style' : ['--' , ': ' ], # style for mirror curve
523523 'nyquist.arrows' : 2 ,
524524 'nyquist.arrow_size' : 8 ,
525525 'nyquist.indent_radius' : 1e-6 , # indentation radius
@@ -695,6 +695,7 @@ def nyquist_plot(syslist, omega=None, plot=True, omega_limits=None,
695695 kwargs .pop ('arrow_length' , False )
696696
697697 # Get values for params (and pop from list to allow keyword use in plot)
698+ omega_num_given = omega_num is not None
698699 omega_num = config ._get_param ('freqplot' , 'number_of_samples' , omega_num )
699700 arrows = config ._get_param (
700701 'nyquist' , 'arrows' , kwargs , _nyquist_defaults , pop = True )
@@ -735,8 +736,13 @@ def _parse_linestyle(style_name, allow_false=False):
735736 omega , omega_range_given = _determine_omega_vector (
736737 syslist , omega , omega_limits , omega_num )
737738 if not omega_range_given :
738- # Start contour at zero frequency
739- omega [0 ] = 0.
739+ if omega_num_given :
740+ # Just reset the starting point
741+ omega [0 ] = 0.0
742+ else :
743+ # Insert points between the origin and the first frequency point
744+ omega = np .concatenate ((
745+ np .linspace (0 , omega [0 ], indent_points ), omega [1 :]))
740746
741747 # Go through each system and keep track of the results
742748 counts , contours = [], []
@@ -937,17 +943,23 @@ def _parse_linestyle(style_name, allow_false=False):
937943 x_reg , y_reg , primary_style [0 ], color = color , * args , ** kwargs )
938944 c = p [0 ].get_color ()
939945
946+ # Figure out how much to offset the curve: the offset goes from
947+ # zero at the start of the scaled section to max_curve_offset as
948+ # we move along the curve
949+ curve_offset = _compute_curve_offset (
950+ resp , scale_mask , max_curve_offset )
951+
940952 # Plot the scaled sections of the curve (changing linestyle)
941953 x_scl = np .ma .masked_where (scale_mask , resp .real )
942954 y_scl = np .ma .masked_where (scale_mask , resp .imag )
943955 plt .plot (
944- x_scl * (1 + max_curve_offset ), y_scl * (1 + max_curve_offset ),
956+ x_scl * (1 + curve_offset ), y_scl * (1 + curve_offset ),
945957 primary_style [1 ], color = c , * args , ** kwargs )
946958
947959 # Plot the primary curve (invisible) for setting arrows
948960 x , y = resp .real .copy (), resp .imag .copy ()
949- x [reg_mask ] *= (1 + max_curve_offset )
950- y [reg_mask ] *= (1 + max_curve_offset )
961+ x [reg_mask ] *= (1 + curve_offset [ reg_mask ] )
962+ y [reg_mask ] *= (1 + curve_offset [ reg_mask ] )
951963 p = plt .plot (x , y , linestyle = 'None' , color = c , * args , ** kwargs )
952964
953965 # Add arrows
@@ -961,14 +973,14 @@ def _parse_linestyle(style_name, allow_false=False):
961973 plt .plot (
962974 x_reg , - y_reg , mirror_style [0 ], color = c , * args , ** kwargs )
963975 plt .plot (
964- x_scl * (1 - max_curve_offset ),
965- - y_scl * (1 - max_curve_offset ),
976+ x_scl * (1 - curve_offset ),
977+ - y_scl * (1 - curve_offset ),
966978 mirror_style [1 ], color = c , * args , ** kwargs )
967979
968980 # Add the arrows (on top of an invisible contour)
969981 x , y = resp .real .copy (), resp .imag .copy ()
970- x [reg_mask ] *= (1 - max_curve_offset )
971- y [reg_mask ] *= (1 - max_curve_offset )
982+ x [reg_mask ] *= (1 - curve_offset [ reg_mask ] )
983+ y [reg_mask ] *= (1 - curve_offset [ reg_mask ] )
972984 p = plt .plot (x , - y , linestyle = 'None' , color = c , * args , ** kwargs )
973985 _add_arrows_to_line2D (
974986 ax , p [0 ], arrow_pos , arrowstyle = arrow_style , dir = - 1 )
@@ -1086,6 +1098,62 @@ def _add_arrows_to_line2D(
10861098 arrows .append (p )
10871099 return arrows
10881100
1101+ #
1102+ # Function to compute Nyquist curve offsets
1103+ #
1104+ # This function computes a smoothly varying offset that starts and ends at
1105+ # zero at the ends of a scaled segment.
1106+ #
1107+ def _compute_curve_offset (resp , mask , max_offset ):
1108+ # Compute the arc length along the curve
1109+ s_curve = np .cumsum (
1110+ np .sqrt (np .diff (resp .real ) ** 2 + np .diff (resp .imag ) ** 2 ))
1111+
1112+ # Initialize the offset
1113+ offset = np .zeros (resp .size )
1114+ arclen = np .zeros (resp .size )
1115+
1116+ # Walk through the response and keep track of each continous component
1117+ i , nsegs = 0 , 0
1118+ while i < resp .size :
1119+ # Skip the regular segment
1120+ while i < resp .size and mask [i ]:
1121+ i += 1 # Increment the counter
1122+ if i == resp .size :
1123+ break ;
1124+ # Keep track of the arclength
1125+ arclen [i ] = arclen [i - 1 ] + np .abs (resp [i ] - resp [i - 1 ])
1126+
1127+ nsegs += 0.5
1128+ if i == resp .size :
1129+ break ;
1130+
1131+ # Save the starting offset of this segment
1132+ seg_start = i
1133+
1134+ # Walk through the scaled segment
1135+ while i < resp .size and not mask [i ]:
1136+ i += 1
1137+ if i == resp .size : # See if we are done with this segment
1138+ break ;
1139+ # Keep track of the arclength
1140+ arclen [i ] = arclen [i - 1 ] + np .abs (resp [i ] - resp [i - 1 ])
1141+
1142+ nsegs += 0.5
1143+ if i == resp .size :
1144+ break ;
1145+
1146+ # Save the ending offset of this segment
1147+ seg_end = i
1148+
1149+ # Now compute the scaling for this segment
1150+ s_segment = arclen [seg_end - 1 ] - arclen [seg_start ]
1151+ offset [seg_start :seg_end ] = max_offset * s_segment / s_curve [- 1 ] * \
1152+ np .sin (np .pi * (arclen [seg_start :seg_end ]
1153+ - arclen [seg_start ])/ s_segment )
1154+
1155+ return offset
1156+
10891157
10901158#
10911159# Gang of Four plot
0 commit comments