@@ -126,7 +126,7 @@ class for a set of subclasses that are used to implement specific
126
126
# Allow ndarray * InputOutputSystem to give IOSystem._rmul_() priority
127
127
__array_priority__ = 12 # override ndarray, matrix, SS types
128
128
129
- def __init__ (self , params = {} , ** kwargs ):
129
+ def __init__ (self , params = None , ** kwargs ):
130
130
"""Create an input/output system.
131
131
132
132
The InputOutputSystem constructor is used to create an input/output
@@ -148,7 +148,7 @@ def __init__(self, params={}, **kwargs):
148
148
states = states , name = name , dt = dt )
149
149
150
150
# default parameters
151
- self .params = params .copy ()
151
+ self .params = {} if params is None else params .copy ()
152
152
153
153
def __mul__ (sys2 , sys1 ):
154
154
"""Multiply two input/output systems (series interconnection)"""
@@ -357,7 +357,7 @@ def _update_params(self, params, warning=False):
357
357
if warning :
358
358
warn ("Parameters passed to InputOutputSystem ignored." )
359
359
360
- def _rhs (self , t , x , u , params = {} ):
360
+ def _rhs (self , t , x , u ):
361
361
"""Evaluate right hand side of a differential or difference equation.
362
362
363
363
Private function used to compute the right hand side of an
@@ -369,23 +369,24 @@ def _rhs(self, t, x, u, params={}):
369
369
NotImplemented ("Evaluation not implemented for system of type " ,
370
370
type (self ))
371
371
372
- def dynamics (self , t , x , u ):
372
+ def dynamics (self , t , x , u , params = None ):
373
373
"""Compute the dynamics of a differential or difference equation.
374
374
375
375
Given time `t`, input `u` and state `x`, returns the value of the
376
376
right hand side of the dynamical system. If the system is continuous,
377
377
returns the time derivative
378
378
379
- dx/dt = f(t, x, u)
379
+ dx/dt = f(t, x, u[, params] )
380
380
381
381
where `f` is the system's (possibly nonlinear) dynamics function.
382
382
If the system is discrete-time, returns the next value of `x`:
383
383
384
- x[t+dt] = f(t, x[t], u[t])
384
+ x[t+dt] = f(t, x[t], u[t][, params] )
385
385
386
- Where `t` is a scalar.
386
+ where `t` is a scalar.
387
387
388
- The inputs `x` and `u` must be of the correct length.
388
+ The inputs `x` and `u` must be of the correct length. The `params`
389
+ argument is an optional dictionary of parameter values.
389
390
390
391
Parameters
391
392
----------
@@ -395,14 +396,17 @@ def dynamics(self, t, x, u):
395
396
current state
396
397
u : array_like
397
398
input
399
+ params : dict (optional)
400
+ system parameter values
398
401
399
402
Returns
400
403
-------
401
404
dx/dt or x[t+dt] : ndarray
402
405
"""
406
+ self ._update_params (params )
403
407
return self ._rhs (t , x , u )
404
408
405
- def _out (self , t , x , u , params = {} ):
409
+ def _out (self , t , x , u ):
406
410
"""Evaluate the output of a system at a given state, input, and time
407
411
408
412
Private function used to compute the output of of an input/output
@@ -414,13 +418,13 @@ def _out(self, t, x, u, params={}):
414
418
# If no output function was defined in subclass, return state
415
419
return x
416
420
417
- def output (self , t , x , u ):
421
+ def output (self , t , x , u , params = None ):
418
422
"""Compute the output of the system
419
423
420
424
Given time `t`, input `u` and state `x`, returns the output of the
421
425
system:
422
426
423
- y = g(t, x, u)
427
+ y = g(t, x, u[, params] )
424
428
425
429
The inputs `x` and `u` must be of the correct length.
426
430
@@ -432,14 +436,17 @@ def output(self, t, x, u):
432
436
current state
433
437
u : array_like
434
438
input
439
+ params : dict (optional)
440
+ system parameter values
435
441
436
442
Returns
437
443
-------
438
444
y : ndarray
439
445
"""
446
+ self ._update_params (params )
440
447
return self ._out (t , x , u )
441
448
442
- def feedback (self , other = 1 , sign = - 1 , params = {} ):
449
+ def feedback (self , other = 1 , sign = - 1 , params = None ):
443
450
"""Feedback interconnection between two input/output systems
444
451
445
452
Parameters
@@ -507,7 +514,7 @@ def feedback(self, other=1, sign=-1, params={}):
507
514
# Return the newly created system
508
515
return newsys
509
516
510
- def linearize (self , x0 , u0 , t = 0 , params = {} , eps = 1e-6 ,
517
+ def linearize (self , x0 , u0 , t = 0 , params = None , eps = 1e-6 ,
511
518
name = None , copy = False , ** kwargs ):
512
519
"""Linearize an input/output system at a given state and input.
513
520
@@ -651,7 +658,7 @@ def __init__(self, linsys, **kwargs):
651
658
# Note: don't use super() to override StateSpace MRO
652
659
InputOutputSystem .__init__ (
653
660
self , inputs = inputs , outputs = outputs , states = states ,
654
- params = {} , dt = dt , name = name )
661
+ params = None , dt = dt , name = name )
655
662
656
663
# Initalize additional state space variables
657
664
StateSpace .__init__ (
@@ -668,7 +675,7 @@ def __init__(self, linsys, **kwargs):
668
675
#: number of states, use :attr:`nstates`.
669
676
states = property (StateSpace ._get_states , StateSpace ._set_states )
670
677
671
- def _update_params (self , params = {} , warning = True ):
678
+ def _update_params (self , params = None , warning = True ):
672
679
# Parameters not supported; issue a warning
673
680
if params and warning :
674
681
warn ("Parameters passed to LinearIOSystems are ignored." )
@@ -756,7 +763,7 @@ class NonlinearIOSystem(InputOutputSystem):
756
763
defaults.
757
764
758
765
"""
759
- def __init__ (self , updfcn , outfcn = None , params = {} , ** kwargs ):
766
+ def __init__ (self , updfcn , outfcn = None , params = None , ** kwargs ):
760
767
"""Create a nonlinear I/O system given update and output functions."""
761
768
# Process keyword arguments
762
769
name , inputs , outputs , states , dt = _process_namedio_keywords (
@@ -791,7 +798,7 @@ def __init__(self, updfcn, outfcn=None, params={}, **kwargs):
791
798
"(and nstates not known)." )
792
799
793
800
# Initialize current parameters to default parameters
794
- self ._current_params = params .copy ()
801
+ self ._current_params = {} if params is None else params .copy ()
795
802
796
803
def __str__ (self ):
797
804
return f"{ InputOutputSystem .__str__ (self )} \n \n " + \
@@ -838,7 +845,8 @@ def __call__(sys, u, params=None, squeeze=None):
838
845
def _update_params (self , params , warning = False ):
839
846
# Update the current parameter values
840
847
self ._current_params = self .params .copy ()
841
- self ._current_params .update (params )
848
+ if params :
849
+ self ._current_params .update (params )
842
850
843
851
def _rhs (self , t , x , u ):
844
852
xdot = self .updfcn (t , x , u , self ._current_params ) \
@@ -862,20 +870,22 @@ class InterconnectedSystem(InputOutputSystem):
862
870
See :func:`~control.interconnect` for a list of parameters.
863
871
864
872
"""
865
- def __init__ (self , syslist , connections = [] , inplist = [] , outlist = [] ,
866
- params = {} , warn_duplicate = None , ** kwargs ):
873
+ def __init__ (self , syslist , connections = None , inplist = None , outlist = None ,
874
+ params = None , warn_duplicate = None , ** kwargs ):
867
875
"""Create an I/O system from a list of systems + connection info."""
868
876
# Convert input and output names to lists if they aren't already
869
- if not isinstance (inplist , (list , tuple )):
877
+ if inplist is not None and not isinstance (inplist , (list , tuple )):
870
878
inplist = [inplist ]
871
- if not isinstance (outlist , (list , tuple )):
879
+ if outlist is not None and not isinstance (outlist , (list , tuple )):
872
880
outlist = [outlist ]
873
881
874
882
# Check if dt argument was given; if not, pull from systems
875
883
dt = kwargs .pop ('dt' , None )
876
884
877
885
# Process keyword arguments (except dt)
878
- defaults = {'inputs' : len (inplist ), 'outputs' : len (outlist )}
886
+ defaults = {
887
+ 'inputs' : len (inplist or []),
888
+ 'outputs' : len (outlist or [])}
879
889
name , inputs , outputs , states , _ = _process_namedio_keywords (
880
890
kwargs , defaults , end = True )
881
891
@@ -894,6 +904,12 @@ def __init__(self, syslist, connections=[], inplist=[], outlist=[],
894
904
895
905
# Go through the system list and keep track of counts, offsets
896
906
for sysidx , sys in enumerate (syslist ):
907
+ # If we were passed a SS or TF system, convert to LinearIOSystem
908
+ if isinstance (sys , (StateSpace , TransferFunction )) and \
909
+ not isinstance (sys , LinearIOSystem ):
910
+ sys = LinearIOSystem (sys )
911
+ syslist [sysidx ] = sys
912
+
897
913
# Make sure time bases are consistent
898
914
dt = common_timebase (dt , sys .dt )
899
915
@@ -969,7 +985,7 @@ def __init__(self, syslist, connections=[], inplist=[], outlist=[],
969
985
970
986
# Convert the list of interconnections to a connection map (matrix)
971
987
self .connect_map = np .zeros ((ninputs , noutputs ))
972
- for connection in connections :
988
+ for connection in connections or [] :
973
989
input_index = self ._parse_input_spec (connection [0 ])
974
990
for output_spec in connection [1 :]:
975
991
output_index , gain = self ._parse_output_spec (output_spec )
@@ -980,7 +996,7 @@ def __init__(self, syslist, connections=[], inplist=[], outlist=[],
980
996
981
997
# Convert the input list to a matrix: maps system to subsystems
982
998
self .input_map = np .zeros ((ninputs , self .ninputs ))
983
- for index , inpspec in enumerate (inplist ):
999
+ for index , inpspec in enumerate (inplist or [] ):
984
1000
if isinstance (inpspec , (int , str , tuple )):
985
1001
inpspec = [inpspec ]
986
1002
if not isinstance (inpspec , list ):
@@ -995,7 +1011,7 @@ def __init__(self, syslist, connections=[], inplist=[], outlist=[],
995
1011
996
1012
# Convert the output list to a matrix: maps subsystems to system
997
1013
self .output_map = np .zeros ((self .noutputs , noutputs + ninputs ))
998
- for index , outspec in enumerate (outlist ):
1014
+ for index , outspec in enumerate (outlist or [] ):
999
1015
if isinstance (outspec , (int , str , tuple )):
1000
1016
outspec = [outspec ]
1001
1017
if not isinstance (outspec , list ):
@@ -1009,13 +1025,14 @@ def __init__(self, syslist, connections=[], inplist=[], outlist=[],
1009
1025
self .output_map [index , ylist_index ] += gain
1010
1026
1011
1027
# Save the parameters for the system
1012
- self .params = params .copy ()
1028
+ self .params = {} if params is None else params .copy ()
1013
1029
1014
1030
def _update_params (self , params , warning = False ):
1015
1031
for sys in self .syslist :
1016
1032
local = sys .params .copy () # start with system parameters
1017
1033
local .update (self .params ) # update with global params
1018
- local .update (params ) # update with locally passed parameters
1034
+ if params :
1035
+ local .update (params ) # update with locally passed parameters
1019
1036
sys ._update_params (local , warning = warning )
1020
1037
1021
1038
def _rhs (self , t , x , u ):
@@ -1565,7 +1582,7 @@ def __init__(self, io_sys, ss_sys=None):
1565
1582
1566
1583
1567
1584
def input_output_response (
1568
- sys , T , U = 0. , X0 = 0 , params = {} ,
1585
+ sys , T , U = 0. , X0 = 0 , params = None ,
1569
1586
transpose = False , return_x = False , squeeze = None ,
1570
1587
solve_ivp_kwargs = {}, t_eval = 'T' , ** kwargs ):
1571
1588
"""Compute the output response of a system to a given input.
@@ -1781,7 +1798,7 @@ def input_output_response(
1781
1798
1782
1799
# Update the parameter values
1783
1800
sys ._update_params (params )
1784
-
1801
+
1785
1802
#
1786
1803
# Define a function to evaluate the input at an arbitrary time
1787
1804
#
@@ -1900,7 +1917,7 @@ def ivp_rhs(t, x):
1900
1917
transpose = transpose , return_x = return_x , squeeze = squeeze )
1901
1918
1902
1919
1903
- def find_eqpt (sys , x0 , u0 = [] , y0 = None , t = 0 , params = {} ,
1920
+ def find_eqpt (sys , x0 , u0 = None , y0 = None , t = 0 , params = None ,
1904
1921
iu = None , iy = None , ix = None , idx = None , dx0 = None ,
1905
1922
return_y = False , return_result = False ):
1906
1923
"""Find the equilibrium point for an input/output system.
@@ -2151,7 +2168,7 @@ def rootfun(z):
2151
2168
2152
2169
2153
2170
# Linearize an input/output system
2154
- def linearize (sys , xeq , ueq = [] , t = 0 , params = {} , ** kw ):
2171
+ def linearize (sys , xeq , ueq = None , t = 0 , params = None , ** kw ):
2155
2172
"""Linearize an input/output system at a given state and input.
2156
2173
2157
2174
This function computes the linearization of an input/output system at a
@@ -2242,7 +2259,7 @@ def ss(*args, **kwargs):
2242
2259
Convert a linear system into space system form. Always creates a
2243
2260
new system, even if sys is already a state space system.
2244
2261
2245
- ``ss(updfcn, outfucn )``
2262
+ ``ss(updfcn, outfcn )``
2246
2263
Create a nonlinear input/output system with update function ``updfcn``
2247
2264
and output function ``outfcn``. See :class:`NonlinearIOSystem` for
2248
2265
more information.
@@ -2523,9 +2540,9 @@ def tf2io(*args, **kwargs):
2523
2540
2524
2541
2525
2542
# Function to create an interconnected system
2526
- def interconnect (syslist , connections = None , inplist = [] , outlist = [], params = {} ,
2527
- check_unused = True , ignore_inputs = None , ignore_outputs = None ,
2528
- warn_duplicate = None , ** kwargs ):
2543
+ def interconnect (syslist , connections = None , inplist = None , outlist = None ,
2544
+ params = None , check_unused = True , ignore_inputs = None ,
2545
+ ignore_outputs = None , warn_duplicate = None , ** kwargs ):
2529
2546
"""Interconnect a set of input/output systems.
2530
2547
2531
2548
This function creates a new system that is an interconnection of a set of
@@ -2767,10 +2784,10 @@ def interconnect(syslist, connections=None, inplist=[], outlist=[], params={},
2767
2784
connections = []
2768
2785
2769
2786
# If inplist/outlist is not present, try using inputs/outputs instead
2770
- if not inplist and inputs is not None :
2771
- inplist = list (inputs )
2772
- if not outlist and outputs is not None :
2773
- outlist = list (outputs )
2787
+ if inplist is None :
2788
+ inplist = list (inputs or [] )
2789
+ if outlist is None :
2790
+ outlist = list (outputs or [] )
2774
2791
2775
2792
# Process input list
2776
2793
if not isinstance (inplist , (list , tuple )):
0 commit comments