4242 'pzmap.grid' : False , # Plot omega-damping grid
4343 'pzmap.marker_size' : 6 , # Size of the markers
4444 'pzmap.marker_width' : 1.5 , # Width of the markers
45- 'pzmap.expansion_factor' : 2 , # Amount to scale plots beyond features
45+ 'pzmap.expansion_factor' : 1.8 , # Amount to scale plots beyond features
46+ 'pzmap.buffer_factor' : 1.05 , # Buffer to leave around plot peaks
4647}
4748
4849#
@@ -110,7 +111,7 @@ def pole_zero_map(sysdata):
110111def pole_zero_plot (
111112 data , plot = None , grid = None , title = None , marker_color = None ,
112113 marker_size = None , marker_width = None , legend_loc = 'upper right' ,
113- xlim = None , ylim = None , interactive = False , ax = None ,
114+ xlim = None , ylim = None , interactive = False , ax = None , scaling = None ,
114115 initial_gain = None , ** kwargs ):
115116 # TODO: update docstring (see other response/plot functions for style)
116117 """Plot a pole/zero map for a linear system.
@@ -144,7 +145,7 @@ def pole_zero_plot(
144145 (legacy) If the `plot` keyword is given, the system poles and zeros
145146 are returned.
146147
147- Notes (TODO: update)
148+ Notes (TODO: update, including scaling )
148149 -----
149150 The pzmap function calls matplotlib.pyplot.axis('equal'), which means
150151 that trying to reset the axis limits may not behave as expected. To
@@ -209,14 +210,15 @@ def pole_zero_plot(
209210 if grid :
210211 plt .clf ()
211212 if all ([isctime (dt = response .dt ) for response in data ]):
212- ax , fig = sgrid ()
213+ ax , fig = sgrid (scaling = scaling )
213214 elif all ([isdtime (dt = response .dt ) for response in data ]):
214- ax , fig = zgrid ()
215+ ax , fig = zgrid (scaling = scaling )
215216 else :
216217 ValueError (
217218 "incompatible time responses; don't know how to grid" )
218219 elif len (axs ) == 0 :
219- ax , fig = nogrid (data [0 ].dt ) # use first response timebase
220+ # use first response timebase
221+ ax , fig = nogrid (data [0 ].dt , scaling = scaling )
220222 else :
221223 # Use the existing axes and any grid that is there
222224 # TODO: allow axis to be overriden via parameter
@@ -270,7 +272,7 @@ def pole_zero_plot(
270272 label = response .sysname )
271273
272274 # Compute the axis limits to use based on the response
273- resp_xlim , resp_ylim = _compute_root_locus_limits (response . loci )
275+ resp_xlim , resp_ylim = _compute_root_locus_limits (response )
274276
275277 # Keep track of the current limits
276278 xlim = [min (xlim [0 ], resp_xlim [0 ]), max (xlim [1 ], resp_xlim [1 ])]
@@ -433,11 +435,22 @@ def _create_root_locus_label(sys, K, s):
433435
434436
435437# Utility function to compute limits for root loci
436- # TODO: compare to old code and recapture functionality (especially asymptotes)
437438# TODO: (note that sys is now available => code here may not be needed)
438- def _compute_root_locus_limits (loci ):
439- # Go through each locus
440- xlim , ylim = [0 , 0 ], 0
439+ def _compute_root_locus_limits (response ):
440+ loci = response .loci
441+
442+ # Start with information about zeros, if present
443+ if response .sys is not None and response .sys .zeros ().size > 0 :
444+ xlim = [
445+ min (0 , np .min (response .sys .zeros ().real )),
446+ max (0 , np .max (response .sys .zeros ().real ))
447+ ]
448+ ylim = max (0 , np .max (response .sys .zeros ().imag ))
449+ else :
450+ xlim , ylim = [0 , 0 ], 0
451+
452+ # Go through each locus and look for features
453+ rho = config ._get_param ('pzmap' , 'buffer_factor' )
441454 for locus in loci .transpose ():
442455 # Include all starting points
443456 xlim = [min (xlim [0 ], locus [0 ].real ), max (xlim [1 ], locus [0 ].real )]
@@ -446,18 +459,22 @@ def _compute_root_locus_limits(loci):
446459 # Find the local maxima of root locus curve
447460 xpeaks = np .where (
448461 np .diff (np .abs (locus .real )) < 0 , locus .real [0 :- 1 ], 0 )
449- xlim = [min (xlim [0 ], np .min (xpeaks )), max (xlim [1 ], np .max (xpeaks ))]
462+ xlim = [
463+ min (xlim [0 ], np .min (xpeaks ) * rho ),
464+ max (xlim [1 ], np .max (xpeaks ) * rho )
465+ ]
450466
451467 ypeaks = np .where (
452468 np .diff (np .abs (locus .imag )) < 0 , locus .imag [0 :- 1 ], 0 )
453- ylim = max (ylim , np .max (ypeaks ))
454-
455- # Adjust the limits to include some space around features
456- # TODO: use _k_max and project out to max k for all value?
457- rho = config ._get_param ('pzmap' , 'expansion_factor' )
458- xlim [0 ] = rho * xlim [0 ] if xlim [0 ] < 0 else 0
459- xlim [1 ] = rho * xlim [1 ] if xlim [1 ] > 0 else 0
460- ylim = rho * ylim if ylim > 0 else np .max (np .abs (xlim ))
469+ ylim = max (ylim , np .max (ypeaks ) * rho )
470+
471+ if isctime (dt = response .dt ):
472+ # Adjust the limits to include some space around features
473+ # TODO: use _k_max and project out to max k for all value?
474+ rho = config ._get_param ('pzmap' , 'expansion_factor' )
475+ xlim [0 ] = rho * xlim [0 ] if xlim [0 ] < 0 else 0
476+ xlim [1 ] = rho * xlim [1 ] if xlim [1 ] > 0 else 0
477+ ylim = rho * ylim if ylim > 0 else np .max (np .abs (xlim ))
461478
462479 return xlim , [- ylim , ylim ]
463480
0 commit comments