Skip to content

Nyquist plot improvements #534

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Feb 12, 2021
Merged

Conversation

murrayrm
Copy link
Member

@murrayrm murrayrm commented Feb 6, 2021

This PR provides improvements to Nyquist plots:

  • The nyquist_plot command now returns the number of encirclements of the -1 point and (optionally) the contour used to generate the plot instead of the magnitude, phase, and frequency values. (The system response can be generated from the contour by simply evaluating the LTI system along the contour).
  • Nyquist plots now automatically create a small indentation around poles on the imaginary access, along Nyquist plots to be closed curves. Options are available to change the size of the indentation (indent_radius) and the direction of the indentation for the case of poles exactly on the imaginary axis (set indent_direction to 'left' or 'right'). Indentation can be turned off by setting indent_direction to none).
  • Arrows are now generated using FancyArrowPatch, which provides a fixed size and aspect ratio in display coordinates (so zooming in doesn't not change the size and shape of arrows). The default arrow style can be overridden using the option arrow_size parameter or the arrow_style parameters.
  • The number (or specific location) of arrows can be specified using the arrows keyword, which allows you to specify the number of arrows or the specific locations (via a list of relative positions on the primary segment).
  • The line style for mirror image of the primary segment can be set using the mirror_style parameter and defaults to a dashed line. The mirror image segment can be omitted by setting mirror_style=False.
  • The MATLAB version of nyquist retains its original call signature and return values.

Before:

After:

Other notes:

  • The mirror_style parameter will conflict with the mirror parameter in PR Describing functions #521 (describing functions). The functionality is basically the same, but there will be a merge conflict in whichever gets merged second. (I think we should use mirror_style).
  • The various default settings can now be set using config.defaults with module name nyquist.
  • The control/tests/nyquist_test.py unit test file serves as a pretty complete unit test, but can also be run in python/ipython to generate plots that you can see (and make sure they look OK).
  • I pulled the code for processing arguments for the MATLAB version of bode into a separate function (_parse_freqplot_args) that is used for both bode and nyquist.
  • Added unit tests, docstrings, some PEP8 cleanup.

@coveralls
Copy link

coveralls commented Feb 6, 2021

Coverage Status

Coverage increased (+0.5%) to 88.798% when pulling e0521b9 on murrayrm:improve_nyquist into eb62d80 on python-control:master.

# If argument was a singleton, turn it into a list
if not hasattr(syslist, '__iter__'):
isscalar = not hasattr(syslist, '__iter__')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IsScalar sounds like it could be referring to the system. ‘Is_Single_system’?

from ..freqplot import nyquist_plot

# If first argument is a list, assume python-control calling format
if (getattr(args[0], '__iter__', False)):
Copy link
Contributor

@sawyerbfuller sawyerbfuller Feb 7, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have seen this style elsewhere in the library so I am curious: isn’t hasattr(args[0], ‘ iter’) both more readable and compact? (I can’t do backticks from my phone so underscores appear as bold)

Copy link
Contributor

@bnavigator bnavigator Feb 7, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hasattr also returns True if args[0] is an iterable sequence, but empty. This code only branches if args[0] is iterable and the sequence does not cast to False (is empty).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, thanks!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getattr([], '__iter__', False) returns <method-wrapper '__iter__' of list object .... So looks like both of them would follow the branch?

@sawyerbfuller
Copy link
Contributor

Very nice! Plots look great and I think the new return values make sense.

Comment on lines 37 to 41
@pytest.fixture(scope="function")
def figure_cleanup():
plt.close('all')
yield
plt.close('all')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you try to use the existing mplcleanup (automatically loaded from conftest.py)?

@pytest.fixture(scope="function")
def mplcleanup():
"""Workaround for python2
python 2 does not like to mix the original mpl decorator with pytest
fixtures. So we roll our own.
"""
save = mpl.units.registry.copy()
try:
yield
finally:
mpl.units.registry.clear()
mpl.units.registry.update(save)
mpl.pyplot.close("all")

Now that we don't test python2 anymore, we could directly map it to matplotlib.testing.decorators.cleanup

@bnavigator
Copy link
Contributor

Hmm, I wonder why the nichols_test xlim warning is back here. The test is run before nyquist test and already uses the mplcleanup fixture.

* regularize argument parsing by using _parse_freqplot_args
* fix bug in exception handling (ControlArgument not defined)
* change printed warning to use warnings.warn
* add unit tests for exceptions, warnings
* start tracing from omega = 0
* add support for indenting around imaginary poles
* change return value to number of encirclements (+ contour, if desired)
* update MATLAB interface to retain legacy format
* new unit tests
@murrayrm murrayrm mentioned this pull request Feb 11, 2021
@murrayrm murrayrm linked an issue Feb 11, 2021 that may be closed by this pull request
@murrayrm murrayrm merged commit 9c6c619 into python-control:master Feb 12, 2021
@murrayrm murrayrm deleted the improve_nyquist branch March 3, 2021 02:56
@murrayrm murrayrm added this to the 0.9.0 milestone Mar 20, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

arrows in nyquist plot
4 participants