@@ -111,7 +111,7 @@ class for a set of subclasses that are used to implement specific
111
111
The :class:`~control.InputOuputSystem` class (and its subclasses) makes
112
112
use of two special methods for implementing much of the work of the class:
113
113
114
- * _rhs (t, x, u): compute the right hand side of the differential or
114
+ * dynamics (t, x, u): compute the right hand side of the differential or
115
115
difference equation for the system. This must be specified by the
116
116
subclass for the system.
117
117
@@ -353,28 +353,69 @@ def _process_signal_list(self, signals, prefix='s'):
353
353
# Find a signal by name
354
354
def _find_signal (self , name , sigdict ): return sigdict .get (name , None )
355
355
356
- # Update parameters used for _rhs , _out (used by subclasses)
356
+ # Update parameters used for dynamics , _out (used by subclasses)
357
357
def _update_params (self , params , warning = False ):
358
358
if (warning ):
359
359
warn ("Parameters passed to InputOutputSystem ignored." )
360
360
361
- def _rhs (self , t , x , u ):
362
- """Evaluate right hand side of a differential or difference equation.
361
+ def dynamics (self , t , x , u ):
362
+ """Compute the dynamics of a differential or difference equation.
363
363
364
- Private function used to compute the right hand side of an
365
- input/output system model.
364
+ Given time `t`, input `u` and state `x`, returns the dynamics of the
365
+ system. If the system is continuous, returns the time derivative
366
366
367
+ ``dx/dt = f(t, x, u)``
368
+
369
+ where `f` is the system's (possibly nonlinear) dynamics function.
370
+ If the system is discrete-time, returns the next value of `x`:
371
+
372
+ ``x[t+dt] = f(t, x[t], u[t])``
373
+
374
+ Where `t` is a scalar.
375
+
376
+ The inputs `x` and `u` must be of the correct length.
377
+
378
+ Parameters
379
+ ----------
380
+ t : float
381
+ the time at which to evaluate
382
+ x : array_like
383
+ current state
384
+ u : array_like
385
+ input
386
+
387
+ Returns
388
+ -------
389
+ `dx/dt` or `x[t+dt]` : ndarray
367
390
"""
368
- NotImplemented ("Evaluation not implemented for system of type " ,
391
+
392
+ NotImplemented ("Dynamics not implemented for system of type " ,
369
393
type (self ))
370
394
371
395
def _out (self , t , x , u , params = {}):
372
- """Evaluate the output of a system at a given state, input, and time
396
+ """Compute the output of the system
397
+
398
+ Given time `t`, input `u` and state `x`, returns the output of the
399
+ system:
400
+
401
+ ``y = g(t, x, u)``
373
402
374
- Private function used to compute the output of of an input/output
375
- system model given the state, input, parameters, and time.
403
+ The inputs `x` and `u` must be of the correct length.
376
404
405
+ Parameters
406
+ ----------
407
+ t : float
408
+ the time at which to evaluate
409
+ x : array_like
410
+ current state
411
+ u : array_like
412
+ input
413
+
414
+ Returns
415
+ -------
416
+ y : ndarray
377
417
"""
418
+
378
419
# If no output function was defined in subclass, return state
379
420
return x
380
421
@@ -533,7 +574,7 @@ def linearize(self, x0, u0, t=0, params={}, eps=1e-6,
533
574
"""
534
575
#
535
576
# If the linearization is not defined by the subclass, perform a
536
- # numerical linearization use the `_rhs ()` and `_out()` member
577
+ # numerical linearization use the `dynamics ()` and `_out()` member
537
578
# functions.
538
579
#
539
580
@@ -554,7 +595,7 @@ def linearize(self, x0, u0, t=0, params={}, eps=1e-6,
554
595
self ._update_params (params )
555
596
556
597
# Compute the nominal value of the update law and output
557
- F0 = self ._rhs (t , x0 , u0 )
598
+ F0 = self .dynamics (t , x0 , u0 )
558
599
H0 = self ._out (t , x0 , u0 )
559
600
560
601
# Create empty matrices that we can fill up with linearizations
@@ -567,14 +608,14 @@ def linearize(self, x0, u0, t=0, params={}, eps=1e-6,
567
608
for i in range (nstates ):
568
609
dx = np .zeros ((nstates ,))
569
610
dx [i ] = eps
570
- A [:, i ] = (self ._rhs (t , x0 + dx , u0 ) - F0 ) / eps
611
+ A [:, i ] = (self .dynamics (t , x0 + dx , u0 ) - F0 ) / eps
571
612
C [:, i ] = (self ._out (t , x0 + dx , u0 ) - H0 ) / eps
572
613
573
614
# Perturb each of the input variables and compute linearization
574
615
for i in range (ninputs ):
575
616
du = np .zeros ((ninputs ,))
576
617
du [i ] = eps
577
- B [:, i ] = (self ._rhs (t , x0 , u0 + du ) - F0 ) / eps
618
+ B [:, i ] = (self .dynamics (t , x0 , u0 + du ) - F0 ) / eps
578
619
D [:, i ] = (self ._out (t , x0 , u0 + du ) - H0 ) / eps
579
620
580
621
# Create the state space system
@@ -694,7 +735,7 @@ def _update_params(self, params={}, warning=True):
694
735
if params and warning :
695
736
warn ("Parameters passed to LinearIOSystems are ignored." )
696
737
697
- def _rhs (self , t , x , u ):
738
+ def dynamics (self , t , x , u ):
698
739
# Convert input to column vector and then change output to 1D array
699
740
xdot = np .dot (self .A , np .reshape (x , (- 1 , 1 ))) \
700
741
+ np .dot (self .B , np .reshape (u , (- 1 , 1 )))
@@ -863,7 +904,7 @@ def _update_params(self, params, warning=False):
863
904
self ._current_params = self .params .copy ()
864
905
self ._current_params .update (params )
865
906
866
- def _rhs (self , t , x , u ):
907
+ def dynamics (self , t , x , u ):
867
908
xdot = self .updfcn (t , x , u , self ._current_params ) \
868
909
if self .updfcn is not None else []
869
910
return np .array (xdot ).reshape ((- 1 ,))
@@ -1033,7 +1074,7 @@ def _update_params(self, params, warning=False):
1033
1074
local .update (params ) # update with locally passed parameters
1034
1075
sys ._update_params (local , warning = warning )
1035
1076
1036
- def _rhs (self , t , x , u ):
1077
+ def dynamics (self , t , x , u ):
1037
1078
# Make sure state and input are vectors
1038
1079
x = np .array (x , ndmin = 1 )
1039
1080
u = np .array (u , ndmin = 1 )
@@ -1047,7 +1088,7 @@ def _rhs(self, t, x, u):
1047
1088
for sys in self .syslist :
1048
1089
# Update the right hand side for this subsystem
1049
1090
if sys .nstates != 0 :
1050
- xdot [state_index :state_index + sys .nstates ] = sys ._rhs (
1091
+ xdot [state_index :state_index + sys .nstates ] = sys .dynamics (
1051
1092
t , x [state_index :state_index + sys .nstates ],
1052
1093
ulist [input_index :input_index + sys .ninputs ])
1053
1094
@@ -1497,14 +1538,14 @@ def input_output_response(sys, T, U=0., X0=0, params={}, method='RK45',
1497
1538
1498
1539
# Create a lambda function for the right hand side
1499
1540
u = sp .interpolate .interp1d (T , U , fill_value = "extrapolate" )
1500
- def ivp_rhs (t , x ): return sys ._rhs (t , x , u (t ))
1541
+ def ivp_dynamics (t , x ): return sys .dynamics (t , x , u (t ))
1501
1542
1502
1543
# Perform the simulation
1503
1544
if isctime (sys ):
1504
1545
if not hasattr (sp .integrate , 'solve_ivp' ):
1505
1546
raise NameError ("scipy.integrate.solve_ivp not found; "
1506
1547
"use SciPy 1.0 or greater" )
1507
- soln = sp .integrate .solve_ivp (ivp_rhs , (T0 , Tf ), X0 , t_eval = T ,
1548
+ soln = sp .integrate .solve_ivp (ivp_dynamics , (T0 , Tf ), X0 , t_eval = T ,
1508
1549
method = method , vectorized = False )
1509
1550
1510
1551
# Compute the output associated with the state (and use sys.out to
@@ -1549,7 +1590,7 @@ def ivp_rhs(t, x): return sys._rhs(t, x, u(t))
1549
1590
y .append (sys ._out (T [i ], x , u (T [i ])))
1550
1591
1551
1592
# Update the state for the next iteration
1552
- x = sys ._rhs (T [i ], x , u (T [i ]))
1593
+ x = sys .dynamics (T [i ], x , u (T [i ]))
1553
1594
1554
1595
# Convert output to numpy arrays
1555
1596
soln .y = np .transpose (np .array (soln .y ))
@@ -1670,8 +1711,8 @@ def find_eqpt(sys, x0, u0=[], y0=None, t=0, params={},
1670
1711
if y0 is None :
1671
1712
# Take u0 as fixed and minimize over x
1672
1713
# TODO: update to allow discrete time systems
1673
- def ode_rhs (z ): return sys ._rhs (t , z , u0 )
1674
- result = root (ode_rhs , x0 , ** kw )
1714
+ def ode_dynamics (z ): return sys .dynamics (t , z , u0 )
1715
+ result = root (ode_dynamics , x0 , ** kw )
1675
1716
z = (result .x , u0 , sys ._out (t , result .x , u0 ))
1676
1717
else :
1677
1718
# Take y0 as fixed and minimize over x and u
@@ -1680,7 +1721,7 @@ def rootfun(z):
1680
1721
x , u = np .split (z , [nstates ])
1681
1722
# TODO: update to allow discrete time systems
1682
1723
return np .concatenate (
1683
- (sys ._rhs (t , x , u ), sys ._out (t , x , u ) - y0 ), axis = 0 )
1724
+ (sys .dynamics (t , x , u ), sys ._out (t , x , u ) - y0 ), axis = 0 )
1684
1725
z0 = np .concatenate ((x0 , u0 ), axis = 0 ) # Put variables together
1685
1726
result = root (rootfun , z0 , ** kw ) # Find the eq point
1686
1727
x , u = np .split (result .x , [nstates ]) # Split result back in two
@@ -1782,7 +1823,7 @@ def rootfun(z):
1782
1823
u [input_vars ] = z [nstate_vars :]
1783
1824
1784
1825
# Compute the update and output maps
1785
- dx = sys ._rhs (t , x , u ) - dx0
1826
+ dx = sys .dynamics (t , x , u ) - dx0
1786
1827
if dtime :
1787
1828
dx -= x # TODO: check
1788
1829
dy = sys ._out (t , x , u ) - y0
0 commit comments