@@ -367,7 +367,7 @@ def test_phase_wrap(TF, wrap_phase, min_phase, max_phase):
367367
368368
369369def test_freqresp_warn_infinite ():
370- """Test evaluation of transfer functions at the origin"""
370+ """Test evaluation warnings for transfer functions w/ pole at the origin"""
371371 sys_finite = ctrl .tf ([1 ], [1 , 0.01 ])
372372 sys_infinite = ctrl .tf ([1 ], [1 , 0.01 , 0 ])
373373
@@ -378,11 +378,13 @@ def test_freqresp_warn_infinite():
378378
379379 # Transfer function with infinite zero frequency gain
380380 with pytest .warns (RuntimeWarning , match = "divide by zero" ):
381- np .testing .assert_almost_equal (sys_infinite (0 ), np .inf )
381+ np .testing .assert_almost_equal (
382+ sys_infinite (0 ), complex (np .inf , np .nan ))
382383 with pytest .warns (RuntimeWarning , match = "divide by zero" ):
383384 np .testing .assert_almost_equal (
384- sys_infinite (0 , warn_infinite = True ), np .inf )
385- np .testing .assert_almost_equal (sys_infinite (0 , warn_infinite = False ), np .inf )
385+ sys_infinite (0 , warn_infinite = True ), complex (np .inf , np .nan ))
386+ np .testing .assert_almost_equal (
387+ sys_infinite (0 , warn_infinite = False ), complex (np .inf , np .nan ))
386388
387389 # Switch to state space
388390 sys_finite = ctrl .tf2ss (sys_finite )
@@ -394,13 +396,15 @@ def test_freqresp_warn_infinite():
394396 np .testing .assert_almost_equal (sys_finite (0 , warn_infinite = True ), 100 )
395397
396398 # State space system with infinite zero frequency gain
397- with pytest .warns (RuntimeWarning , match = "not finite" ):
398- np .testing .assert_almost_equal (sys_infinite (0 ), np .inf )
399- with pytest .warns (RuntimeWarning , match = "not finite" ):
400- np .testing .assert_almost_equal (sys_infinite (0 ), np .inf )
401- np .testing .assert_almost_equal (sys_infinite (0 , warn_infinite = True ), np .inf )
402- np .testing .assert_almost_equal (sys_infinite (0 , warn_infinite = False ), np .inf )
403-
399+ with pytest .warns (RuntimeWarning , match = "singular matrix" ):
400+ np .testing .assert_almost_equal (
401+ sys_infinite (0 ), complex (np .inf , np .nan ))
402+ with pytest .warns (RuntimeWarning , match = "singular matrix" ):
403+ np .testing .assert_almost_equal (
404+ sys_infinite (0 , warn_infinite = True ), complex (np .inf , np .nan ))
405+ np .testing .assert_almost_equal (sys_infinite (
406+ 0 , warn_infinite = False ), complex (np .inf , np .nan ))
407+
404408
405409def test_dcgain_consistency ():
406410 """Test to make sure that DC gain is consistently evaluated"""
@@ -412,25 +416,74 @@ def test_dcgain_consistency():
412416 sys_ss = ctrl .tf2ss (sys_tf )
413417 assert 0 in sys_ss .pole ()
414418
415- # Evaluation
416- np .testing .assert_equal (sys_tf (0 ), np .inf + 0j )
417- np .testing .assert_equal (sys_ss (0 ), np .inf + 0j )
418- np .testing .assert_equal (sys_tf .dcgain (), np .inf + 0j )
419- np .testing .assert_equal (sys_ss .dcgain (), np .inf + 0j )
419+ # Finite (real) numerator over 0 denominator => inf + nanj
420+ np .testing .assert_equal (
421+ sys_tf (0 , warn_infinite = False ), complex (np .inf , np .nan ))
422+ np .testing .assert_equal (
423+ sys_ss (0 , warn_infinite = False ), complex (np .inf , np .nan ))
424+ np .testing .assert_equal (
425+ sys_tf (0j , warn_infinite = False ), complex (np .inf , np .nan ))
426+ np .testing .assert_equal (
427+ sys_ss (0j , warn_infinite = False ), complex (np .inf , np .nan ))
428+ np .testing .assert_equal (
429+ sys_tf .dcgain (warn_infinite = False ), complex (np .inf , np .nan ))
430+ np .testing .assert_equal (
431+ sys_ss .dcgain (warn_infinite = False ), complex (np .inf , np .nan ))
420432
421433 # Set up transfer function with pole, zero at the origin
422434 sys_tf = ctrl .tf ([1 , 0 ], [1 , 0 ])
423435 assert 0 in sys_tf .pole ()
424436 assert 0 in sys_tf .zero ()
425-
437+
426438 sys_ss = ctrl .tf2ss (ctrl .tf ([1 , 0 ], [1 , 1 ])) * \
427439 ctrl .tf2ss (ctrl .tf ([1 ], [1 , 0 ]))
428440 assert 0 in sys_ss .pole ()
429441 assert 0 in sys_ss .zero ()
430442
431- # Pole and zero at the origin should give nan for the response
432- np .testing .assert_equal (sys_tf (0 ), np .nan )
433- np .testing .assert_equal (sys_tf .dcgain (), np .nan )
434- # TODO: state space cases not yet working
435- # np.testing.assert_equal(sys_ss(0), np.nan)
436- # np.testing.assert_equal(sys_ss.dcgain(), np.nan)
443+ # Pole and zero at the origin should give nan + nanj for the response
444+ np .testing .assert_equal (
445+ sys_tf (0 , warn_infinite = False ), complex (np .nan , np .nan ))
446+ np .testing .assert_equal (
447+ sys_tf (0j , warn_infinite = False ), complex (np .nan , np .nan ))
448+ np .testing .assert_equal (
449+ sys_tf .dcgain (warn_infinite = False ), complex (np .nan , np .nan ))
450+ np .testing .assert_equal (
451+ sys_ss (0 , warn_infinite = False ), complex (np .nan , np .nan ))
452+ np .testing .assert_equal (
453+ sys_ss (0j , warn_infinite = False ), complex (np .nan , np .nan ))
454+ np .testing .assert_equal (
455+ sys_ss .dcgain (warn_infinite = False ), complex (np .nan , np .nan ))
456+
457+ # Pole with non-zero, complex numerator => inf + infj
458+ s = ctrl .tf ('s' )
459+ sys_tf = (s + 1 ) / (s ** 2 + 1 )
460+ assert 1j in sys_tf .pole ()
461+
462+ # Set up state space system with pole on imaginary axis
463+ sys_ss = ctrl .tf2ss (sys_tf )
464+ assert 1j in sys_tf .pole ()
465+
466+ # Make sure we get correct response if evaluated at the pole
467+ np .testing .assert_equal (
468+ sys_tf (1j , warn_infinite = False ), complex (np .inf , np .inf ))
469+
470+ # For state space, numerical errors come into play
471+ resp_ss = sys_ss (1j , warn_infinite = False )
472+ if np .isfinite (resp_ss ):
473+ assert abs (resp_ss ) > 1e15
474+ else :
475+ if resp_ss != complex (np .inf , np .inf ):
476+ pytest .xfail ("statesp evaluation at poles not fully implemented" )
477+ else :
478+ np .testing .assert_equal (resp_ss , complex (np .inf , np .inf ))
479+
480+ # DC gain is finite
481+ np .testing .assert_almost_equal (sys_tf .dcgain (), 1. )
482+ np .testing .assert_almost_equal (sys_ss .dcgain (), 1. )
483+
484+ # Make sure that we get the *signed* DC gain
485+ sys_tf = - 1 / (s + 1 )
486+ np .testing .assert_almost_equal (sys_tf .dcgain (), - 1 )
487+
488+ sys_ss = ctrl .tf2ss (sys_tf )
489+ np .testing .assert_almost_equal (sys_ss .dcgain (), - 1 )
0 commit comments