Skip to content

Time response plot improvements #1018

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

Conversation

murrayrm
Copy link
Member

This PR makes improvements to the code for plotting time responses, building on some of the improvements made for frequency response plots (#1011):

  • Fix up the ax keyword processing to allow arrays or lists + uniform processing for all (time and frequency) plot routines.
  • Slight refactoring of code to put common functions in ctrlplot.py (additional changes coming in future PR to further consolidate common code)
  • Allow time responses for multiple systems with common time vector and inputs. This enables commands like
    ct.step_response([sys1, sys2]).plot
    
    to find a single time interval (before you had to call step_response twice and could get different default time intervals).
  • Fix sequential plotting so that different colors are used and plot title is updated (like Bode and Nyquist).
  • Allow label keyword in various time response commands to override default label generation.
  • Allow legends to be turned on and off using show_legend keyword.
  • Added a file examples/plot_gallery.py that generates a PDF file with some standard plots to compare changes in basic plotting between PRs and releases.

To get a sense of the differences, consider the following code:

sys_mimo1 = ct.tf2ss(
    [[[1], [0.1]], [[0.2], [1]]],
    [[[1, 0.6, 1], [1, 1, 1]], [[1, 0.4, 1], [1, 2, 1]]], name="sys_mimo1")
sys_mimo2 = ct.tf2ss(
    [[[1], [0.1]], [[0.2], [1]]],
    [[[1, 0.2, 1], [1, 24, 22, 5]], [[1, 4, 16, 21], [1, 0.1]]],
    name="sys_mimo2")
ct.step_response(sys_mimo1).plot()
ct.step_response(sys_mimo2).plot()

Prior to this PR, the output from this code was

mimo_step-orig

Note the fact that both responses are in the same color.

With this PR, the output becomes

mimo_step-seq

Furthermore, you can compute a single time range using the command

ct.step_response([sys_mimo1, sys_mimo2])

which gives a single time range for both systems:

mimo_step-list

@coveralls
Copy link

Coverage Status

coverage: 94.524% (+0.006%) from 94.518%
when pulling d459569 on murrayrm:timeresp_improvements-01Jun2024
into feeb56a on python-control:main.

@slivingston slivingston self-requested a review June 29, 2024 22:54
@slivingston slivingston self-assigned this Jun 29, 2024
@murrayrm murrayrm added this to the 0.10.1 milestone Jun 30, 2024
@murrayrm murrayrm mentioned this pull request Jun 30, 2024
11 tasks
control/nlsys.py Outdated
@@ -1327,8 +1327,8 @@ def input_output_response(

Parameters
----------
sys : InputOutputSystem
Input/output system to simulate.
sysdata : I/O system or list of I/O systems
Copy link
Member

Choose a reason for hiding this comment

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

It is useful to include a class name here because, in that case, docs generation results in a link to documentation for the class. Unless I missed something, this should be NonlinearIOSystem. This will work even when the type is written as "...or list of...", e.g., compare with the data parameter of nyquist_plot: https://python-control.readthedocs.io/en/0.10.0/generated/control.nyquist_plot.html

@slivingston
Copy link
Member

I will finish reviewing this tomorrow morning.

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.

Good improvements! This is ready to merge after you consider/apply my comments.

common_prefix = common_prefix[:last_space]
prefix_len = len(common_prefix)

# Look for a common suffice (up to a space)
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
# Look for a common suffice (up to a space)
# Look for a common suffix (up to a space)

Copy link
Member

Choose a reason for hiding this comment

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

Misprints:

  • "to define and input/output" -> "to define an input/output"
  • "response object, we can be" -> "response object, which can be"

# used to compare what things look like between different versions of the
# library. It is mainly intended for uses by developers to make sure there
# are no unexpected changes in plot formats, but also has some interest
# examples of htings you can plot.
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
# examples of htings you can plot.
# examples of things you can plot.

# Create a pdf file for storing the results
from matplotlib.backends.backend_pdf import PdfPages
from datetime import date
git_info = os.popen('git describe').read().strip()
Copy link
Member

Choose a reason for hiding this comment

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

Should it be considered an error if a Git repository is not found?

I ask because os.popen will not raise an exception if the subprocess has errors, so git_info will be an empty string and plot_gallery.py will continue if git describe fails. If you want an exception, I recommend check_output:

import subprocess
git_info = subprocess.check_output(['git', 'describe'], text=True).strip()

automatically compute the time vector based on the poles and zeros of
system. If a list of systems is passed, a common time vector will be
computed and a list of responses will be returned in the form of a
:class:`TimeResponseList` object. The :func:`force_response` function can
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
:class:`TimeResponseList` object. The :func:`force_response` function can
:class:`TimeResponseList` object. The :func:`forced_response` function can

:class:`TimeResponseList` object. The :func:`force_response` function can
also take a list of systems, to which a single common input is applied.
The :class:`TimeResponseList` object has a `plot()` method that will plot
each of the reponses in turn, using a sequence of different colors with
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
each of the reponses in turn, using a sequence of different colors with
each of the responses in turn, using a sequence of different colors with

For linear time invariant (LTI) systems, the :func:`impulse_response`,
:func:`initial_response`, and :func:`step_response` functions will
automatically compute the time vector based on the poles and zeros of
system. If a list of systems is passed, a common time vector will be
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
system. If a list of systems is passed, a common time vector will be
the system. If a list of systems is passed, a common time vector will be

control/nlsys.py Outdated
@@ -1317,7 +1317,7 @@ def nlsys(


def input_output_response(
sys, T, U=0., X0=0, params=None, ignore_errors=False,
sysdata, T, U=0., X0=0, params=None, ignore_errors=False,
Copy link
Member

Choose a reason for hiding this comment

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

Changing the parameter name sys to sysdata here and in other functions modified by this pull request is good for clarity to users, but it will break existing calls of the form input_output_response(sys=ss, ...). As such, the next version probably should be 0.11.0 instead of 0.10.1 (#1020) to emphasize that existing code using control may need to be updated.

@coveralls
Copy link

Coverage Status

coverage: 94.554% (+0.04%) from 94.518%
when pulling 10f009b on murrayrm:timeresp_improvements-01Jun2024
into feeb56a on python-control:main.

@murrayrm murrayrm merged commit 5ba44b5 into python-control:main Jul 7, 2024
23 checks passed
@murrayrm murrayrm deleted the timeresp_improvements-01Jun2024 branch July 7, 2024 02:33
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.

3 participants