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..321580ba7 100644 --- a/control/tests/freqresp_test.py +++ b/control/tests/freqresp_test.py @@ -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(), np.inf) np.testing.assert_equal( - sys_ss.dcgain(warn_infinite=False), complex(np.inf, np.nan)) + 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), complex(np.nan, 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), complex(np.nan, 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), complex(np.inf, np.nan)) + sys_ss.dcgain(), 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
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: