From df0051c2849bc7accf772ee80faeb752b0478002 Mon Sep 17 00:00:00 2001 From: bnavigator Date: Thu, 20 Aug 2020 21:14:48 +0200 Subject: [PATCH 1/5] update mpl_toolkit function --- control/grid.py | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/control/grid.py b/control/grid.py index a383dd27c..374b69ead 100644 --- a/control/grid.py +++ b/control/grid.py @@ -19,10 +19,13 @@ def __call__(self, direction, factor, values): class ModifiedExtremeFinderCycle(angle_helper.ExtremeFinderCycle): - '''Changed to allow only left hand-side polar grid''' + '''Changed to allow only left hand-side polar grid + + https://matplotlib.org/_modules/mpl_toolkits/axisartist/angle_helper.html#ExtremeFinderCycle.__call__ + ''' def __call__(self, transform_xy, x1, y1, x2, y2): - x_, y_ = np.linspace(x1, x2, self.nx), np.linspace(y1, y2, self.ny) - x, y = np.meshgrid(x_, y_) + x, y = np.meshgrid( + np.linspace(x1, x2, self.nx), np.linspace(y1, y2, self.ny)) lon, lat = transform_xy(np.ravel(x), np.ravel(y)) with np.errstate(invalid='ignore'): @@ -41,7 +44,25 @@ def __call__(self, transform_xy, x1, y1, x2, y2): lat_min, lat_max = np.nanmin(lat), np.nanmax(lat) lon_min, lon_max, lat_min, lat_max = \ - self._adjust_extremes(lon_min, lon_max, lat_min, lat_max) + self._add_pad(lon_min, lon_max, lat_min, lat_max) + + # check cycle + if self.lon_cycle: + lon_max = min(lon_max, lon_min + self.lon_cycle) + if self.lat_cycle: + lat_max = min(lat_max, lat_min + self.lat_cycle) + + if self.lon_minmax is not None: + min0 = self.lon_minmax[0] + lon_min = max(min0, lon_min) + max0 = self.lon_minmax[1] + lon_max = min(max0, lon_max) + + if self.lat_minmax is not None: + min0 = self.lat_minmax[0] + lat_min = max(min0, lat_min) + max0 = self.lat_minmax[1] + lat_max = min(max0, lat_max) return lon_min, lon_max, lat_min, lat_max From abc0c3c002b485b2a05101a8459628ad9eafe333 Mon Sep 17 00:00:00 2001 From: bnavigator Date: Thu, 20 Aug 2020 21:35:47 +0200 Subject: [PATCH 2/5] fix grid formatter --- control/grid.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/control/grid.py b/control/grid.py index 374b69ead..0d0e8b2ea 100644 --- a/control/grid.py +++ b/control/grid.py @@ -12,7 +12,7 @@ class FormatterDMS(object): '''Transforms angle ticks to damping ratios''' def __call__(self, direction, factor, values): - angles_deg = values/factor + angles_deg = np.asarray(values)/factor damping_ratios = np.cos((180-angles_deg) * np.pi/180) ret = ["%.2f" % val for val in damping_ratios] return ret From bdbd1985bbb07f5877825f956aeb13ab765094f4 Mon Sep 17 00:00:00 2001 From: bnavigator Date: Thu, 20 Aug 2020 23:21:53 +0200 Subject: [PATCH 3/5] fix wrong config read on pzmap --- control/pzmap.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/control/pzmap.py b/control/pzmap.py index fe8e551a0..012fccca3 100644 --- a/control/pzmap.py +++ b/control/pzmap.py @@ -87,8 +87,8 @@ def pzmap(sys, plot=True, grid=False, title='Pole Zero Map', **kwargs): plot = kwargs['Plot'] # Get parameter values - plot = config._get_param('rlocus', 'plot', plot, True) - grid = config._get_param('rlocus', 'grid', grid, False) + plot = config._get_param('pzmap', 'plot', plot, True) + grid = config._get_param('pzmap', 'grid', grid, False) if not isinstance(sys, LTI): raise TypeError('Argument ``sys``: must be a linear system.') From 2ca522083594993c91db98f700ce8c43b86a77fd Mon Sep 17 00:00:00 2001 From: bnavigator Date: Thu, 20 Aug 2020 23:22:15 +0200 Subject: [PATCH 4/5] add pzmap_test --- control/tests/conftest.py | 28 ++++++++++++++- control/tests/pzmap_test.py | 71 +++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 1 deletion(-) create mode 100755 control/tests/pzmap_test.py diff --git a/control/tests/conftest.py b/control/tests/conftest.py index e98bbe1d7..60c3d0de1 100755 --- a/control/tests/conftest.py +++ b/control/tests/conftest.py @@ -1,13 +1,39 @@ # contest.py - pytest local plugins and fixtures -import control import os +import matplotlib as mpl import pytest +import control + @pytest.fixture(scope="session", autouse=True) def use_numpy_ndarray(): """Switch the config to use ndarray instead of matrix""" if os.getenv("PYTHON_CONTROL_STATESPACE_ARRAY") == "1": control.config.defaults['statesp.use_numpy_matrix'] = False + + +@pytest.fixture(scope="function") +def editsdefaults(): + """Make sure any changes to the defaults only last during a test""" + restore = control.config.defaults.copy() + yield + control.config.defaults.update(restore) + + +@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") diff --git a/control/tests/pzmap_test.py b/control/tests/pzmap_test.py new file mode 100755 index 000000000..5903999b0 --- /dev/null +++ b/control/tests/pzmap_test.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- +""" pzmap_test.py - test pzmap() + +Created on Thu Aug 20 20:06:21 2020 + +@author: bnavigator +""" + +import numpy as np +import pytest +from matplotlib import pyplot as plt + +from control import TransferFunction, config, pzmap + + +@pytest.mark.parametrize("kwargs", + [pytest.param(dict(), id="default"), + pytest.param(dict(plot=False), id="plot=False"), + pytest.param(dict(plot=True), id="plot=True"), + pytest.param(dict(grid=True), id="grid=True"), + pytest.param(dict(title="My Title"), id="title")]) +@pytest.mark.parametrize("setdefaults", [False, True], ids=["kw", "config"]) +@pytest.mark.parametrize("dt", [0, 1], ids=["s", "z"]) +def test_pzmap(kwargs, setdefaults, dt, editsdefaults, mplcleanup): + """Test pzmap""" + # T from from pvtol-nested example + T = TransferFunction([-9.0250000e-01, -4.7200750e+01, -8.6812900e+02, + +5.6261850e+03, +2.1258472e+05, +8.4724600e+05, + +1.0192000e+06, +2.3520000e+05], + [9.02500000e-03, 9.92862812e-01, 4.96974094e+01, + 1.35705659e+03, 2.09294163e+04, 1.64898435e+05, + 6.54572220e+05, 1.25274600e+06, 1.02420000e+06, + 2.35200000e+05], + dt) + + Pref = [-23.8877+19.3837j, -23.8877-19.3837j, -23.8349+15.7846j, + -23.8349-15.7846j, -5.2320 +0.4117j, -5.2320 -0.4117j, + -2.2246 +0.0000j, -1.5160 +0.0000j, -0.3627 +0.0000j] + Zref = [-23.8877+19.3837j, -23.8877-19.3837j, +14.3637 +0.0000j, + -14.3637 +0.0000j, -2.2246 +0.0000j, -2.0000 +0.0000j, + -0.3000 +0.0000j] + + pzkwargs = kwargs.copy() + if setdefaults: + for k in ['plot', 'grid']: + if k in pzkwargs: + v = pzkwargs.pop(k) + config.set_defaults('pzmap', **{k: v}) + + P, Z = pzmap(T, **pzkwargs) + + np.testing.assert_allclose(P, Pref, rtol=1e-3) + np.testing.assert_allclose(Z, Zref, rtol=1e-3) + + if kwargs.get('plot', True): + ax = plt.gca() + assert ax.get_title() == kwargs.get('title', 'Pole Zero Map') + if kwargs.get('grid', False): + # TODO: check for correct grid + pass + + +def test_pzmap_warns(): + with pytest.warns(FutureWarning): + pzmap(TransferFunction([1], [1, 2]), Plot=True) + + +def test_pzmap_raises(): + with pytest.raises(TypeError): + # not an LTI system + pzmap(([1], [1,2])) From 5632796bea9d6047b2b5cfdf0df4b54545840905 Mon Sep 17 00:00:00 2001 From: bnavigator Date: Fri, 21 Aug 2020 01:31:11 +0200 Subject: [PATCH 5/5] test for correct grid and no plot. fix pzmap config handling --- control/grid.py | 8 +++----- control/pzmap.py | 2 +- control/tests/pzmap_test.py | 22 ++++++++++++++++++++-- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/control/grid.py b/control/grid.py index 0d0e8b2ea..07ca4a59d 100644 --- a/control/grid.py +++ b/control/grid.py @@ -34,11 +34,9 @@ def __call__(self, transform_xy, x1, y1, x2, y2): # Changed from 180 to 360 to be able to span only # 90-270 (left hand side) lon -= 360. * ((lon - lon0) > 360.) - if self.lat_cycle is not None: + if self.lat_cycle is not None: # pragma: no cover lat0 = np.nanmin(lat) - # Changed from 180 to 360 to be able to span only - # 90-270 (left hand side) - lat -= 360. * ((lat - lat0) > 360.) + lat -= 360. * ((lat - lat0) > 180.) lon_min, lon_max = np.nanmin(lon), np.nanmax(lon) lat_min, lat_max = np.nanmin(lat), np.nanmax(lat) @@ -49,7 +47,7 @@ def __call__(self, transform_xy, x1, y1, x2, y2): # check cycle if self.lon_cycle: lon_max = min(lon_max, lon_min + self.lon_cycle) - if self.lat_cycle: + if self.lat_cycle: # pragma: no cover lat_max = min(lat_max, lat_min + self.lat_cycle) if self.lon_minmax is not None: diff --git a/control/pzmap.py b/control/pzmap.py index 012fccca3..a7752e484 100644 --- a/control/pzmap.py +++ b/control/pzmap.py @@ -58,7 +58,7 @@ # TODO: Implement more elegant cross-style axes. See: # http://matplotlib.sourceforge.net/examples/axes_grid/demo_axisline_style.html # http://matplotlib.sourceforge.net/examples/axes_grid/demo_curvelinear_grid.html -def pzmap(sys, plot=True, grid=False, title='Pole Zero Map', **kwargs): +def pzmap(sys, plot=None, grid=None, title='Pole Zero Map', **kwargs): """ Plot a pole/zero map for a linear system. diff --git a/control/tests/pzmap_test.py b/control/tests/pzmap_test.py index 5903999b0..8d41807b8 100755 --- a/control/tests/pzmap_test.py +++ b/control/tests/pzmap_test.py @@ -6,9 +6,11 @@ @author: bnavigator """ +import matplotlib import numpy as np import pytest from matplotlib import pyplot as plt +from mpl_toolkits.axisartist import Axes as mpltAxes from control import TransferFunction, config, pzmap @@ -54,10 +56,26 @@ def test_pzmap(kwargs, setdefaults, dt, editsdefaults, mplcleanup): if kwargs.get('plot', True): ax = plt.gca() + assert ax.get_title() == kwargs.get('title', 'Pole Zero Map') + + # FIXME: This won't work when zgrid and sgrid are unified + children = ax.get_children() + has_zgrid = False + for c in children: + if isinstance(c, matplotlib.text.Annotation): + if r'\pi' in c.get_text(): + has_zgrid = True + has_sgrid = isinstance(ax, mpltAxes) + if kwargs.get('grid', False): - # TODO: check for correct grid - pass + assert dt == has_zgrid + assert dt != has_sgrid + else: + assert not has_zgrid + assert not has_sgrid + else: + assert not plt.get_fignums() def test_pzmap_warns(): pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy