GeezerVison is showing github.com — discard zero imaginary part for sys.dcgain() · python-control/python-control@d8bddbb
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: control/lti.py +7Lines changed: 7 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -208,6 +208,13 @@ def dcgain(self): | 208 | 208 | raise NotImplementedError("dcgain not implemented for %s objects" % | 209 | 209 | str(self.__class__)) | 210 | 210 |
| | 211 | + def _dcgain(self, warn_infinite): | | 212 | + zeroresp = self(0 if self.isctime() else 1, | | 213 | + warn_infinite=warn_infinite) | | 214 | + if np.all(np.logical_or(np.isreal(zeroresp), np.isnan(zeroresp.imag))): | | 215 | + return zeroresp.real | | 216 | + else: | | 217 | + return zeroresp | 211 | 218 |
| 212 | 219 | # Test to see if a system is SISO | 213 | 220 | def issiso(sys, strict=False): | |
Collapse file Copy file name to clipboardExpand all lines: control/statesp.py +18-7Lines changed: 18 additions & 7 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -1216,16 +1216,27 @@ def dcgain(self, warn_infinite=False): | 1216 | 1216 |
| 1217 | 1217 | .. math: G(1) = C (I - A)^{-1} B + D | 1218 | 1218 |
| | 1219 | + Parameters | | 1220 | + ---------- | | 1221 | + warn_infinite : bool, optional | | 1222 | + By default, don't issue a warning message if the zero-frequency | | 1223 | + gain is infinite. Setting `warn_infinite` to generate the warning | | 1224 | + message. | | 1225 | +
| 1219 | 1226 | Returns | 1220 | 1227 | ------- | 1221 | | - gain : ndarray | 1222 | | - An array of shape (outputs,inputs); the array will either be the | 1223 | | - zero-frequency (or DC) gain, or, if the frequency response is | 1224 | | - singular, the array will be filled with (inf + nanj). | 1225 | | -
| | 1228 | + gain : (outputs, inputs) ndarray or scalar | | 1229 | + Array or scalar value for SISO systems, depending on | | 1230 | + config.defaults['control.squeeze_frequency_response']. | | 1231 | + The value of the array elements or the scalar is either the | | 1232 | + zero-frequency (or DC) gain, or `inf`, if the frequency response | | 1233 | + is singular. | | 1234 | +
| | 1235 | + For real valued systems, the empty imaginary part of the | | 1236 | + complex zero-frequency response is discarded and a real array or | | 1237 | + scalaris returned. | 1226 | 1238 | """ | 1227 | | - return self(0, warn_infinite=warn_infinite) if self.isctime() \ | 1228 | | - else self(1, warn_infinite=warn_infinite) | | 1239 | + return self._dcgain(warn_infinite) | 1229 | 1240 |
| 1230 | 1241 | def _isstatic(self): | 1231 | 1242 | """True if and only if the system has no dynamics, that is, | |
Collapse file Copy file name to clipboardExpand all lines: control/tests/freqresp_test.py +6-6Lines changed: 6 additions & 6 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -423,7 +423,7 @@ def test_dcgain_consistency(): | 423 | 423 | sys_ss = ctrl.tf2ss(sys_tf) | 424 | 424 | assert 0 in sys_ss.pole() | 425 | 425 |
| 426 | | - # Finite (real) numerator over 0 denominator => inf + nanj | | 426 | + # Finite (real) numerator over 0 denominator => inf | 427 | 427 | np.testing.assert_equal( | 428 | 428 | sys_tf(0, warn_infinite=False), complex(np.inf, np.nan)) | 429 | 429 | np.testing.assert_equal( | @@ -433,9 +433,9 @@ def test_dcgain_consistency(): | 433 | 433 | np.testing.assert_equal( | 434 | 434 | sys_ss(0j, warn_infinite=False), complex(np.inf, np.nan)) | 435 | 435 | np.testing.assert_equal( | 436 | | - sys_tf.dcgain(warn_infinite=False), complex(np.inf, np.nan)) | | 436 | + sys_tf.dcgain(warn_infinite=False), np.inf) | 437 | 437 | np.testing.assert_equal( | 438 | | - sys_ss.dcgain(warn_infinite=False), complex(np.inf, np.nan)) | | 438 | + sys_ss.dcgain(warn_infinite=False), np.inf) | 439 | 439 |
| 440 | 440 | # Set up transfer function with pole, zero at the origin | 441 | 441 | sys_tf = ctrl.tf([1, 0], [1, 0]) | @@ -448,7 +448,7 @@ def test_dcgain_consistency(): | 448 | 448 | np.testing.assert_equal( | 449 | 449 | sys_tf(0j, warn_infinite=False), complex(np.nan, np.nan)) | 450 | 450 | np.testing.assert_equal( | 451 | | - sys_tf.dcgain(warn_infinite=False), complex(np.nan, np.nan)) | | 451 | + sys_tf.dcgain(warn_infinite=False), np.nan) | 452 | 452 |
| 453 | 453 | # Set up state space version | 454 | 454 | sys_ss = ctrl.tf2ss(ctrl.tf([1, 0], [1, 1])) * \ | @@ -462,15 +462,15 @@ def test_dcgain_consistency(): | 462 | 462 | np.testing.assert_equal( | 463 | 463 | sys_ss(0j, warn_infinite=False), complex(np.nan, np.nan)) | 464 | 464 | np.testing.assert_equal( | 465 | | - sys_ss.dcgain(warn_infinite=False), complex(np.nan, np.nan)) | | 465 | + sys_ss.dcgain(warn_infinite=False), np.nan) | 466 | 466 | elif 0 in sys_ss.pole(): | 467 | 467 | # Pole at the origin, but zero elsewhere => should get (inf + nanj) | 468 | 468 | np.testing.assert_equal( | 469 | 469 | sys_ss(0, warn_infinite=False), complex(np.inf, np.nan)) | 470 | 470 | np.testing.assert_equal( | 471 | 471 | sys_ss(0j, warn_infinite=False), complex(np.inf, np.nan)) | 472 | 472 | np.testing.assert_equal( | 473 | | - sys_ss.dcgain(warn_infinite=False), complex(np.inf, np.nan)) | | 473 | + sys_ss.dcgain(warn_infinite=False), np.inf) | 474 | 474 | else: | 475 | 475 | # Near pole/zero cancellation => nothing sensible to check | 476 | 476 | pass | |
Collapse file Copy file name to clipboardExpand all lines: control/tests/statesp_test.py +3-4Lines changed: 3 additions & 4 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -498,7 +498,7 @@ def test_dc_gain_cont(self): | 498 | 498 | np.testing.assert_allclose(sys2.dcgain(), expected) | 499 | 499 |
| 500 | 500 | sys3 = StateSpace(0., 1., 1., 0.) | 501 | | - np.testing.assert_equal(sys3.dcgain(), complex(np.inf, np.nan)) | | 501 | + np.testing.assert_equal(sys3.dcgain(), np.inf) | 502 | 502 |
| 503 | 503 | def test_dc_gain_discr(self): | 504 | 504 | """Test DC gain for discrete-time state-space systems.""" | @@ -516,7 +516,7 @@ def test_dc_gain_discr(self): | 516 | 516 |
| 517 | 517 | # summer | 518 | 518 | sys = StateSpace(1, 1, 1, 0, True) | 519 | | - np.testing.assert_equal(sys.dcgain(), complex(np.inf, np.nan)) | | 519 | + np.testing.assert_equal(sys.dcgain(), np.inf) | 520 | 520 |
| 521 | 521 | @pytest.mark.parametrize("outputs", range(1, 6)) | 522 | 522 | @pytest.mark.parametrize("inputs", range(1, 6)) | @@ -539,7 +539,7 @@ def test_dc_gain_integrator(self, outputs, inputs, dt): | 539 | 539 | c = np.eye(max(outputs, states))[:outputs, :states] | 540 | 540 | d = np.zeros((outputs, inputs)) | 541 | 541 | sys = StateSpace(a, b, c, d, dt) | 542 | | - dc = np.full_like(d, complex(np.inf, np.nan), dtype=complex) | | 542 | + dc = np.full_like(d, np.inf, dtype=float) | 543 | 543 | if sys.issiso(): | 544 | 544 | dc = dc.squeeze() | 545 | 545 |
| @@ -953,4 +953,3 @@ def test_xferfcn_ndarray_precedence(op, tf, arr): | 953 | 953 | ss = ct.tf2ss(tf) | 954 | 954 | result = op(arr, ss) | 955 | 955 | assert isinstance(result, ct.StateSpace) | 956 | | - |
Collapse file Copy file name to clipboardExpand all lines: control/tests/xferfcn_test.py +3-3Lines changed: 3 additions & 3 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -807,7 +807,7 @@ def test_dcgain_cont(self): | 807 | 807 | np.testing.assert_equal(sys2.dcgain(), 2) | 808 | 808 |
| 809 | 809 | sys3 = TransferFunction(6, [1, 0]) | 810 | | - np.testing.assert_equal(sys3.dcgain(), complex(np.inf, np.nan)) | | 810 | + np.testing.assert_equal(sys3.dcgain(), np.inf) | 811 | 811 |
| 812 | 812 | num = [[[15], [21], [33]], [[10], [14], [22]]] | 813 | 813 | den = [[[1, 3], [2, 3], [3, 3]], [[1, 5], [2, 7], [3, 11]]] | @@ -827,13 +827,13 @@ def test_dcgain_discr(self): | 827 | 827 |
| 828 | 828 | # differencer | 829 | 829 | sys = TransferFunction(1, [1, -1], True) | 830 | | - np.testing.assert_equal(sys.dcgain(), complex(np.inf, np.nan)) | | 830 | + np.testing.assert_equal(sys.dcgain(), np.inf) | 831 | 831 |
| 832 | 832 | # differencer, with warning | 833 | 833 | sys = TransferFunction(1, [1, -1], True) | 834 | 834 | with pytest.warns(RuntimeWarning, match="divide by zero"): | 835 | 835 | np.testing.assert_equal( | 836 | | - sys.dcgain(warn_infinite=True), complex(np.inf, np.nan)) | | 836 | + sys.dcgain(warn_infinite=True), np.inf) | 837 | 837 |
| 838 | 838 | # summer | 839 | 839 | sys = TransferFunction([1, -1], [1], True) | |
Collapse file Copy file name to clipboardExpand all lines: control/xferfcn.py +11-4Lines changed: 11 additions & 4 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -1070,12 +1070,19 @@ def dcgain(self, warn_infinite=False): | 1070 | 1070 |
| 1071 | 1071 | Returns | 1072 | 1072 | ------- | 1073 | | - gain : ndarray | 1074 | | - The zero-frequency gain | | 1073 | + gain : (outputs, inputs) ndarray or scalar | | 1074 | + Array or scalar value for SISO systems, depending on | | 1075 | + config.defaults['control.squeeze_frequency_response']. | | 1076 | + The value of the array elements or the scalar is either the | | 1077 | + zero-frequency (or DC) gain, or `inf`, if the frequency response | | 1078 | + is singular. | | 1079 | +
| | 1080 | + For real valued systems, the empty imaginary part of the | | 1081 | + complex zero-frequency response is discarded and a real array or | | 1082 | + scalaris returned. | 1075 | 1083 |
| 1076 | 1084 | """ | 1077 | | - return self(0, warn_infinite=warn_infinite) if self.isctime() \ | 1078 | | - else self(1, warn_infinite=warn_infinite) | | 1085 | + return self._dcgain(warn_infinite) | 1079 | 1086 |
| 1080 | 1087 | def _isstatic(self): | 1081 | 1088 | """returns True if and only if all of the numerator and denominator | |
|