11# nlsys.py - input/output system module
2- #
32# RMM, 28 April 2019
43#
54# Additional features to add
1918
2019"""
2120
22- __author__ = "Richard Murray"
23- __copyright__ = "Copyright 2019, California Institute of Technology"
24- __credits__ = ["Richard Murray" ]
25- __license__ = "BSD"
26- __maintainer__ = "Richard Murray"
27- __email__ = "murray@cds.caltech.edu"
28-
2921import numpy as np
3022import scipy as sp
3123import copy
4133 'input_output_response' , 'find_eqpt' , 'linearize' ,
4234 'interconnect' ]
4335
44- # Define module default parameter values
45- _iosys_defaults = {}
46-
4736
4837class NonlinearIOSystem (InputOutputSystem ):
4938 """Nonlinear I/O system.
@@ -110,10 +99,19 @@ class NonlinearIOSystem(InputOutputSystem):
11099 --------
111100 InputOutputSystem : Input/output system class.
112101
113- """
114- # Set priority for operators
115- __array_priority__ = 13 # override ndarray, SS and TF types
102+ Notes
103+ -----
104+ The :class:`~control.InputOuputSystem` class (and its subclasses) makes
105+ use of two special methods for implementing much of the work of the class:
116106
107+ * _rhs(t, x, u): compute the right hand side of the differential or
108+ difference equation for the system. If not specified, the system
109+ has no state.
110+
111+ * _out(t, x, u): compute the output for the current state of the system.
112+ The default is to return the entire system state.
113+
114+ """
117115 def __init__ (self , updfcn , outfcn = None , params = None , ** kwargs ):
118116 """Create a nonlinear I/O system given update and output functions."""
119117 # Process keyword arguments
@@ -135,8 +133,9 @@ def __init__(self, updfcn, outfcn=None, params=None, **kwargs):
135133 if self .nstates is None :
136134 self .nstates = 0
137135 else :
138- raise ValueError ("States specified but no update function "
139- "given." )
136+ raise ValueError (
137+ "states specified but no update function given." )
138+
140139 if outfcn is None :
141140 # No output function specified => outputs = states
142141 if self .noutputs is None and self .nstates is not None :
@@ -145,7 +144,7 @@ def __init__(self, updfcn, outfcn=None, params=None, **kwargs):
145144 # Number of outputs = number of states => all is OK
146145 pass
147146 elif self .noutputs is not None and self .noutputs != 0 :
148- raise ValueError ("Outputs specified but no output function "
147+ raise ValueError ("outputs specified but no output function "
149148 "(and nstates not known)." )
150149
151150 # Initialize current parameters to default parameters
@@ -266,7 +265,7 @@ def __add__(self, other):
266265 # Make sure number of input and outputs match
267266 if self .ninputs != other .ninputs or self .noutputs != other .noutputs :
268267 raise ValueError ("Can't add systems with incompatible numbers of "
269- "inputs or outputs. " )
268+ "inputs or outputs" )
270269
271270 # Create a new system to handle the composition
272271 inplist = [[(0 , i ), (1 , i )] for i in range (self .ninputs )]
@@ -286,8 +285,8 @@ def __radd__(self, other):
286285
287286 # Make sure number of input and outputs match
288287 if self .ninputs != other .ninputs or self .noutputs != other .noutputs :
289- raise ValueError ("Can 't add systems with incompatible numbers of "
290- "inputs or outputs. " )
288+ raise ValueError ("can 't add systems with incompatible numbers of "
289+ "inputs or outputs" )
291290
292291 # Create a new system to handle the composition
293292 inplist = [[(0 , i ), (1 , i )] for i in range (other .ninputs )]
@@ -308,8 +307,8 @@ def __sub__(self, other):
308307 # Make sure number of input and outputs match
309308 if self .ninputs != other .ninputs or self .noutputs != other .noutputs :
310309 raise ValueError (
311- "Can 't substract systems with incompatible numbers of "
312- "inputs or outputs. " )
310+ "can 't substract systems with incompatible numbers of "
311+ "inputs or outputs" )
313312 ninputs = self .ninputs
314313 noutputs = self .noutputs
315314
@@ -613,7 +612,7 @@ def __init__(self, syslist, connections=None, inplist=None, outlist=None,
613612 name , inputs , outputs , states , _ = _process_iosys_keywords (kwargs )
614613
615614 # Initialize the system list and index
616- self .syslist = list (syslist ) # insure modifications can be made
615+ self .syslist = list (syslist ) # ensure modifications can be made
617616 self .syslist_index = {}
618617
619618 # Initialize the input, output, and state counts, indices
@@ -638,7 +637,7 @@ def __init__(self, syslist, connections=None, inplist=None, outlist=None,
638637 # Make sure number of inputs, outputs, states is given
639638 if sys .ninputs is None or sys .noutputs is None or \
640639 sys .nstates is None :
641- raise TypeError ("System '%s' must define number of inputs, "
640+ raise TypeError ("system '%s' must define number of inputs, "
642641 "outputs, states in order to be connected" %
643642 sys .name )
644643
@@ -734,7 +733,7 @@ def outfcn(t, x, u, params):
734733 input_indices , output_indices ):
735734 if self .connect_map [input_index , output_index ] != 0 :
736735 warn ("multiple connections given for input %d" %
737- input_index + ". Combining with previous entries. " )
736+ input_index + "; combining with previous entries" )
738737 self .connect_map [input_index , output_index ] += gain
739738
740739 # Convert the input list to a matrix: maps system to subsystems
@@ -744,13 +743,13 @@ def outfcn(t, x, u, params):
744743 inpspec = [inpspec ]
745744 if not isinstance (inpspec , list ):
746745 raise ValueError ("specifications in inplist must be of type "
747- "int, str, tuple or list. " )
746+ "int, str, tuple or list" )
748747 for spec in inpspec :
749748 ulist_indices = self ._parse_input_spec (spec )
750749 for j , ulist_index in enumerate (ulist_indices ):
751750 if self .input_map [ulist_index , index ] != 0 :
752751 warn ("multiple connections given for input %d" %
753- index + ". Combining with previous entries." )
752+ index + "; combining with previous entries." )
754753 self .input_map [ulist_index , index + j ] += 1
755754
756755 # Convert the output list to a matrix: maps subsystems to system
@@ -760,13 +759,13 @@ def outfcn(t, x, u, params):
760759 outspec = [outspec ]
761760 if not isinstance (outspec , list ):
762761 raise ValueError ("specifications in outlist must be of type "
763- "int, str, tuple or list. " )
762+ "int, str, tuple or list" )
764763 for spec in outspec :
765764 ylist_indices , gain = self ._parse_output_spec (spec )
766765 for j , ylist_index in enumerate (ylist_indices ):
767766 if self .output_map [index , ylist_index ] != 0 :
768767 warn ("multiple connections given for output %d" %
769- index + ". Combining with previous entries. " )
768+ index + "; combining with previous entries" )
770769 self .output_map [index + j , ylist_index ] += gain
771770
772771 def _update_params (self , params , warning = False ):
@@ -866,7 +865,7 @@ def _compute_static_io(self, t, x, u):
866865
867866 # Make sure that we stopped before detecting an algebraic loop
868867 if cycle_count == 0 :
869- raise RuntimeError ("Algebraic loop detected. " )
868+ raise RuntimeError ("algebraic loop detected" )
870869
871870 return ulist , ylist
872871
@@ -876,7 +875,7 @@ def _parse_input_spec(self, spec):
876875 subsys_index , input_indices , gain = _parse_spec (
877876 self .syslist , spec , 'input' )
878877 if gain != 1 :
879- raise ValueError ("gain not allowed in spec '%s'. " % str (spec ))
878+ raise ValueError ("gain not allowed in spec '%s'" % str (spec ))
880879
881880 # Return the indices into the input vector list (ylist)
882881 return [self .input_offset [subsys_index ] + i for i in input_indices ]
@@ -1431,8 +1430,8 @@ def ivp_rhs(t, x):
14311430 # Make sure the time vector is uniformly spaced
14321431 dt = t_eval [1 ] - t_eval [0 ]
14331432 if not np .allclose (t_eval [1 :] - t_eval [:- 1 ], dt ):
1434- raise ValueError ("Parameter ``t_eval``: time values must be "
1435- "equally spaced. " )
1433+ raise ValueError ("parameter ``t_eval``: time values must be "
1434+ "equally spaced" )
14361435
14371436 # Make sure the sample time matches the given time
14381437 if sys .dt is not True :
@@ -1579,7 +1578,7 @@ def find_eqpt(sys, x0, u0=None, y0=None, t=0, params=None,
15791578 (u0 is not None and len (u0 ) != ninputs ) or \
15801579 (y0 is not None and len (y0 ) != noutputs ) or \
15811580 (dx0 is not None and len (dx0 ) != nstates ):
1582- raise ValueError ("Length of input arguments does not match system. " )
1581+ raise ValueError ("length of input arguments does not match system" )
15831582
15841583 # Update the parameter values
15851584 sys ._update_params (params )
@@ -1691,8 +1690,8 @@ def rootfun(z):
16911690 num_freedoms = len (state_vars ) + len (input_vars )
16921691 num_constraints = len (output_vars ) + len (deriv_vars )
16931692 if num_constraints != num_freedoms :
1694- warn ("Number of constraints (%d) does not match number of degrees "
1695- "of freedom (%d). Results may be meaningless. " %
1693+ warn ("number of constraints (%d) does not match number of degrees "
1694+ "of freedom (%d); results may be meaningless" %
16961695 (num_constraints , num_freedoms ))
16971696
16981697 # Make copies of the state and input variables to avoid overwriting
@@ -1823,7 +1822,7 @@ def _find_size(sysval, vecval):
18231822 elif sysval == 1 :
18241823 # (1, scalar) is also a valid combination from legacy code
18251824 return 1
1826- raise ValueError ("Can 't determine size of system component. " )
1825+ raise ValueError ("can 't determine size of system component" )
18271826
18281827
18291828# Function to create an interconnected system
0 commit comments