16
16
from control import ControlMIMONotImplemented , FrequencyResponseData , \
17
17
StateSpace , TransferFunction , margin , phase_crossover_frequencies , \
18
18
stability_margins , disk_margins , tf , ss
19
+ from control .exception import slycot_check
19
20
20
21
s = TransferFunction .s
21
22
@@ -382,55 +383,45 @@ def test_siso_disk_margin():
382
383
L = tf (25 , [1 , 10 , 10 , 10 ])
383
384
384
385
# Balanced (S - T) disk-based stability margins
385
- DM , DGM , DPM = disk_margins (L , omega , skew = 0.0 )
386
- assert_allclose ([DM ], [0.46 ], atol = 0.1 ) # disk margin of 0.46
387
- assert_allclose ([DGM ], [4.05 ], atol = 0.1 ) # disk-based gain margin of 4.05 dB
388
- assert_allclose ([DPM ], [25.8 ], atol = 0.1 ) # disk-based phase margin of 25.8 deg
386
+ DM , DGM , DPM = disk_margins (L , omega , skew = 0.0 )
387
+ assert_allclose ([DM ], [0.46 ], atol = 0.1 ) # disk margin of 0.46
388
+ assert_allclose ([DGM ], [4.05 ], atol = 0.1 ) # disk-based gain margin of 4.05 dB
389
+ assert_allclose ([DPM ], [25.8 ], atol = 0.1 ) # disk-based phase margin of 25.8 deg
389
390
390
391
# For SISO systems, the S-based (S) disk margin should match the third output
391
392
# of existing library "stability_margins", i.e., minimum distance from the
392
393
# Nyquist plot to -1.
393
394
_ , _ , SM = stability_margins (L )[:3 ]
394
- DM = disk_margins (L , omega , skew = 1.0 )[0 ]
395
- assert_allclose ([DM ], [SM ], atol = 0.01 )
395
+ DM = disk_margins (L , omega , skew = 1.0 )[0 ]
396
+ assert_allclose ([DM ], [SM ], atol = 0.01 )
396
397
397
398
def test_mimo_disk_margin ():
398
399
# Frequencies of interest
399
400
omega = np .logspace (- 1 , 3 , 1001 )
400
401
401
402
# Loop transfer gain
402
- P = ss ([[0 , 10 ],[- 10 , 0 ]], np .eye (2 ), [[1 , 10 ], [- 10 , 1 ]], [[ 0 , 0 ],[ 0 , 0 ]] ) # plant
403
- K = ss ([],[],[], [[1 , - 2 ], [0 , 1 ]]) # controller
404
- Lo = P * K # loop transfer function, broken at plant output
405
- Li = K * P # loop transfer function, broken at plant input
403
+ P = ss ([[0 , 10 ], [- 10 , 0 ]], np .eye (2 ), [[1 , 10 ], [- 10 , 1 ]], 0 ) # plant
404
+ K = ss ([], [], [], [[1 , - 2 ], [0 , 1 ]]) # controller
405
+ Lo = P * K # loop transfer function, broken at plant output
406
+ Li = K * P # loop transfer function, broken at plant input
406
407
407
- if importlib .util .find_spec ('slycot' ) == None :
408
- with pytest .raises (ControlMIMONotImplemented ,\
409
- match = "Need slycot to compute MIMO disk_margins" ):
410
-
411
- # Balanced (S - T) disk-based stability margins at plant output
412
- DMo , DGMo , DPMo = disk_margins (Lo , omega , skew = 0.0 )
413
- assert_allclose ([DMo ], [0.3754 ], atol = 0.1 ) # disk margin of 0.3754
414
- assert_allclose ([DGMo ], [3.3 ], atol = 0.1 ) # disk-based gain margin of 3.3 dB
415
- assert_allclose ([DPMo ], [21.26 ], atol = 0.1 ) # disk-based phase margin of 21.26 deg
416
-
417
- # Balanced (S - T) disk-based stability margins at plant input
418
- DMi , DGMi , DPMi = disk_margins (Li , omega , skew = 0.0 )
419
- assert_allclose ([DMi ], [0.3754 ], atol = 0.1 ) # disk margin of 0.3754
420
- assert_allclose ([DGMi ], [3.3 ], atol = 0.1 ) # disk-based gain margin of 3.3 dB
421
- assert_allclose ([DPMi ], [21.26 ], atol = 0.1 ) # disk-based phase margin of 21.26 deg
422
- else :
408
+ if slycot_check ():
423
409
# Balanced (S - T) disk-based stability margins at plant output
424
- DMo , DGMo , DPMo = disk_margins (Lo , omega , skew = 0.0 )
425
- assert_allclose ([DMo ], [0.3754 ], atol = 0.1 ) # disk margin of 0.3754
426
- assert_allclose ([DGMo ], [3.3 ], atol = 0.1 ) # disk-based gain margin of 3.3 dB
427
- assert_allclose ([DPMo ], [21.26 ], atol = 0.1 ) # disk-based phase margin of 21.26 deg
410
+ DMo , DGMo , DPMo = disk_margins (Lo , omega , skew = 0.0 )
411
+ assert_allclose ([DMo ], [0.3754 ], atol = 0.1 ) # disk margin of 0.3754
412
+ assert_allclose ([DGMo ], [3.3 ], atol = 0.1 ) # disk-based gain margin of 3.3 dB
413
+ assert_allclose ([DPMo ], [21.26 ], atol = 0.1 ) # disk-based phase margin of 21.26 deg
428
414
429
415
# Balanced (S - T) disk-based stability margins at plant input
430
- DMi , DGMi , DPMi = disk_margins (Li , omega , skew = 0.0 )
431
- assert_allclose ([DMi ], [0.3754 ], atol = 0.1 ) # disk margin of 0.3754
432
- assert_allclose ([DGMi ], [3.3 ], atol = 0.1 ) # disk-based gain margin of 3.3 dB
433
- assert_allclose ([DPMi ], [21.26 ], atol = 0.1 ) # disk-based phase margin of 21.26 deg
416
+ DMi , DGMi , DPMi = disk_margins (Li , omega , skew = 0.0 )
417
+ assert_allclose ([DMi ], [0.3754 ], atol = 0.1 ) # disk margin of 0.3754
418
+ assert_allclose ([DGMi ], [3.3 ], atol = 0.1 ) # disk-based gain margin of 3.3 dB
419
+ assert_allclose ([DPMi ], [21.26 ], atol = 0.1 ) # disk-based phase margin of 21.26 deg
420
+ else :
421
+ # Slycot not installed. Should throw exception.
422
+ with pytest .raises (ControlMIMONotImplemented ,\
423
+ match = "Need slycot to compute MIMO disk_margins" ):
424
+ DMo , DGMo , DPMo = disk_margins (Lo , omega , skew = 0.0 )
434
425
435
426
def test_siso_disk_margin_return_all ():
436
427
# Frequencies of interest
@@ -440,68 +431,49 @@ def test_siso_disk_margin_return_all():
440
431
L = tf (25 , [1 , 10 , 10 , 10 ])
441
432
442
433
# Balanced (S - T) disk-based stability margins
443
- DM , DGM , DPM = disk_margins (L , omega , skew = 0.0 , returnall = True )
434
+ DM , DGM , DPM = disk_margins (L , omega , skew = 0.0 , returnall = True )
444
435
assert_allclose ([omega [np .argmin (DM )]], [1.94 ],\
445
- atol = 0.01 ) # sensitivity peak at 1.94 rad/s
446
- assert_allclose ([min (DM )], [0.46 ], atol = 0.1 ) # disk margin of 0.46
436
+ atol = 0.01 ) # sensitivity peak at 1.94 rad/s
437
+ assert_allclose ([min (DM )], [0.46 ], atol = 0.1 ) # disk margin of 0.46
447
438
assert_allclose ([DGM [np .argmin (DM )]], [4.05 ],\
448
- atol = 0.1 ) # disk-based gain margin of 4.05 dB
439
+ atol = 0.1 ) # disk-based gain margin of 4.05 dB
449
440
assert_allclose ([DPM [np .argmin (DM )]], [25.8 ],\
450
- atol = 0.1 ) # disk-based phase margin of 25.8 deg
441
+ atol = 0.1 ) # disk-based phase margin of 25.8 deg
451
442
452
443
def test_mimo_disk_margin_return_all ():
453
444
# Frequencies of interest
454
445
omega = np .logspace (- 1 , 3 , 1001 )
455
446
456
447
# Loop transfer gain
457
- P = ss ([[0 , 10 ],[- 10 , 0 ]], np .eye (2 ),\
458
- [[1 , 10 ], [- 10 , 1 ]], [[ 0 , 0 ],[ 0 , 0 ]] ) # plant
459
- K = ss ([],[],[], [[1 , - 2 ], [0 , 1 ]]) # controller
460
- Lo = P * K # loop transfer function, broken at plant output
461
- Li = K * P # loop transfer function, broken at plant input
448
+ P = ss ([[0 , 10 ], [- 10 , 0 ]], np .eye (2 ),\
449
+ [[1 , 10 ], [- 10 , 1 ]], 0 ) # plant
450
+ K = ss ([], [], [], [[1 , - 2 ], [0 , 1 ]]) # controller
451
+ Lo = P * K # loop transfer function, broken at plant output
452
+ Li = K * P # loop transfer function, broken at plant input
462
453
463
- if importlib .util .find_spec ('slycot' ) == None :
464
- with pytest .raises (ControlMIMONotImplemented ,\
465
- match = "Need slycot to compute MIMO disk_margins" ):
466
-
467
- # Balanced (S - T) disk-based stability margins at plant output
468
- DMo , DGMo , DPMo = disk_margins (Lo , omega , skew = 0.0 , returnall = True )
469
- assert_allclose ([omega [np .argmin (DMo )]], [omega [0 ]],\
470
- atol = 0.01 ) # sensitivity peak at 0 rad/s (or smallest provided)
471
- assert_allclose ([min (DMo )], [0.3754 ], atol = 0.1 ) # disk margin of 0.3754
472
- assert_allclose ([DGMo [np .argmin (DMo )]], [3.3 ],\
473
- atol = 0.1 ) # disk-based gain margin of 3.3 dB
474
- assert_allclose ([DPMo [np .argmin (DMo )]], [21.26 ],\
475
- atol = 0.1 ) # disk-based phase margin of 21.26 deg
476
-
477
- # Balanced (S - T) disk-based stability margins at plant input
478
- DMi , DGMi , DPMi = disk_margins (Li , omega , skew = 0.0 , returnall = True )
479
- assert_allclose ([omega [np .argmin (DMi )]], [omega [0 ]],\
480
- atol = 0.01 ) # sensitivity peak at 0 rad/s (or smallest provided)
481
- assert_allclose ([min (DMi )], [0.3754 ],\
482
- atol = 0.1 ) # disk margin of 0.3754
483
- assert_allclose ([DGMi [np .argmin (DMi )]], [3.3 ],\
484
- atol = 0.1 ) # disk-based gain margin of 3.3 dB
485
- assert_allclose ([DPMi [np .argmin (DMi )]], [21.26 ],\
486
- atol = 0.1 ) # disk-based phase margin of 21.26 deg
487
- else :
454
+ if slycot_check ():
488
455
# Balanced (S - T) disk-based stability margins at plant output
489
- DMo , DGMo , DPMo = disk_margins (Lo , omega , skew = 0.0 , returnall = True )
456
+ DMo , DGMo , DPMo = disk_margins (Lo , omega , skew = 0.0 , returnall = True )
490
457
assert_allclose ([omega [np .argmin (DMo )]], [omega [0 ]],\
491
- atol = 0.01 ) # sensitivity peak at 0 rad/s (or smallest provided)
492
- assert_allclose ([min (DMo )], [0.3754 ], atol = 0.1 ) # disk margin of 0.3754
458
+ atol = 0.01 ) # sensitivity peak at 0 rad/s (or smallest provided)
459
+ assert_allclose ([min (DMo )], [0.3754 ], atol = 0.1 ) # disk margin of 0.3754
493
460
assert_allclose ([DGMo [np .argmin (DMo )]], [3.3 ],\
494
- atol = 0.1 ) # disk-based gain margin of 3.3 dB
461
+ atol = 0.1 ) # disk-based gain margin of 3.3 dB
495
462
assert_allclose ([DPMo [np .argmin (DMo )]], [21.26 ],\
496
- atol = 0.1 ) # disk-based phase margin of 21.26 deg
463
+ atol = 0.1 ) # disk-based phase margin of 21.26 deg
497
464
498
465
# Balanced (S - T) disk-based stability margins at plant input
499
- DMi , DGMi , DPMi = disk_margins (Li , omega , skew = 0.0 , returnall = True )
466
+ DMi , DGMi , DPMi = disk_margins (Li , omega , skew = 0.0 , returnall = True )
500
467
assert_allclose ([omega [np .argmin (DMi )]], [omega [0 ]],\
501
- atol = 0.01 ) # sensitivity peak at 0 rad/s (or smallest provided)
468
+ atol = 0.01 ) # sensitivity peak at 0 rad/s (or smallest provided)
502
469
assert_allclose ([min (DMi )], [0.3754 ],\
503
- atol = 0.1 ) # disk margin of 0.3754
470
+ atol = 0.1 ) # disk margin of 0.3754
504
471
assert_allclose ([DGMi [np .argmin (DMi )]], [3.3 ],\
505
- atol = 0.1 ) # disk-based gain margin of 3.3 dB
472
+ atol = 0.1 ) # disk-based gain margin of 3.3 dB
506
473
assert_allclose ([DPMi [np .argmin (DMi )]], [21.26 ],\
507
- atol = 0.1 ) # disk-based phase margin of 21.26 deg
474
+ atol = 0.1 ) # disk-based phase margin of 21.26 deg
475
+ else :
476
+ # Slycot not installed. Should throw exception.
477
+ with pytest .raises (ControlMIMONotImplemented ,\
478
+ match = "Need slycot to compute MIMO disk_margins" ):
479
+ DMo , DGMo , DPMo = disk_margins (Lo , omega , skew = 0.0 , returnall = True )
0 commit comments