From 8c9e807ec64c62cde1dc6e174580099dfac8afd9 Mon Sep 17 00:00:00 2001 From: Ben Greiner Date: Fri, 19 Mar 2021 23:44:48 +0100 Subject: [PATCH 1/2] discard zero imaginary part for sys.dcgain() --- control/lti.py | 7 +++++++ control/statesp.py | 25 ++++++++++++++++++------- control/tests/freqresp_test.py | 12 ++++++------ control/tests/statesp_test.py | 7 +++---- control/tests/xferfcn_test.py | 6 +++--- control/xferfcn.py | 15 +++++++++++---- 6 files changed, 48 insertions(+), 24 deletions(-) diff --git a/control/lti.py b/control/lti.py index 30569863a..01d04e020 100644 --- a/control/lti.py +++ b/control/lti.py @@ -208,6 +208,13 @@ def dcgain(self): raise NotImplementedError("dcgain not implemented for %s objects" % str(self.__class__)) + def _dcgain(self, warn_infinite): + zeroresp = self(0 if self.isctime() else 1, + warn_infinite=warn_infinite) + if np.all(np.logical_or(np.isreal(zeroresp), np.isnan(zeroresp.imag))): + return zeroresp.real + else: + return zeroresp # Test to see if a system is SISO def issiso(sys, strict=False): diff --git a/control/statesp.py b/control/statesp.py index d2b613024..c75e6f66a 100644 --- a/control/statesp.py +++ b/control/statesp.py @@ -1216,16 +1216,27 @@ def dcgain(self, warn_infinite=False): .. math: G(1) = C (I - A)^{-1} B + D + Parameters + ---------- + warn_infinite : bool, optional + By default, don't issue a warning message if the zero-frequency + gain is infinite. Setting `warn_infinite` to generate the warning + message. + Returns ------- - gain : ndarray - An array of shape (outputs,inputs); the array will either be the - zero-frequency (or DC) gain, or, if the frequency response is - singular, the array will be filled with (inf + nanj). - + gain : (outputs, inputs) ndarray or scalar + Array or scalar value for SISO systems, depending on + config.defaults['control.squeeze_frequency_response']. + The value of the array elements or the scalar is either the + zero-frequency (or DC) gain, or `inf`, if the frequency response + is singular. + + For real valued systems, the empty imaginary part of the + complex zero-frequency response is discarded and a real array or + scalar is returned. """ - return self(0, warn_infinite=warn_infinite) if self.isctime() \ - else self(1, warn_infinite=warn_infinite) + return self._dcgain(warn_infinite) def _isstatic(self): """True if and only if the system has no dynamics, that is, diff --git a/control/tests/freqresp_test.py b/control/tests/freqresp_test.py index 983330af0..2ef426151 100644 --- a/control/tests/freqresp_test.py +++ b/control/tests/freqresp_test.py @@ -423,7 +423,7 @@ def test_dcgain_consistency(): sys_ss = ctrl.tf2ss(sys_tf) assert 0 in sys_ss.pole() - # Finite (real) numerator over 0 denominator => inf + nanj + # Finite (real) numerator over 0 denominator => inf np.testing.assert_equal( sys_tf(0, warn_infinite=False), complex(np.inf, np.nan)) np.testing.assert_equal( @@ -433,9 +433,9 @@ def test_dcgain_consistency(): np.testing.assert_equal( sys_ss(0j, warn_infinite=False), complex(np.inf, np.nan)) np.testing.assert_equal( - sys_tf.dcgain(warn_infinite=False), complex(np.inf, np.nan)) + sys_tf.dcgain(warn_infinite=False), np.inf) np.testing.assert_equal( - sys_ss.dcgain(warn_infinite=False), complex(np.inf, np.nan)) + sys_ss.dcgain(warn_infinite=False), np.inf) # Set up transfer function with pole, zero at the origin sys_tf = ctrl.tf([1, 0], [1, 0]) @@ -448,7 +448,7 @@ def test_dcgain_consistency(): np.testing.assert_equal( sys_tf(0j, warn_infinite=False), complex(np.nan, np.nan)) np.testing.assert_equal( - sys_tf.dcgain(warn_infinite=False), complex(np.nan, np.nan)) + sys_tf.dcgain(warn_infinite=False), np.nan) # Set up state space version sys_ss = ctrl.tf2ss(ctrl.tf([1, 0], [1, 1])) * \ @@ -462,7 +462,7 @@ def test_dcgain_consistency(): np.testing.assert_equal( sys_ss(0j, warn_infinite=False), complex(np.nan, np.nan)) np.testing.assert_equal( - sys_ss.dcgain(warn_infinite=False), complex(np.nan, np.nan)) + sys_ss.dcgain(warn_infinite=False), np.nan) elif 0 in sys_ss.pole(): # Pole at the origin, but zero elsewhere => should get (inf + nanj) np.testing.assert_equal( @@ -470,7 +470,7 @@ def test_dcgain_consistency(): np.testing.assert_equal( sys_ss(0j, warn_infinite=False), complex(np.inf, np.nan)) np.testing.assert_equal( - sys_ss.dcgain(warn_infinite=False), complex(np.inf, np.nan)) + sys_ss.dcgain(warn_infinite=False), np.inf) else: # Near pole/zero cancellation => nothing sensible to check pass diff --git a/control/tests/statesp_test.py b/control/tests/statesp_test.py index 983b9d7a6..6a7509001 100644 --- a/control/tests/statesp_test.py +++ b/control/tests/statesp_test.py @@ -498,7 +498,7 @@ def test_dc_gain_cont(self): np.testing.assert_allclose(sys2.dcgain(), expected) sys3 = StateSpace(0., 1., 1., 0.) - np.testing.assert_equal(sys3.dcgain(), complex(np.inf, np.nan)) + np.testing.assert_equal(sys3.dcgain(), np.inf) def test_dc_gain_discr(self): """Test DC gain for discrete-time state-space systems.""" @@ -516,7 +516,7 @@ def test_dc_gain_discr(self): # summer sys = StateSpace(1, 1, 1, 0, True) - np.testing.assert_equal(sys.dcgain(), complex(np.inf, np.nan)) + np.testing.assert_equal(sys.dcgain(), np.inf) @pytest.mark.parametrize("outputs", range(1, 6)) @pytest.mark.parametrize("inputs", range(1, 6)) @@ -539,7 +539,7 @@ def test_dc_gain_integrator(self, outputs, inputs, dt): c = np.eye(max(outputs, states))[:outputs, :states] d = np.zeros((outputs, inputs)) sys = StateSpace(a, b, c, d, dt) - dc = np.full_like(d, complex(np.inf, np.nan), dtype=complex) + dc = np.full_like(d, np.inf, dtype=float) if sys.issiso(): dc = dc.squeeze() @@ -953,4 +953,3 @@ def test_xferfcn_ndarray_precedence(op, tf, arr): ss = ct.tf2ss(tf) result = op(arr, ss) assert isinstance(result, ct.StateSpace) - diff --git a/control/tests/xferfcn_test.py b/control/tests/xferfcn_test.py index b892655e9..06e7fc9d8 100644 --- a/control/tests/xferfcn_test.py +++ b/control/tests/xferfcn_test.py @@ -807,7 +807,7 @@ def test_dcgain_cont(self): np.testing.assert_equal(sys2.dcgain(), 2) sys3 = TransferFunction(6, [1, 0]) - np.testing.assert_equal(sys3.dcgain(), complex(np.inf, np.nan)) + np.testing.assert_equal(sys3.dcgain(), np.inf) num = [[[15], [21], [33]], [[10], [14], [22]]] den = [[[1, 3], [2, 3], [3, 3]], [[1, 5], [2, 7], [3, 11]]] @@ -827,13 +827,13 @@ def test_dcgain_discr(self): # differencer sys = TransferFunction(1, [1, -1], True) - np.testing.assert_equal(sys.dcgain(), complex(np.inf, np.nan)) + np.testing.assert_equal(sys.dcgain(), np.inf) # differencer, with warning sys = TransferFunction(1, [1, -1], True) with pytest.warns(RuntimeWarning, match="divide by zero"): np.testing.assert_equal( - sys.dcgain(warn_infinite=True), complex(np.inf, np.nan)) + sys.dcgain(warn_infinite=True), np.inf) # summer sys = TransferFunction([1, -1], [1], True) diff --git a/control/xferfcn.py b/control/xferfcn.py index 50e4870a8..3e48c7f24 100644 --- a/control/xferfcn.py +++ b/control/xferfcn.py @@ -1070,12 +1070,19 @@ def dcgain(self, warn_infinite=False): Returns ------- - gain : ndarray - The zero-frequency gain + gain : (outputs, inputs) ndarray or scalar + Array or scalar value for SISO systems, depending on + config.defaults['control.squeeze_frequency_response']. + The value of the array elements or the scalar is either the + zero-frequency (or DC) gain, or `inf`, if the frequency response + is singular. + + For real valued systems, the empty imaginary part of the + complex zero-frequency response is discarded and a real array or + scalar is returned. """ - return self(0, warn_infinite=warn_infinite) if self.isctime() \ - else self(1, warn_infinite=warn_infinite) + return self._dcgain(warn_infinite) def _isstatic(self): """returns True if and only if all of the numerator and denominator From 9a54254eec86d99aacbf3df30e693c139dc2af1c Mon Sep 17 00:00:00 2001 From: Ben Greiner Date: Sat, 20 Mar 2021 00:55:28 +0100 Subject: [PATCH 2/2] Apply review suggestions by @murrayrm: revert comment change, remove parameter --- control/tests/freqresp_test.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/control/tests/freqresp_test.py b/control/tests/freqresp_test.py index 2ef426151..321580ba7 100644 --- a/control/tests/freqresp_test.py +++ b/control/tests/freqresp_test.py @@ -423,7 +423,7 @@ def test_dcgain_consistency(): sys_ss = ctrl.tf2ss(sys_tf) assert 0 in sys_ss.pole() - # Finite (real) numerator over 0 denominator => inf + # Finite (real) numerator over 0 denominator => inf + nanj np.testing.assert_equal( sys_tf(0, warn_infinite=False), complex(np.inf, np.nan)) np.testing.assert_equal( @@ -433,9 +433,9 @@ def test_dcgain_consistency(): np.testing.assert_equal( sys_ss(0j, warn_infinite=False), complex(np.inf, np.nan)) np.testing.assert_equal( - sys_tf.dcgain(warn_infinite=False), np.inf) + sys_tf.dcgain(), np.inf) np.testing.assert_equal( - sys_ss.dcgain(warn_infinite=False), np.inf) + sys_ss.dcgain(), np.inf) # Set up transfer function with pole, zero at the origin sys_tf = ctrl.tf([1, 0], [1, 0]) @@ -448,7 +448,7 @@ def test_dcgain_consistency(): np.testing.assert_equal( sys_tf(0j, warn_infinite=False), complex(np.nan, np.nan)) np.testing.assert_equal( - sys_tf.dcgain(warn_infinite=False), np.nan) + sys_tf.dcgain(), np.nan) # Set up state space version sys_ss = ctrl.tf2ss(ctrl.tf([1, 0], [1, 1])) * \ @@ -462,7 +462,7 @@ def test_dcgain_consistency(): np.testing.assert_equal( sys_ss(0j, warn_infinite=False), complex(np.nan, np.nan)) np.testing.assert_equal( - sys_ss.dcgain(warn_infinite=False), np.nan) + sys_ss.dcgain(), np.nan) elif 0 in sys_ss.pole(): # Pole at the origin, but zero elsewhere => should get (inf + nanj) np.testing.assert_equal( @@ -470,7 +470,7 @@ def test_dcgain_consistency(): np.testing.assert_equal( sys_ss(0j, warn_infinite=False), complex(np.inf, np.nan)) np.testing.assert_equal( - sys_ss.dcgain(warn_infinite=False), np.inf) + sys_ss.dcgain(), np.inf) else: # Near pole/zero cancellation => nothing sensible to check pass 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