-
Notifications
You must be signed in to change notification settings - Fork 438
Reimplementation of 2D phase plots #980
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
Conversation
control/flatsys/__init__.py
Outdated
@@ -35,7 +35,7 @@ | |||
# Author: Richard M. Murray | |||
# Date: 1 Jul 2019 | |||
|
|||
r"""The :mod:`control.flatsys` package contains a set of classes and functions | |||
r"""The :mod:`control.flatsys` module contains a set of classes and functions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The original is correct: flatsys
is a package according to standard terms: https://docs.python.org/3/glossary.html#term-package
control/phaseplot.py
Outdated
import numpy as np | ||
import matplotlib.pyplot as mpl | ||
"""The :mod:`control.phaseplot` module contains functions for generating 2D | ||
phase plots. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Convention is for summary to be 1 line (https://peps.python.org/pep-0257/#multi-line-docstrings).
examples/phase_plane_plots.py
Outdated
@@ -0,0 +1,215 @@ | |||
# phase_portraits.py - phase portrait examples |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# phase_portraits.py - phase portrait examples | |
# phase_plane_plots.py - phase portrait examples |
@@ -29,6 +30,9 @@ freqplot-siso_bode-default.png: ../control/tests/freqplot_test.py | |||
rlocus-siso_ctime-default.png: ../control/tests/rlocus_test.py | |||
PYTHONPATH=.. python $< | |||
|
|||
phaseplot-dampedosc-default.png: ../control/tests/phaseplot_test.py | |||
PYTHONPATH=.. python $< |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the other images generated by phaseplot_test.py (phaseplot-oscillator-helpers.png
, phaseplot-invpend-meshgrid.png
) be included here? It does not seem important because the PNG files are being committed, but I note this here in case you wanted to.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree that they could be listed, but I didn't list them all out since triggering on the one file will update all files => if the test generation script is updated, all PNG files are updated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point! I will complete review tomorrow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixes some very outdated code. Very nice!
control/phaseplot.py
Outdated
return new_kwargs | ||
|
||
# Create array for storing outputs | ||
out = np.empty(3, dtype=object) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Docstring says this is a list. Seems like a list might be better/simpler here than a numpy array, e.g. out = [[], None, None]
control/phaseplot.py
Outdated
|
||
Parameters | ||
---------- | ||
sys : NonlinearIOSystem or callable(x, t, ...) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is motivation to maintain the old convention of (x, t, ...) argument order, rather than (t, x, ...) when called with a callable? Backwards compatibility? I am not aware of anywhere else we or scipy adheres to that convention anymore, but I might be missing something.
control/phaseplot.py
Outdated
vfdata = np.zeros((points.shape[0], 4)) | ||
for i, x in enumerate(points): | ||
vfdata[i, :2] = x | ||
vfdata[i, 2:] = sys.dynamics(0, x, 0, params=params) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this wouldn't work if sys
is just a function - is the callable intended to be a function if it is not a system?
control/phaseplot.py
Outdated
Parameters | ||
---------- | ||
sys : NonlinearIOSystem or callable(x, t, ...) | ||
Function used to generate phase plane data. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Relatedly, should there be a statement here that only two-state systems can be accepted? (and a test that gives a helpful error?)
In addition to adressing comments, functionality for handling callables (versus systems) was missing; now added. |
control/phaseplot.py
Outdated
# Create reverse time system, if needed | ||
if dir != 'forward': | ||
revsys = NonlinearIOSystem( | ||
lambda t, x, u, params: -np.array(sys.updfcn(t, x, u, params)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lambda t, x, u, params: -np.array(sys.updfcn(t, x, u, params)), | |
lambda t, x, u, params: -np.asarray(sys.updfcn(t, x, u, params)), |
This won't re-create the array if it is already one.
relatedly, and this is more of a question: I am confused about the use of each of these methods, all of which do roughly the same thing. Is this correct: _rhs
fast and low-level, updfcn
just a reference to the function that was passed in, and dynamics
: user-friendly function that may not be as fast? Just checking that this is the right one (speed is kind of important in this application)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct on the various versions. In general:
- _updfcn() should not be called directly (though OK here, because we are creating a new system).
- _rhs() should be called inside loops (with a call to _update_params() before the loop starts, which sets the parameter values)
- dynamics() is the user-callable function (which calls _update_params and then _rhs)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work! I found a few more misprints, but nothing critical. Locally I merged this into the current tip of main
branch and successfully ran the tests and example code, and built the documentation.
examples/phase_plane_plots.py
Outdated
# RMM, 25 Mar 2024 | ||
# | ||
# This file contains a number of examples of phase plane plots generated | ||
# using the phaseplot module. Most of these figures lines up with examples |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# using the phaseplot module. Most of these figures lines up with examples | |
# using the phaseplot module. Most of these figures line up with examples |
control/phaseplot.py
Outdated
phase plots. The base function for creating phase plane portraits is | ||
:func:`~control.phase_plane_plot`, which generates a phase plane portrait | ||
for a 2 state I/O system (with no inputs). In addition, several other | ||
functions are available to creat ecustomize phase plane plots: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
functions are available to creat ecustomize phase plane plots: | |
functions are available to customize phase plane plots: |
control/phaseplot.py
Outdated
Parameters | ||
---------- | ||
sys : NonlinearIOSystem or callable(t, x, ...) | ||
I/O systems or function used to generate phase plane data. If a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I/O systems or function used to generate phase plane data. If a | |
I/O system or function used to generate phase plane data. If a |
doc/plotting.rst
Outdated
The :func:`~control.phase_plane_plot` function calls these helper functions | ||
based on the options it is passed. | ||
|
||
Note that unlike other plotting function, phase plane plots do not involve |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that unlike other plotting function, phase plane plots do not involve | |
Note that unlike other plotting functions, phase plane plots do not involve |
control/phaseplot.py
Outdated
plot_separatrices : bool or dict | ||
If `True` (default) then plot separatrices starting from each | ||
equilibrium point. If set to a dict, pass on the key-value pairs | ||
in the dict as keywords to :func:`~control.phaseplot.streamlines`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in the dict as keywords to :func:`~control.phaseplot.streamlines`. | |
in the dict as keywords to :func:`~control.phaseplot.separatrices`. |
control/phaseplot.py
Outdated
|
||
import numpy as np | ||
import matplotlib.pyplot as mpl | ||
# Create a system form a callable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# Create a system form a callable | |
# Create a system from a callable |
control/phaseplot.py
Outdated
# if not isinstance(pointdata, (list, tuple)) or len(pointdata) != 4: | ||
# raise ValueError("invalid grid data specification") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# if not isinstance(pointdata, (list, tuple)) or len(pointdata) != 4: | |
# raise ValueError("invalid grid data specification") |
This can be deleted, or the check in the comment can be expanded to handle case of isinstance(pointdata, np.ndarray)
.
Thanks for the careful reviews, @sawyerbfuller and @slivingston! Will merge as soon as checks finish up (just in case). |
This PR reimplements the phase plot functionality of python-control by creating a new function
phase_plane_plot
and creating a modulephaseplot
that provides more specialized functionality. The legacyphase_plot
function is still available (with a deprecation warning).From the documentation:
The default method for generating a phase plane plot is to provide a 2D dynamical system along with a range of coordinates and time limit:
By default, the plot includes streamlines generated from starting points on limits of the plot, with arrows showing the flow of the system, as well as any equilibrium points for the system. A variety of options are available to modify the information that is plotted, including plotting a grid of vectors instead of streamlines and turning on and off various features of the plot.
To illustrate some of these possibilities, consider a phase plane plot for an inverted pendulum system, which is created using a mesh grid:
This figure shows several features of more complex phase plane plots: multiple equilibrium points are shown, with saddle points showing separatrices, and streamlines generated along a 5x8 mesh of initial conditions. At each mesh point, a streamline is created that goes 5 time units forward and backward in time. A separate grid specification is used to find equilibrium points and separatrices (since the course grid spacing of 5x8 does not find all possible equilibrium points). Together, the multiple features in the phase plane plot give a good global picture of the topological strucrure of solutions of the dynamical system.
Phase plots can be buit up by hand using a variety of helper functions that are part of the
control.phaseplot
(pp) module:The following helper functions are available:
The
phase_plane_plot
function calls these helper functions based on the options it is passed.Note that unlike other plotting function, phase plane plots do not involve computing a response and then plotting the result via a
plot()
method. Instead, the plot is generated directly be a call to thephase_plane_plot
function (or one of thect.phaseplot
helper functions.