Skip to content

Update Nyquist rescaling + other improvements #1155

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

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

murrayrm
Copy link
Member

This PR addresses issue #1143 by changing the way that rescaling is handled in nyquist_plot. The prior code saturated the Nyquist curve at max_curve_magnitude, which would leave to confusing plots when the Nyquist curve had interesting features at large magnitude, since all of that detail was "collapsed" onto the limiting circle. In the new code, a 1-1 rescaling function is used that maps large magnitude features into a continuous range of magnitudes starting at a "blend point" and going out to max_curve_magnitude. The result is that the features of the Nyquist curve can be more easily visualized. The fraction of the Nyquist curve that is "blended" is set with a new parameter blend_fraction, that defaults to 0.20 (so the last 20% of the Nyquist curve prior to max_curve_magnitude is rescaled, with everything from the blend point to $\infty$ mapping from the blend point to max_curve_magnitude.

Using the example that @JonHowMIT raised in issue #1143, we go from this:

Figure_1_orig

to this:

Figure_1_new

The original behavior can be retained by setting blend_fraction=0.

A few other changes that are also part of this PR:

  • Updated max_curve_magnitude from a default of 20 to 15 (this allows the -1 point to be see better).
  • Updated the default number of arrows from 2 to 3 (looked better in the gallery of plots available in control/tests/nyquist_test.
  • Modified control/tests/nyquist_test to generate a gallery of plots when run as a script (via python or ipython).
  • Changed the output of nyquist_plot to match its documentation. In particular, cplt.lines was returning a 1D array instead of a 2D array (as done in all other _plot functions).
  • Updated unit tests and examples.

@coveralls
Copy link

coveralls commented Jun 21, 2025

Coverage Status

coverage: 94.747% (-0.01%) from 94.759%
when pulling cf3daa4 on murrayrm:fix_nyquist_rescaling-24Mar2025
into 34c6d59 on python-control:main.

@slivingston slivingston self-requested a review June 22, 2025 17:59
Copy link
Member

@slivingston slivingston left a comment

Choose a reason for hiding this comment

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

I made several code quality comments. In terms of clarity of plots, I think this new rescaling behavior is good and ready to merge.

from datetime import date

# Create the file to store figures
git_info = subprocess.check_output(['git', 'describe'], text=True).strip()
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
git_info = subprocess.check_output(['git', 'describe'], text=True).strip()
try:
git_info = subprocess.check_output(['git', 'describe'], text=True).strip()
except subprocess.CalledProcessError:
git_info = 'UNKNOWN-REPO-INFO'

This will fail if the tests are run from outside the Git repository directory, which is possible from anywhere using

python -m control.tests.nyquist_test

To handle this, I recommend to either default to something like "UNKNOWN" or to warn the user ("unable to get version info from git") and not include {git_info} in the file name.

Comment on lines 1902 to 1904
if blend_fraction < 0 or blend_fraction > 1:
raise ValueError("blend_fraction must be between 0 and 1")
blend_curve_start = (1 - blend_fraction) * max_curve_magnitude
Copy link
Member

Choose a reason for hiding this comment

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

Neither checking feasibility of blend_fraction nor declaring blend_curve_start depends on the loop variables (idx and response), so these can be moved to before the start of this for-loop.

abs_subset = np.abs(subset)
unit_vectors = subset / abs_subset # Preserve phase/direction

if blend_curve_start is None or \
Copy link
Member

Choose a reason for hiding this comment

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

blend_curve_start is never None, so the first clause (blend_curve_start is None) can be deleted.

@murrayrm murrayrm linked an issue Jun 25, 2025 that may be closed by this pull request
@murrayrm
Copy link
Member Author

Thanks for the careful review @slivingston. Incorporated your comments and will merge as soon as unit tests pass.

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.

Nyquist plot rescaling is confusing
3 participants