@@ -371,3 +371,140 @@ def test_phase_wrap(TF, wrap_phase, min_phase, max_phase):
371
371
mag , phase , omega = ctrl .bode (TF , wrap_phase = wrap_phase )
372
372
assert (min (phase ) >= min_phase )
373
373
assert (max (phase ) <= max_phase )
374
+
375
+
376
+ def test_freqresp_warn_infinite ():
377
+ """Test evaluation warnings for transfer functions w/ pole at the origin"""
378
+ sys_finite = ctrl .tf ([1 ], [1 , 0.01 ])
379
+ sys_infinite = ctrl .tf ([1 ], [1 , 0.01 , 0 ])
380
+
381
+ # Transfer function with finite zero frequency gain
382
+ np .testing .assert_almost_equal (sys_finite (0 ), 100 )
383
+ np .testing .assert_almost_equal (sys_finite (0 , warn_infinite = False ), 100 )
384
+ np .testing .assert_almost_equal (sys_finite (0 , warn_infinite = True ), 100 )
385
+
386
+ # Transfer function with infinite zero frequency gain
387
+ with pytest .warns (RuntimeWarning , match = "divide by zero" ):
388
+ np .testing .assert_almost_equal (
389
+ sys_infinite (0 ), complex (np .inf , np .nan ))
390
+ with pytest .warns (RuntimeWarning , match = "divide by zero" ):
391
+ np .testing .assert_almost_equal (
392
+ sys_infinite (0 , warn_infinite = True ), complex (np .inf , np .nan ))
393
+ np .testing .assert_almost_equal (
394
+ sys_infinite (0 , warn_infinite = False ), complex (np .inf , np .nan ))
395
+
396
+ # Switch to state space
397
+ sys_finite = ctrl .tf2ss (sys_finite )
398
+ sys_infinite = ctrl .tf2ss (sys_infinite )
399
+
400
+ # State space system with finite zero frequency gain
401
+ np .testing .assert_almost_equal (sys_finite (0 ), 100 )
402
+ np .testing .assert_almost_equal (sys_finite (0 , warn_infinite = False ), 100 )
403
+ np .testing .assert_almost_equal (sys_finite (0 , warn_infinite = True ), 100 )
404
+
405
+ # State space system with infinite zero frequency gain
406
+ with pytest .warns (RuntimeWarning , match = "singular matrix" ):
407
+ np .testing .assert_almost_equal (
408
+ sys_infinite (0 ), complex (np .inf , np .nan ))
409
+ with pytest .warns (RuntimeWarning , match = "singular matrix" ):
410
+ np .testing .assert_almost_equal (
411
+ sys_infinite (0 , warn_infinite = True ), complex (np .inf , np .nan ))
412
+ np .testing .assert_almost_equal (sys_infinite (
413
+ 0 , warn_infinite = False ), complex (np .inf , np .nan ))
414
+
415
+
416
+ def test_dcgain_consistency ():
417
+ """Test to make sure that DC gain is consistently evaluated"""
418
+ # Set up transfer function with pole at the origin
419
+ sys_tf = ctrl .tf ([1 ], [1 , 0 ])
420
+ assert 0 in sys_tf .pole ()
421
+
422
+ # Set up state space system with pole at the origin
423
+ sys_ss = ctrl .tf2ss (sys_tf )
424
+ assert 0 in sys_ss .pole ()
425
+
426
+ # Finite (real) numerator over 0 denominator => inf + nanj
427
+ np .testing .assert_equal (
428
+ sys_tf (0 , warn_infinite = False ), complex (np .inf , np .nan ))
429
+ np .testing .assert_equal (
430
+ sys_ss (0 , warn_infinite = False ), complex (np .inf , np .nan ))
431
+ np .testing .assert_equal (
432
+ sys_tf (0j , warn_infinite = False ), complex (np .inf , np .nan ))
433
+ np .testing .assert_equal (
434
+ sys_ss (0j , warn_infinite = False ), complex (np .inf , np .nan ))
435
+ np .testing .assert_equal (
436
+ sys_tf .dcgain (warn_infinite = False ), complex (np .inf , np .nan ))
437
+ np .testing .assert_equal (
438
+ sys_ss .dcgain (warn_infinite = False ), complex (np .inf , np .nan ))
439
+
440
+ # Set up transfer function with pole, zero at the origin
441
+ sys_tf = ctrl .tf ([1 , 0 ], [1 , 0 ])
442
+ assert 0 in sys_tf .pole ()
443
+ assert 0 in sys_tf .zero ()
444
+
445
+ # Pole and zero at the origin should give nan + nanj for the response
446
+ np .testing .assert_equal (
447
+ sys_tf (0 , warn_infinite = False ), complex (np .nan , np .nan ))
448
+ np .testing .assert_equal (
449
+ sys_tf (0j , warn_infinite = False ), complex (np .nan , np .nan ))
450
+ np .testing .assert_equal (
451
+ sys_tf .dcgain (warn_infinite = False ), complex (np .nan , np .nan ))
452
+
453
+ # Set up state space version
454
+ sys_ss = ctrl .tf2ss (ctrl .tf ([1 , 0 ], [1 , 1 ])) * \
455
+ ctrl .tf2ss (ctrl .tf ([1 ], [1 , 0 ]))
456
+
457
+ # Different systems give different representations => test accordingly
458
+ if 0 in sys_ss .pole () and 0 in sys_ss .zero ():
459
+ # Pole and zero at the origin => should get (nan + nanj)
460
+ np .testing .assert_equal (
461
+ sys_ss (0 , warn_infinite = False ), complex (np .nan , np .nan ))
462
+ np .testing .assert_equal (
463
+ sys_ss (0j , warn_infinite = False ), complex (np .nan , np .nan ))
464
+ np .testing .assert_equal (
465
+ sys_ss .dcgain (warn_infinite = False ), complex (np .nan , np .nan ))
466
+ elif 0 in sys_ss .pole ():
467
+ # Pole at the origin, but zero elsewhere => should get (inf + nanj)
468
+ np .testing .assert_equal (
469
+ sys_ss (0 , warn_infinite = False ), complex (np .inf , np .nan ))
470
+ np .testing .assert_equal (
471
+ sys_ss (0j , warn_infinite = False ), complex (np .inf , np .nan ))
472
+ np .testing .assert_equal (
473
+ sys_ss .dcgain (warn_infinite = False ), complex (np .inf , np .nan ))
474
+ else :
475
+ # Near pole/zero cancellation => nothing sensible to check
476
+ pass
477
+
478
+ # Pole with non-zero, complex numerator => inf + infj
479
+ s = ctrl .tf ('s' )
480
+ sys_tf = (s + 1 ) / (s ** 2 + 1 )
481
+ assert 1j in sys_tf .pole ()
482
+
483
+ # Set up state space system with pole on imaginary axis
484
+ sys_ss = ctrl .tf2ss (sys_tf )
485
+ assert 1j in sys_tf .pole ()
486
+
487
+ # Make sure we get correct response if evaluated at the pole
488
+ np .testing .assert_equal (
489
+ sys_tf (1j , warn_infinite = False ), complex (np .inf , np .inf ))
490
+
491
+ # For state space, numerical errors come into play
492
+ resp_ss = sys_ss (1j , warn_infinite = False )
493
+ if np .isfinite (resp_ss ):
494
+ assert abs (resp_ss ) > 1e15
495
+ else :
496
+ if resp_ss != complex (np .inf , np .inf ):
497
+ pytest .xfail ("statesp evaluation at poles not fully implemented" )
498
+ else :
499
+ np .testing .assert_equal (resp_ss , complex (np .inf , np .inf ))
500
+
501
+ # DC gain is finite
502
+ np .testing .assert_almost_equal (sys_tf .dcgain (), 1. )
503
+ np .testing .assert_almost_equal (sys_ss .dcgain (), 1. )
504
+
505
+ # Make sure that we get the *signed* DC gain
506
+ sys_tf = - 1 / (s + 1 )
507
+ np .testing .assert_almost_equal (sys_tf .dcgain (), - 1 )
508
+
509
+ sys_ss = ctrl .tf2ss (sys_tf )
510
+ np .testing .assert_almost_equal (sys_ss .dcgain (), - 1 )
0 commit comments