Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .github/scripts/set-conda-test-matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,19 @@
'blas_lib': cbl}
conda_jobs.append(cjob)

# Make sure Windows jobs are included even if we didn't build any
windows_pythons = ['3.11'] # Whatever you want to test

for py in windows_pythons:
for blas in combinations['windows']:
cjob = {
'packagekey': f'windows-{py}',
'os': 'windows',
'python': py,
'blas_lib': blas,
'package_source': 'conda-forge'
}
conda_jobs.append(cjob)

matrix = { 'include': conda_jobs }
print(json.dumps(matrix))
9 changes: 7 additions & 2 deletions .github/workflows/os-blas-test-matrix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ jobs:
os:
- 'ubuntu'
- 'macos'
- 'windows'
python:
# build on one, expand matrix in conda-build from the Sylcot/conda-recipe/conda_build_config.yaml
- '3.11'
Expand Down Expand Up @@ -332,7 +331,13 @@ jobs:
echo "libblas * *mkl" >> $CONDA_PREFIX/conda-meta/pinned
;;
esac
conda install -c ./slycot-conda-pkgs slycot
if [ "${{ matrix.os }}" = "windows" ]; then
echo "Installing slycot from conda-forge on Windows"
conda install slycot
else
echo "Installing built conda package from local channel"
conda install -c ./slycot-conda-pkgs slycot
fi
conda list
- name: Test with pytest
run: JOBNAME="$JOBNAME" pytest control/tests
Expand Down
10 changes: 9 additions & 1 deletion control/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ def use_legacy_defaults(version):
Parameters
----------
version : string
Version number of the defaults desired. Ranges from '0.1' to '0.10.1'.
Version number of `python-control` to use for setting defaults.

Examples
--------
Expand Down Expand Up @@ -342,6 +342,14 @@ def use_legacy_defaults(version):
#
reset_defaults() # start from a clean slate

# Version 0.10.2:
if major == 0 and minor < 10 or (minor == 10 and patch < 2):
from math import inf

# Reset Nyquist defaults
set_defaults('nyquist', arrows=2, max_curve_magnitude=20,
blend_fraction=0, indent_points=50)

# Version 0.9.2:
if major == 0 and minor < 9 or (minor == 9 and patch < 2):
from math import inf
Expand Down
6 changes: 3 additions & 3 deletions control/freqplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -1659,7 +1659,7 @@ def nyquist_plot(
portions of the contour are plotted using a different line style.
label : str or array_like of str, optional
If present, replace automatically generated label(s) with the given
label(s). If sysdata is a list, strings should be specified for each
label(s). If `data` is a list, strings should be specified for each
system.
label_freq : int, optional
Label every nth frequency on the plot. If not specified, no labels
Expand Down Expand Up @@ -1690,8 +1690,8 @@ def nyquist_plot(
elements is equivalent to providing `omega_limits`.
omega_num : int, optional
Number of samples to use for the frequency range. Defaults to
`config.defaults['freqplot.number_of_samples']`. Ignored if data is
not a list of systems.
`config.defaults['freqplot.number_of_samples']`. Ignored if `data`
is not a system or list of systems.
plot : bool, optional
(legacy) If given, `nyquist_plot` returns the legacy return values
of (counts, contours). If False, return the values with no plot.
Expand Down
12 changes: 7 additions & 5 deletions control/margins.py
Original file line number Diff line number Diff line change
Expand Up @@ -536,10 +536,12 @@ def disk_margins(L, omega, skew=0.0, returnall=False):
1D array of (non-negative) frequencies (rad/s) at which
to evaluate the disk-based stability margins.
skew : float or array_like, optional
skew parameter(s) for disk margin (default = 0.0).
skew = 0.0 "balanced" sensitivity function 0.5*(S - T).
skew = 1.0 sensitivity function S.
skew = -1.0 complementary sensitivity function T.
Skew parameter(s) for disk margin (default = 0.0):

* skew = 0.0 "balanced" sensitivity function 0.5*(S - T)
* skew = 1.0 sensitivity function S
* skew = -1.0 complementary sensitivity function T

returnall : bool, optional
If True, return frequency-dependent margins.
If False (default), return worst-case (minimum) margins.
Expand All @@ -553,7 +555,7 @@ def disk_margins(L, omega, skew=0.0, returnall=False):
DPM : float or array_like
Disk-based phase margin.

Example
Examples
--------
>> omega = np.logspace(-1, 3, 1001)
>> P = control.ss([[0, 10], [-10, 0]], np.eye(2), [[1, 10],
Expand Down
2 changes: 1 addition & 1 deletion control/matlab/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

This subpackage contains a number of functions that emulate some of
the functionality of MATLAB. The intent of these functions is to
provide a simple interface to the python control systems library
provide a simple interface to the Python Control Systems Library
(python-control) for people who are familiar with the MATLAB Control
Systems Toolbox (tm).

Expand Down
2 changes: 1 addition & 1 deletion control/pzmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ def replot(self, cplt: ControlPlot):

Parameters
----------
cplt: ControlPlot
cplt : `ControlPlot`
Graphics handles of the existing plot.
"""
pole_zero_replot(self, cplt)
Expand Down
21 changes: 12 additions & 9 deletions control/tests/discrete_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ class Tsys:
sys = rss(3, 1, 1)
T.siso_ss1 = StateSpace(sys.A, sys.B, sys.C, sys.D, None)
T.siso_ss1c = StateSpace(sys.A, sys.B, sys.C, sys.D, 0.0)
T.siso_ss1d = StateSpace(sys.A, sys.B, sys.C, sys.D, 0.1)
T.siso_ss2d = StateSpace(sys.A, sys.B, sys.C, sys.D, 0.2)
T.siso_ss3d = StateSpace(sys.A, sys.B, sys.C, sys.D, True)

dsys = ct.sample_system(sys, 1)
T.siso_ss1d = StateSpace(dsys.A, dsys.B, dsys.C, dsys.D, 0.1)
T.siso_ss2d = StateSpace(dsys.A, dsys.B, dsys.C, dsys.D, 0.2)
T.siso_ss3d = StateSpace(dsys.A, dsys.B, dsys.C, dsys.D, True)

# Two input, two output continuous-time system
A = [[-3., 4., 2.], [-1., -3., 0.], [2., 5., 3.]]
Expand All @@ -39,17 +41,18 @@ class Tsys:
T.mimo_ss1c = StateSpace(A, B, C, D, 0)

# Two input, two output discrete-time system
T.mimo_ss1d = StateSpace(A, B, C, D, 0.1)
T.mimo_ss1d = ct.sample_system(T.mimo_ss1c, 0.1)

# Same system, but with a different sampling time
T.mimo_ss2d = StateSpace(A, B, C, D, 0.2)
T.mimo_ss2d = StateSpace(
T.mimo_ss1d.A, T.mimo_ss1d.B, T.mimo_ss1d.C, T.mimo_ss1d.D, 0.2)

# Single input, single output continuus and discrete transfer function
T.siso_tf1 = TransferFunction([1, 1], [1, 2, 1], None)
T.siso_tf1c = TransferFunction([1, 1], [1, 2, 1], 0)
T.siso_tf1d = TransferFunction([1, 1], [1, 2, 1], 0.1)
T.siso_tf2d = TransferFunction([1, 1], [1, 2, 1], 0.2)
T.siso_tf3d = TransferFunction([1, 1], [1, 2, 1], True)
T.siso_tf1c = TransferFunction([1, 1], [1, 0.2, 1], 0)
T.siso_tf1d = TransferFunction([1, 1], [1, 0.2, 0.1], 0.1)
T.siso_tf2d = TransferFunction([1, 1], [1, 0.2, 0.1], 0.2)
T.siso_tf3d = TransferFunction([1, 1], [1, 0.2, 0.1], True)

return T

Expand Down
12 changes: 6 additions & 6 deletions control/tests/margin_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ def test_siso_disk_margin():
# Balanced (S - T) disk-based stability margins
DM, DGM, DPM = disk_margins(L, omega, skew=0.0)
assert_allclose([DM], [0.46], atol=0.1) # disk margin of 0.46
assert_allclose([DGM], [4.05], atol=0.1) # disk-based gain margin of 4.05 dB
assert_allclose([DGM], [4.05], atol=0.1) # disk-based gain margin of 4.05 dB
assert_allclose([DPM], [25.8], atol=0.1) # disk-based phase margin of 25.8 deg

# For SISO systems, the S-based (S) disk margin should match the third output
Expand All @@ -408,13 +408,13 @@ def test_mimo_disk_margin():
# Balanced (S - T) disk-based stability margins at plant output
DMo, DGMo, DPMo = disk_margins(Lo, omega, skew=0.0)
assert_allclose([DMo], [0.3754], atol=0.1) # disk margin of 0.3754
assert_allclose([DGMo], [3.3], atol=0.1) # disk-based gain margin of 3.3 dB
assert_allclose([DGMo], [3.3], atol=0.1) # disk-based gain margin of 3.3 dB
assert_allclose([DPMo], [21.26], atol=0.1) # disk-based phase margin of 21.26 deg

# Balanced (S - T) disk-based stability margins at plant input
DMi, DGMi, DPMi = disk_margins(Li, omega, skew=0.0)
assert_allclose([DMi], [0.3754], atol=0.1) # disk margin of 0.3754
assert_allclose([DGMi], [3.3], atol=0.1) # disk-based gain margin of 3.3 dB
assert_allclose([DGMi], [3.3], atol=0.1) # disk-based gain margin of 3.3 dB
assert_allclose([DPMi], [21.26], atol=0.1) # disk-based phase margin of 21.26 deg
else:
# Slycot not installed. Should throw exception.
Expand All @@ -435,7 +435,7 @@ def test_siso_disk_margin_return_all():
atol=0.01) # sensitivity peak at 1.94 rad/s
assert_allclose([min(DM)], [0.46], atol=0.1) # disk margin of 0.46
assert_allclose([DGM[np.argmin(DM)]], [4.05],\
atol=0.1) # disk-based gain margin of 4.05 dB
atol=0.1) # disk-based gain margin of 4.05 dB
assert_allclose([DPM[np.argmin(DM)]], [25.8],\
atol=0.1) # disk-based phase margin of 25.8 deg

Expand All @@ -457,7 +457,7 @@ def test_mimo_disk_margin_return_all():
atol=0.01) # sensitivity peak at 0 rad/s (or smallest provided)
assert_allclose([min(DMo)], [0.3754], atol=0.1) # disk margin of 0.3754
assert_allclose([DGMo[np.argmin(DMo)]], [3.3],\
atol=0.1) # disk-based gain margin of 3.3 dB
atol=0.1) # disk-based gain margin of 3.3 dB
assert_allclose([DPMo[np.argmin(DMo)]], [21.26],\
atol=0.1) # disk-based phase margin of 21.26 deg

Expand All @@ -468,7 +468,7 @@ def test_mimo_disk_margin_return_all():
assert_allclose([min(DMi)], [0.3754],\
atol=0.1) # disk margin of 0.3754
assert_allclose([DGMi[np.argmin(DMi)]], [3.3],\
atol=0.1) # disk-based gain margin of 3.3 dB
atol=0.1) # disk-based gain margin of 3.3 dB
assert_allclose([DPMi[np.argmin(DMi)]], [21.26],\
atol=0.1) # disk-based phase margin of 21.26 deg
else:
Expand Down
4 changes: 2 additions & 2 deletions doc/develop.rst
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ Filenames
* Source files are lower case, usually less than 10 characters (and 8
or less is better).

* Unit tests (in `control/tests/`) are of the form `module_test.py` or
`module_function.py`.
* Unit tests (in `control/tests/`) are of the form `module_test.py`,
`module_functionality_test.py`, or `functionality_test.py`.


Class names
Expand Down
1 change: 1 addition & 0 deletions doc/examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ other sources.
examples/mrac_siso_lyapunov
examples/markov
examples/era_msd
examples/disk_margins

Jupyter Notebooks
=================
Expand Down
1 change: 1 addition & 0 deletions doc/examples/disk_margins.py
6 changes: 3 additions & 3 deletions doc/intro.rst
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ some things to keep in mind:

* Vectors and matrices used as arguments to functions can be written
using lists, with commas required between elements and column
vectors implemented as nested list . So [1 2 3] must be written as
vectors implemented as nested lists. So [1 2 3] must be written as
[1, 2, 3] and matrices are written using 2D nested lists, e.g., [[1,
2], [3, 4]].
* Functions that in MATLAB would return variable numbers of values
Expand All @@ -150,12 +150,12 @@ This documentation has a number of notional conventions and functionality:
Manual, which contains documentation for all functions, classes,
configurable default parameters, and other detailed information.

* Class, functions, and methods with additional documentation appear
* Classes, functions, and methods with additional documentation appear
in a bold, code font that link to the Reference Manual. Example: `ss`.

* Links to other sections appear in blue. Example: :ref:`nonlinear-systems`.

* Parameters appear in a (non-bode) code font, as do code fragments.
* Parameters appear in a (non-bold) code font, as do code fragments.
Example: `omega`.

* Example code is contained in code blocks that can be copied using
Expand Down
19 changes: 10 additions & 9 deletions doc/releases/0.10.2-notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,16 @@ Version 0.10.2 Release Notes (current)
* `GitHub release page
<https://github.com/python-control/python-control/releases/tag/0.10.2>`_

This release contains numerous bug fixes and improvements, including
substantial updates to the documentation, including refactoring of the
online manual into a User Guide and a Reference Manual, as well as
more consistent and complete docstrings. In addition, signals and
systems can now be referenced using signal labels in addition to
offsets, and phase plane plots make use of the matplotlib
`~matplotlib.pyplot.streamplot` function. Numerous other changes have
been made to improve consistency of keyword arguments and function
names, with legacy aliases available.
This release includes numerous bug fixes and improvements, with major
changes such as a substantial reorganization of the documentation into
a User Guide and Reference Manual, more consistent and complete
docstrings, and support for referencing signals and subsystems by name
as well as by index. Phase plane plots now use matplotlib’s
`streamplot` for better visuals. New functions include `combine_tf`
and `split_tf` for MIMO/SISO conversion and `disk_margins` for
stability analysis. Additional improvements include consistent keyword
usage, expanded LTI system methods for plotting and responses, better
error messages, and legacy aliases to maintain backward compatibility.

This version of `python-control` requires Python 3.10 or higher, NumPy
1.23 or higher (2.x recommended), and SciPy 1.8 or higher.
Expand Down
6 changes: 3 additions & 3 deletions examples/python-control_tutorial.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@
"id": "qMVGK15gNQw2"
},
"source": [
"## Example 1: Open loop analysis of a coupled mass spring system\n",
"## Example 1: Open Loop Analysis of a Coupled Mass Spring System\n",
"\n",
"Consider the spring mass system below:\n",
"\n",
Expand Down Expand Up @@ -781,7 +781,7 @@
"id": "2f27f767-e012-45f9-8b76-cc040cfc89e2",
"metadata": {},
"source": [
"## Example 2: Trajectory tracking for a kinematic vehicle model\n",
"## Example 2: Trajectory Tracking for a Kinematic Vehicle Model\n",
"\n",
"This example illustrates the use of python-control to model, analyze, and design nonlinear control systems.\n",
"\n",
Expand Down Expand Up @@ -1213,7 +1213,7 @@
"id": "03b1fd75-579c-47da-805d-68f155957084",
"metadata": {},
"source": [
"## Computing environment"
"## Computing Environment"
]
},
{
Expand Down
3 changes: 1 addition & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,12 @@ build-backend = "setuptools.build_meta"
name = "control"
description = "Python Control Systems Library"
authors = [{name = "Python Control Developers", email = "python-control-developers@lists.sourceforge.net"}]
license = {text = "BSD-3-Clause"}
license = "BSD-3-Clause"
readme = "README.rst"
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Science/Research",
"Intended Audience :: Developers",
"License :: OSI Approved :: BSD License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
Expand Down
Loading