1616from control import ControlMIMONotImplemented , FrequencyResponseData , \
1717 StateSpace , TransferFunction , margin , phase_crossover_frequencies , \
1818 stability_margins , disk_margins , tf , ss
19+ from control .exception import slycot_check
1920
2021s = TransferFunction .s
2122
@@ -382,55 +383,45 @@ def test_siso_disk_margin():
382383 L = tf (25 , [1 , 10 , 10 , 10 ])
383384
384385 # 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
389390
390391 # For SISO systems, the S-based (S) disk margin should match the third output
391392 # of existing library "stability_margins", i.e., minimum distance from the
392393 # Nyquist plot to -1.
393394 _ , _ , 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 )
396397
397398def test_mimo_disk_margin ():
398399 # Frequencies of interest
399400 omega = np .logspace (- 1 , 3 , 1001 )
400401
401402 # 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
406407
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 ():
423409 # 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
428414
429415 # 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 )
434425
435426def test_siso_disk_margin_return_all ():
436427 # Frequencies of interest
@@ -440,68 +431,49 @@ def test_siso_disk_margin_return_all():
440431 L = tf (25 , [1 , 10 , 10 , 10 ])
441432
442433 # 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 )
444435 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
447438 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
449440 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
451442
452443def test_mimo_disk_margin_return_all ():
453444 # Frequencies of interest
454445 omega = np .logspace (- 1 , 3 , 1001 )
455446
456447 # 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
462453
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 ():
488455 # 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 )
490457 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
493460 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
495462 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
497464
498465 # 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 )
500467 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)
502469 assert_allclose ([min (DMi )], [0.3754 ],\
503- atol = 0.1 ) # disk margin of 0.3754
470+ atol = 0.1 ) # disk margin of 0.3754
504471 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
506473 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