4848# Define module default parameter values
4949_iosys_defaults = {}
5050
51+
5152class InputOutputSystem (object ):
5253 """A class for representing input/output systems.
5354
@@ -75,7 +76,7 @@ class for a set of subclasses that are used to implement specific
7576 System timebase. 0 (default) indicates continuous
7677 time, True indicates discrete time with unspecified sampling
7778 time, positive number is discrete time with specified
78- sampling time, None indicates unspecified timebase (either
79+ sampling time, None indicates unspecified timebase (either
7980 continuous or discrete time).
8081 params : dict, optional
8182 Parameter values for the systems. Passed to the evaluation functions
@@ -95,7 +96,7 @@ class for a set of subclasses that are used to implement specific
9596 System timebase. 0 (default) indicates continuous
9697 time, True indicates discrete time with unspecified sampling
9798 time, positive number is discrete time with specified
98- sampling time, None indicates unspecified timebase (either
99+ sampling time, None indicates unspecified timebase (either
99100 continuous or discrete time).
100101 params : dict, optional
101102 Parameter values for the systems. Passed to the evaluation functions
@@ -118,6 +119,7 @@ class for a set of subclasses that are used to implement specific
118119 """
119120
120121 idCounter = 0
122+
121123 def name_or_default (self , name = None ):
122124 if name is None :
123125 name = "sys[{}]" .format (InputOutputSystem .idCounter )
@@ -153,15 +155,15 @@ def __init__(self, inputs=None, outputs=None, states=None, params={},
153155 System timebase. 0 (default) indicates continuous
154156 time, True indicates discrete time with unspecified sampling
155157 time, positive number is discrete time with specified
156- sampling time, None indicates unspecified timebase (either
158+ sampling time, None indicates unspecified timebase (either
157159 continuous or discrete time).
158160 params : dict, optional
159161 Parameter values for the systems. Passed to the evaluation
160162 functions for the system as default values, overriding internal
161163 defaults.
162164 name : string, optional
163- System name (used for specifying signals). If unspecified, a generic
164- name <sys[id]> is generated with a unique integer id.
165+ System name (used for specifying signals). If unspecified, a
166+ generic name <sys[id]> is generated with a unique integer id.
165167
166168 Returns
167169 -------
@@ -190,11 +192,14 @@ def __str__(self):
190192 """String representation of an input/output system"""
191193 str = "System: " + (self .name if self .name else "(None)" ) + "\n "
192194 str += "Inputs (%s): " % self .ninputs
193- for key in self .input_index : str += key + ", "
195+ for key in self .input_index :
196+ str += key + ", "
194197 str += "\n Outputs (%s): " % self .noutputs
195- for key in self .output_index : str += key + ", "
198+ for key in self .output_index :
199+ str += key + ", "
196200 str += "\n States (%s): " % self .nstates
197- for key in self .state_index : str += key + ", "
201+ for key in self .state_index :
202+ str += key + ", "
198203 return str
199204
200205 def __mul__ (sys2 , sys1 ):
@@ -224,10 +229,11 @@ def __mul__(sys2, sys1):
224229 # Make sure timebase are compatible
225230 dt = common_timebase (sys1 .dt , sys2 .dt )
226231
227- inplist = [(0 ,i ) for i in range (sys1 .ninputs )]
228- outlist = [(1 ,i ) for i in range (sys2 .noutputs )]
232+ inplist = [(0 , i ) for i in range (sys1 .ninputs )]
233+ outlist = [(1 , i ) for i in range (sys2 .noutputs )]
229234 # Return the series interconnection between the systems
230- newsys = InterconnectedSystem ((sys1 , sys2 ), inplist = inplist , outlist = outlist )
235+ newsys = InterconnectedSystem (
236+ (sys1 , sys2 ), inplist = inplist , outlist = outlist )
231237
232238 # Set up the connection map manually
233239 newsys .set_connect_map (np .block (
@@ -281,10 +287,11 @@ def __add__(sys1, sys2):
281287 ninputs = sys1 .ninputs
282288 noutputs = sys1 .noutputs
283289
284- inplist = [[(0 ,i ),(1 ,i )] for i in range (ninputs )]
285- outlist = [[(0 ,i ),(1 ,i )] for i in range (noutputs )]
290+ inplist = [[(0 , i ), (1 , i )] for i in range (ninputs )]
291+ outlist = [[(0 , i ), (1 , i )] for i in range (noutputs )]
286292 # Create a new system to handle the composition
287- newsys = InterconnectedSystem ((sys1 , sys2 ), inplist = inplist , outlist = outlist )
293+ newsys = InterconnectedSystem (
294+ (sys1 , sys2 ), inplist = inplist , outlist = outlist )
288295
289296 # Return the newly created system
290297 return newsys
@@ -303,10 +310,11 @@ def __neg__(sys):
303310 if sys .ninputs is None or sys .noutputs is None :
304311 raise ValueError ("Can't determine number of inputs or outputs" )
305312
306- inplist = [(0 ,i ) for i in range (sys .ninputs )]
307- outlist = [(0 ,i , - 1 ) for i in range (sys .noutputs )]
313+ inplist = [(0 , i ) for i in range (sys .ninputs )]
314+ outlist = [(0 , i , - 1 ) for i in range (sys .noutputs )]
308315 # Create a new system to hold the negation
309- newsys = InterconnectedSystem ((sys ,), dt = sys .dt , inplist = inplist , outlist = outlist )
316+ newsys = InterconnectedSystem (
317+ (sys ,), dt = sys .dt , inplist = inplist , outlist = outlist )
310318
311319 # Return the newly created system
312320 return newsys
@@ -476,12 +484,13 @@ def feedback(self, other=1, sign=-1, params={}):
476484 # Make sure timebases are compatible
477485 dt = common_timebase (self .dt , other .dt )
478486
479- inplist = [(0 ,i ) for i in range (self .ninputs )]
480- outlist = [(0 ,i ) for i in range (self .noutputs )]
487+ inplist = [(0 , i ) for i in range (self .ninputs )]
488+ outlist = [(0 , i ) for i in range (self .noutputs )]
481489
482490 # Return the series interconnection between the systems
483- newsys = InterconnectedSystem ((self , other ), inplist = inplist , outlist = outlist ,
484- params = params , dt = dt )
491+ newsys = InterconnectedSystem (
492+ (self , other ), inplist = inplist , outlist = outlist ,
493+ params = params , dt = dt )
485494
486495 # Set up the connecton map manually
487496 newsys .set_connect_map (np .block (
@@ -514,8 +523,10 @@ def linearize(self, x0, u0, t=0, params={}, eps=1e-6,
514523 ninputs = _find_size (self .ninputs , u0 )
515524
516525 # Convert x0, u0 to arrays, if needed
517- if np .isscalar (x0 ): x0 = np .ones ((nstates ,)) * x0
518- if np .isscalar (u0 ): u0 = np .ones ((ninputs ,)) * u0
526+ if np .isscalar (x0 ):
527+ x0 = np .ones ((nstates ,)) * x0
528+ if np .isscalar (u0 ):
529+ u0 = np .ones ((ninputs ,)) * u0
519530
520531 # Compute number of outputs by evaluating the output function
521532 noutputs = _find_size (self .noutputs , self ._out (t , x0 , u0 ))
@@ -566,7 +577,8 @@ def linearize(self, x0, u0, t=0, params={}, eps=1e-6,
566577 def copy (self , newname = None ):
567578 """Make a copy of an input/output system."""
568579 newsys = copy .copy (self )
569- newsys .name = self .name_or_default ("copy of " + self .name if not newname else newname )
580+ newsys .name = self .name_or_default (
581+ "copy of " + self .name if not newname else newname )
570582 return newsys
571583
572584
@@ -605,15 +617,15 @@ def __init__(self, linsys, inputs=None, outputs=None, states=None,
605617 System timebase. 0 (default) indicates continuous
606618 time, True indicates discrete time with unspecified sampling
607619 time, positive number is discrete time with specified
608- sampling time, None indicates unspecified timebase (either
620+ sampling time, None indicates unspecified timebase (either
609621 continuous or discrete time).
610622 params : dict, optional
611623 Parameter values for the systems. Passed to the evaluation
612624 functions for the system as default values, overriding internal
613625 defaults.
614626 name : string, optional
615- System name (used for specifying signals). If unspecified, a generic
616- name <sys[id]> is generated with a unique integer id.
627+ System name (used for specifying signals). If unspecified, a
628+ generic name <sys[id]> is generated with a unique integer id.
617629
618630 Returns
619631 -------
@@ -729,11 +741,11 @@ def __init__(self, updfcn, outfcn=None, inputs=None, outputs=None,
729741 * dt = 0: continuous time system (default)
730742 * dt > 0: discrete time system with sampling period 'dt'
731743 * dt = True: discrete time with unspecified sampling period
732- * dt = None: no timebase specified
744+ * dt = None: no timebase specified
733745
734746 name : string, optional
735- System name (used for specifying signals). If unspecified, a generic
736- name <sys[id]> is generated with a unique integer id.
747+ System name (used for specifying signals). If unspecified, a
748+ generic name <sys[id]> is generated with a unique integer id.
737749
738750 Returns
739751 -------
@@ -899,23 +911,28 @@ def __init__(self, syslist, connections=[], inplist=[], outlist=[],
899911 * dt = 0: continuous time system (default)
900912 * dt > 0: discrete time system with sampling period 'dt'
901913 * dt = True: discrete time with unspecified sampling period
902- * dt = None: no timebase specified
914+ * dt = None: no timebase specified
903915
904916 name : string, optional
905- System name (used for specifying signals). If unspecified, a generic
906- name <sys[id]> is generated with a unique integer id.
917+ System name (used for specifying signals). If unspecified, a
918+ generic name <sys[id]> is generated with a unique integer id.
907919
908920 """
909921 # Convert input and output names to lists if they aren't already
910- if not isinstance (inplist , (list , tuple )): inplist = [inplist ]
911- if not isinstance (outlist , (list , tuple )): outlist = [outlist ]
922+ if not isinstance (inplist , (list , tuple )):
923+ inplist = [inplist ]
924+ if not isinstance (outlist , (list , tuple )):
925+ outlist = [outlist ]
912926
913927 # Check to make sure all systems are consistent
914928 self .syslist = syslist
915929 self .syslist_index = {}
916- nstates = 0 ; self .state_offset = []
917- ninputs = 0 ; self .input_offset = []
918- noutputs = 0 ; self .output_offset = []
930+ nstates = 0
931+ self .state_offset = []
932+ ninputs = 0
933+ self .input_offset = []
934+ noutputs = 0
935+ self .output_offset = []
919936 sysobj_name_dct = {}
920937 sysname_count_dct = {}
921938 for sysidx , sys in enumerate (syslist ):
@@ -943,14 +960,16 @@ def __init__(self, syslist, connections=[], inplist=[], outlist=[],
943960 # Duplicates are renamed sysname_1, sysname_2, etc.
944961 if sys in sysobj_name_dct :
945962 sys = sys .copy ()
946- warn ("Duplicate object found in system list: %s. Making a copy" % str (sys ))
963+ warn ("Duplicate object found in system list: %s. "
964+ "Making a copy" % str (sys ))
947965 if sys .name is not None and sys .name in sysname_count_dct :
948966 count = sysname_count_dct [sys .name ]
949967 sysname_count_dct [sys .name ] += 1
950968 sysname = sys .name + "_" + str (count )
951969 sysobj_name_dct [sys ] = sysname
952970 self .syslist_index [sysname ] = sysidx
953- warn ("Duplicate name found in system list. Renamed to {}" .format (sysname ))
971+ warn ("Duplicate name found in system list. "
972+ "Renamed to {}" .format (sysname ))
954973 else :
955974 sysname_count_dct [sys .name ] = 1
956975 sysobj_name_dct [sys ] = sys .name
@@ -959,7 +978,8 @@ def __init__(self, syslist, connections=[], inplist=[], outlist=[],
959978 if states is None :
960979 states = []
961980 for sys , sysname in sysobj_name_dct .items ():
962- states += [sysname + '.' + statename for statename in sys .state_index .keys ()]
981+ states += [sysname + '.' +
982+ statename for statename in sys .state_index .keys ()]
963983
964984 # Create the I/O system
965985 super (InterconnectedSystem , self ).__init__ (
@@ -989,14 +1009,16 @@ def __init__(self, syslist, connections=[], inplist=[], outlist=[],
9891009 # Convert the input list to a matrix: maps system to subsystems
9901010 self .input_map = np .zeros ((ninputs , self .ninputs ))
9911011 for index , inpspec in enumerate (inplist ):
992- if isinstance (inpspec , (int , str , tuple )): inpspec = [inpspec ]
1012+ if isinstance (inpspec , (int , str , tuple )):
1013+ inpspec = [inpspec ]
9931014 for spec in inpspec :
9941015 self .input_map [self ._parse_input_spec (spec ), index ] = 1
9951016
9961017 # Convert the output list to a matrix: maps subsystems to system
9971018 self .output_map = np .zeros ((self .noutputs , noutputs + ninputs ))
9981019 for index , outspec in enumerate (outlist ):
999- if isinstance (outspec , (int , str , tuple )): outspec = [outspec ]
1020+ if isinstance (outspec , (int , str , tuple )):
1021+ outspec = [outspec ]
10001022 for spec in outspec :
10011023 ylist_index , gain = self ._parse_output_spec (spec )
10021024 self .output_map [index , ylist_index ] = gain
@@ -1041,7 +1063,7 @@ def _rhs(self, t, x, u):
10411063
10421064 # Go through each system and update the right hand side for that system
10431065 xdot = np .zeros ((self .nstates ,)) # Array to hold results
1044- state_index = 0 ; input_index = 0 # Start at the beginning
1066+ state_index , input_index = 0 , 0 # Start at the beginning
10451067 for sys in self .syslist :
10461068 # Update the right hand side for this subsystem
10471069 if sys .nstates != 0 :
@@ -1084,7 +1106,7 @@ def _compute_static_io(self, t, x, u):
10841106 # TODO (later): see if there is a more efficient way to compute
10851107 cycle_count = len (self .syslist ) + 1
10861108 while cycle_count > 0 :
1087- state_index = 0 ; input_index = 0 ; output_index = 0
1109+ state_index , input_index , output_index = 0 , 0 , 0
10881110 for sys in self .syslist :
10891111 # Compute outputs for each system from current state
10901112 ysys = sys ._out (
@@ -1097,8 +1119,8 @@ def _compute_static_io(self, t, x, u):
10971119
10981120 # Store the input in the second part of ylist
10991121 ylist [noutputs + input_index :
1100- noutputs + input_index + sys .ninputs ] = \
1101- ulist [input_index :input_index + sys .ninputs ]
1122+ noutputs + input_index + sys .ninputs ] = \
1123+ ulist [input_index :input_index + sys .ninputs ]
11021124
11031125 # Increment the index pointers
11041126 state_index += sys .nstates
@@ -1229,7 +1251,8 @@ def _parse_signal(self, spec, signame='input', dictname=None):
12291251 return spec
12301252
12311253 # Figure out the name of the dictionary to use
1232- if dictname is None : dictname = signame + '_index'
1254+ if dictname is None :
1255+ dictname = signame + '_index'
12331256
12341257 if isinstance (spec , str ):
12351258 # If we got a dotted string, break up into pieces
@@ -1415,7 +1438,8 @@ def input_output_response(sys, T, U=0., X0=0, params={}, method='RK45',
14151438 for i in range (len (T )):
14161439 u = U [i ] if len (U .shape ) == 1 else U [:, i ]
14171440 y [:, i ] = sys ._out (T [i ], [], u )
1418- if (squeeze ): y = np .squeeze (y )
1441+ if squeeze :
1442+ y = np .squeeze (y )
14191443 if return_x :
14201444 return T , y , []
14211445 else :
@@ -1495,7 +1519,8 @@ def ivp_rhs(t, x): return sys._rhs(t, x, u(t))
14951519 raise TypeError ("Can't determine system type" )
14961520
14971521 # Get rid of extra dimensions in the output, of desired
1498- if (squeeze ): y = np .squeeze (y )
1522+ if squeeze :
1523+ y = np .squeeze (y )
14991524
15001525 if return_x :
15011526 return soln .t , y , soln .y
@@ -1580,9 +1605,12 @@ def find_eqpt(sys, x0, u0=[], y0=None, t=0, params={},
15801605 noutputs = _find_size (sys .noutputs , y0 )
15811606
15821607 # Convert x0, u0, y0 to arrays, if needed
1583- if np .isscalar (x0 ): x0 = np .ones ((nstates ,)) * x0
1584- if np .isscalar (u0 ): u0 = np .ones ((ninputs ,)) * u0
1585- if np .isscalar (y0 ): y0 = np .ones ((ninputs ,)) * y0
1608+ if np .isscalar (x0 ):
1609+ x0 = np .ones ((nstates ,)) * x0
1610+ if np .isscalar (u0 ):
1611+ u0 = np .ones ((ninputs ,)) * u0
1612+ if np .isscalar (y0 ):
1613+ y0 = np .ones ((ninputs ,)) * y0
15861614
15871615 # Discrete-time not yet supported
15881616 if isdtime (sys , strict = True ):
@@ -1718,7 +1746,8 @@ def rootfun(z):
17181746
17191747 # Compute the update and output maps
17201748 dx = sys ._rhs (t , x , u ) - dx0
1721- if dtime : dx -= x # TODO: check
1749+ if dtime :
1750+ dx -= x # TODO: check
17221751 dy = sys ._out (t , x , u ) - y0
17231752
17241753 # Map the results into the constrained variables
@@ -1736,7 +1765,8 @@ def rootfun(z):
17361765 z = (x , u , sys ._out (t , x , u ))
17371766
17381767 # Return the result based on what the user wants and what we found
1739- if not return_y : z = z [0 :2 ] # Strip y from result if not desired
1768+ if not return_y :
1769+ z = z [0 :2 ] # Strip y from result if not desired
17401770 if return_result :
17411771 # Return whatever we got, along with the result dictionary
17421772 return z + (result ,)
@@ -1810,7 +1840,8 @@ def _find_size(sysval, vecval):
18101840
18111841
18121842# Convert a state space system into an input/output system (wrapper)
1813- def ss2io (* args , ** kw ): return LinearIOSystem (* args , ** kw )
1843+ def ss2io (* args , ** kw ):
1844+ return LinearIOSystem (* args , ** kw )
18141845ss2io .__doc__ = LinearIOSystem .__init__ .__doc__
18151846
18161847
0 commit comments