Skip to content

Commit 033b236

Browse files
committed
rename method _rhs in iosys to dynamics
1 parent f701b75 commit 033b236

File tree

5 files changed

+97
-56
lines changed

5 files changed

+97
-56
lines changed

control/iosys.py

Lines changed: 66 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ class for a set of subclasses that are used to implement specific
111111
The :class:`~control.InputOuputSystem` class (and its subclasses) makes
112112
use of two special methods for implementing much of the work of the class:
113113
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
115115
difference equation for the system. This must be specified by the
116116
subclass for the system.
117117
@@ -353,28 +353,69 @@ def _process_signal_list(self, signals, prefix='s'):
353353
# Find a signal by name
354354
def _find_signal(self, name, sigdict): return sigdict.get(name, None)
355355

356-
# Update parameters used for _rhs, _out (used by subclasses)
356+
# Update parameters used for dynamics, _out (used by subclasses)
357357
def _update_params(self, params, warning=False):
358358
if (warning):
359359
warn("Parameters passed to InputOutputSystem ignored.")
360360

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.
363363
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
366366
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
367390
"""
368-
NotImplemented("Evaluation not implemented for system of type ",
391+
392+
NotImplemented("Dynamics not implemented for system of type ",
369393
type(self))
370394

371395
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)``
373402
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.
376404
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
377417
"""
418+
378419
# If no output function was defined in subclass, return state
379420
return x
380421

@@ -533,7 +574,7 @@ def linearize(self, x0, u0, t=0, params={}, eps=1e-6,
533574
"""
534575
#
535576
# 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
537578
# functions.
538579
#
539580

@@ -554,7 +595,7 @@ def linearize(self, x0, u0, t=0, params={}, eps=1e-6,
554595
self._update_params(params)
555596

556597
# Compute the nominal value of the update law and output
557-
F0 = self._rhs(t, x0, u0)
598+
F0 = self.dynamics(t, x0, u0)
558599
H0 = self._out(t, x0, u0)
559600

560601
# 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,
567608
for i in range(nstates):
568609
dx = np.zeros((nstates,))
569610
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
571612
C[:, i] = (self._out(t, x0 + dx, u0) - H0) / eps
572613

573614
# Perturb each of the input variables and compute linearization
574615
for i in range(ninputs):
575616
du = np.zeros((ninputs,))
576617
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
578619
D[:, i] = (self._out(t, x0, u0 + du) - H0) / eps
579620

580621
# Create the state space system
@@ -694,7 +735,7 @@ def _update_params(self, params={}, warning=True):
694735
if params and warning:
695736
warn("Parameters passed to LinearIOSystems are ignored.")
696737

697-
def _rhs(self, t, x, u):
738+
def dynamics(self, t, x, u):
698739
# Convert input to column vector and then change output to 1D array
699740
xdot = np.dot(self.A, np.reshape(x, (-1, 1))) \
700741
+ np.dot(self.B, np.reshape(u, (-1, 1)))
@@ -863,7 +904,7 @@ def _update_params(self, params, warning=False):
863904
self._current_params = self.params.copy()
864905
self._current_params.update(params)
865906

866-
def _rhs(self, t, x, u):
907+
def dynamics(self, t, x, u):
867908
xdot = self.updfcn(t, x, u, self._current_params) \
868909
if self.updfcn is not None else []
869910
return np.array(xdot).reshape((-1,))
@@ -1033,7 +1074,7 @@ def _update_params(self, params, warning=False):
10331074
local.update(params) # update with locally passed parameters
10341075
sys._update_params(local, warning=warning)
10351076

1036-
def _rhs(self, t, x, u):
1077+
def dynamics(self, t, x, u):
10371078
# Make sure state and input are vectors
10381079
x = np.array(x, ndmin=1)
10391080
u = np.array(u, ndmin=1)
@@ -1047,7 +1088,7 @@ def _rhs(self, t, x, u):
10471088
for sys in self.syslist:
10481089
# Update the right hand side for this subsystem
10491090
if sys.nstates != 0:
1050-
xdot[state_index:state_index + sys.nstates] = sys._rhs(
1091+
xdot[state_index:state_index + sys.nstates] = sys.dynamics(
10511092
t, x[state_index:state_index + sys.nstates],
10521093
ulist[input_index:input_index + sys.ninputs])
10531094

@@ -1497,14 +1538,14 @@ def input_output_response(sys, T, U=0., X0=0, params={}, method='RK45',
14971538

14981539
# Create a lambda function for the right hand side
14991540
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))
15011542

15021543
# Perform the simulation
15031544
if isctime(sys):
15041545
if not hasattr(sp.integrate, 'solve_ivp'):
15051546
raise NameError("scipy.integrate.solve_ivp not found; "
15061547
"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,
15081549
method=method, vectorized=False)
15091550

15101551
# 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))
15491590
y.append(sys._out(T[i], x, u(T[i])))
15501591

15511592
# 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]))
15531594

15541595
# Convert output to numpy arrays
15551596
soln.y = np.transpose(np.array(soln.y))
@@ -1670,8 +1711,8 @@ def find_eqpt(sys, x0, u0=[], y0=None, t=0, params={},
16701711
if y0 is None:
16711712
# Take u0 as fixed and minimize over x
16721713
# 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)
16751716
z = (result.x, u0, sys._out(t, result.x, u0))
16761717
else:
16771718
# Take y0 as fixed and minimize over x and u
@@ -1680,7 +1721,7 @@ def rootfun(z):
16801721
x, u = np.split(z, [nstates])
16811722
# TODO: update to allow discrete time systems
16821723
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)
16841725
z0 = np.concatenate((x0, u0), axis=0) # Put variables together
16851726
result = root(rootfun, z0, **kw) # Find the eq point
16861727
x, u = np.split(result.x, [nstates]) # Split result back in two
@@ -1782,7 +1823,7 @@ def rootfun(z):
17821823
u[input_vars] = z[nstate_vars:]
17831824

17841825
# Compute the update and output maps
1785-
dx = sys._rhs(t, x, u) - dx0
1826+
dx = sys.dynamics(t, x, u) - dx0
17861827
if dtime:
17871828
dx -= x # TODO: check
17881829
dy = sys._out(t, x, u) - y0

control/sisotool.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ def sisotool(sys, kvect=None, xlim_rlocus=None, ylim_rlocus=None,
101101

102102
# First time call to setup the bode and step response plots
103103
_SisotoolUpdate(sys, fig,
104-
1 if kvect is None else kvect[0], bode_plot_params)
104+
1 if kvect is None else kvect[0], bode_plot_params, tvect=tvect)
105105

106106
# Setup the root-locus plot window
107107
root_locus(sys, kvect=kvect, xlim=xlim_rlocus,

control/tests/iosys_test.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def test_linear_iosys(self, tsys):
5656
# Make sure that the right hand side matches linear system
5757
for x, u in (([0, 0], 0), ([1, 0], 0), ([0, 1], 0), ([0, 0], 1)):
5858
np.testing.assert_array_almost_equal(
59-
np.reshape(iosys._rhs(0, x, u), (-1, 1)),
59+
np.reshape(iosys.dynamics(0, x, u), (-1, 1)),
6060
np.dot(linsys.A, np.reshape(x, (-1, 1))) + np.dot(linsys.B, u))
6161

6262
# Make sure that simulations also line up
@@ -687,7 +687,7 @@ def test_find_eqpts(self, tsys):
687687
assert result.success
688688
np.testing.assert_array_almost_equal(xeq, [1.64705879, 1.17923874])
689689
np.testing.assert_array_almost_equal(
690-
nlsys._rhs(0, xeq, ueq), np.zeros((2,)))
690+
nlsys.dynamics(0, xeq, ueq), np.zeros((2,)))
691691

692692
# Ducted fan dynamics with output = velocity
693693
nlsys = ios.NonlinearIOSystem(pvtol, lambda t, x, u, params: x[0:2])
@@ -697,15 +697,15 @@ def test_find_eqpts(self, tsys):
697697
nlsys, [0, 0, 0, 0], [0, 4*9.8], return_result=True)
698698
assert result.success
699699
np.testing.assert_array_almost_equal(
700-
nlsys._rhs(0, xeq, ueq), np.zeros((4,)))
700+
nlsys.dynamics(0, xeq, ueq), np.zeros((4,)))
701701
np.testing.assert_array_almost_equal(xeq, [0, 0, 0, 0])
702702

703703
# Use a small lateral force to cause motion
704704
xeq, ueq, result = ios.find_eqpt(
705705
nlsys, [0, 0, 0, 0], [0.01, 4*9.8], return_result=True)
706706
assert result.success
707707
np.testing.assert_array_almost_equal(
708-
nlsys._rhs(0, xeq, ueq), np.zeros((4,)), decimal=5)
708+
nlsys.dynamics(0, xeq, ueq), np.zeros((4,)), decimal=5)
709709

710710
# Equilibrium point with fixed output
711711
xeq, ueq, result = ios.find_eqpt(
@@ -715,7 +715,7 @@ def test_find_eqpts(self, tsys):
715715
np.testing.assert_array_almost_equal(
716716
nlsys._out(0, xeq, ueq), [0.1, 0.1], decimal=5)
717717
np.testing.assert_array_almost_equal(
718-
nlsys._rhs(0, xeq, ueq), np.zeros((4,)), decimal=5)
718+
nlsys.dynamics(0, xeq, ueq), np.zeros((4,)), decimal=5)
719719

720720
# Specify outputs to constrain (replicate previous)
721721
xeq, ueq, result = ios.find_eqpt(
@@ -725,15 +725,15 @@ def test_find_eqpts(self, tsys):
725725
np.testing.assert_array_almost_equal(
726726
nlsys._out(0, xeq, ueq), [0.1, 0.1], decimal=5)
727727
np.testing.assert_array_almost_equal(
728-
nlsys._rhs(0, xeq, ueq), np.zeros((4,)), decimal=5)
728+
nlsys.dynamics(0, xeq, ueq), np.zeros((4,)), decimal=5)
729729

730730
# Specify inputs to constrain (replicate previous), w/ no result
731731
xeq, ueq = ios.find_eqpt(
732732
nlsys, [0, 0, 0, 0], [0.01, 4*9.8], y0=[0.1, 0.1], iu = [])
733733
np.testing.assert_array_almost_equal(
734734
nlsys._out(0, xeq, ueq), [0.1, 0.1], decimal=5)
735735
np.testing.assert_array_almost_equal(
736-
nlsys._rhs(0, xeq, ueq), np.zeros((4,)), decimal=5)
736+
nlsys.dynamics(0, xeq, ueq), np.zeros((4,)), decimal=5)
737737

738738
# Now solve the problem with the original PVTOL variables
739739
# Constrain the output angle and x velocity
@@ -746,7 +746,7 @@ def test_find_eqpts(self, tsys):
746746
np.testing.assert_array_almost_equal(
747747
nlsys_full._out(0, xeq, ueq)[[2, 3]], [0.1, 0.1], decimal=5)
748748
np.testing.assert_array_almost_equal(
749-
nlsys_full._rhs(0, xeq, ueq)[-4:], np.zeros((4,)), decimal=5)
749+
nlsys_full.dynamics(0, xeq, ueq)[-4:], np.zeros((4,)), decimal=5)
750750

751751
# Fix one input and vary the other
752752
nlsys_full = ios.NonlinearIOSystem(pvtol_full, None)
@@ -759,7 +759,7 @@ def test_find_eqpts(self, tsys):
759759
np.testing.assert_array_almost_equal(
760760
nlsys_full._out(0, xeq, ueq)[[3]], [0.1], decimal=5)
761761
np.testing.assert_array_almost_equal(
762-
nlsys_full._rhs(0, xeq, ueq)[-4:], np.zeros((4,)), decimal=5)
762+
nlsys_full.dynamics(0, xeq, ueq)[-4:], np.zeros((4,)), decimal=5)
763763

764764
# PVTOL with output = y velocity
765765
xeq, ueq, result = ios.find_eqpt(
@@ -771,7 +771,7 @@ def test_find_eqpts(self, tsys):
771771
np.testing.assert_array_almost_equal(
772772
nlsys_full._out(0, xeq, ueq)[-3:], [0.1, 0, 0], decimal=5)
773773
np.testing.assert_array_almost_equal(
774-
nlsys_full._rhs(0, xeq, ueq)[-5:], np.zeros((5,)), decimal=5)
774+
nlsys_full.dynamics(0, xeq, ueq)[-5:], np.zeros((5,)), decimal=5)
775775

776776
# Unobservable system
777777
linsys = ct.StateSpace(

control/tests/type_conversion_test.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def sys_dict():
1919
sdict['frd'] = ct.frd([10+0j, 9 + 1j, 8 + 2j], [1,2,3])
2020
sdict['lio'] = ct.LinearIOSystem(ct.ss([[-1]], [[5]], [[5]], [[0]]))
2121
sdict['ios'] = ct.NonlinearIOSystem(
22-
sdict['lio']._rhs, sdict['lio']._out, 1, 1, 1)
22+
sdict['lio'].dynamics, sdict['lio']._out, 1, 1, 1)
2323
sdict['arr'] = np.array([[2.0]])
2424
sdict['flt'] = 3.
2525
return sdict
@@ -66,7 +66,7 @@ def sys_dict():
6666
('add', 'ios', ['xos', 'xos', 'E', 'ios', 'ios', 'xos', 'xos']),
6767
('add', 'arr', ['ss', 'tf', 'xrd', 'xio', 'xos', 'arr', 'arr']),
6868
('add', 'flt', ['ss', 'tf', 'xrd', 'xio', 'xos', 'arr', 'flt']),
69-
69+
7070
# op left ss tf frd lio ios arr flt
7171
('sub', 'ss', ['ss', 'ss', 'xrd', 'ss', 'xos', 'ss', 'ss' ]),
7272
('sub', 'tf', ['tf', 'tf', 'xrd', 'tf', 'xos', 'tf', 'tf' ]),
@@ -75,7 +75,7 @@ def sys_dict():
7575
('sub', 'ios', ['xos', 'xio', 'E', 'ios', 'xos' 'xos', 'xos']),
7676
('sub', 'arr', ['ss', 'tf', 'xrd', 'xio', 'xos', 'arr', 'arr']),
7777
('sub', 'flt', ['ss', 'tf', 'xrd', 'xio', 'xos', 'arr', 'flt']),
78-
78+
7979
# op left ss tf frd lio ios arr flt
8080
('mul', 'ss', ['ss', 'ss', 'xrd', 'ss', 'xos', 'ss', 'ss' ]),
8181
('mul', 'tf', ['tf', 'tf', 'xrd', 'tf', 'xos', 'tf', 'tf' ]),
@@ -84,7 +84,7 @@ def sys_dict():
8484
('mul', 'ios', ['xos', 'xos', 'E', 'ios', 'ios', 'xos', 'xos']),
8585
('mul', 'arr', ['ss', 'tf', 'xrd', 'xio', 'xos', 'arr', 'arr']),
8686
('mul', 'flt', ['ss', 'tf', 'frd', 'xio', 'xos', 'arr', 'flt']),
87-
87+
8888
# op left ss tf frd lio ios arr flt
8989
('truediv', 'ss', ['xs', 'tf', 'xrd', 'xio', 'xos', 'xs', 'xs' ]),
9090
('truediv', 'tf', ['tf', 'tf', 'xrd', 'tf', 'xos', 'tf', 'tf' ]),
@@ -100,7 +100,7 @@ def sys_dict():
100100
for rtype, expected in zip(rtype_list, expected_list):
101101
# Add this to the list of tests to run
102102
test_matrix.append([opname, ltype, rtype, expected])
103-
103+
104104
@pytest.mark.parametrize("opname, ltype, rtype, expected", test_matrix)
105105
def test_operator_type_conversion(opname, ltype, rtype, expected, sys_dict):
106106
op = getattr(operator, opname)
@@ -110,7 +110,7 @@ def test_operator_type_conversion(opname, ltype, rtype, expected, sys_dict):
110110
# Get rid of warnings for InputOutputSystem objects by making a copy
111111
if isinstance(leftsys, ct.InputOutputSystem) and leftsys == rightsys:
112112
rightsys = leftsys.copy()
113-
113+
114114
# Make sure we get the right result
115115
if expected == 'E' or expected[0] == 'x':
116116
# Exception expected
@@ -119,7 +119,7 @@ def test_operator_type_conversion(opname, ltype, rtype, expected, sys_dict):
119119
else:
120120
# Operation should work and return the given type
121121
result = op(leftsys, rightsys)
122-
122+
123123
# Print out what we are testing in case something goes wrong
124124
assert isinstance(result, type_dict[expected])
125125

@@ -138,7 +138,7 @@ def test_operator_type_conversion(opname, ltype, rtype, expected, sys_dict):
138138
#
139139
# * For IOS/LTI, convert to IOS. In the case of a linear I/O system (LIO),
140140
# this will preserve the linear structure since the LTI system will
141-
# be converted to state space.
141+
# be converted to state space.
142142
#
143143
# * When combining state space or transfer with linear I/O systems, the
144144
# * output should be of type Linear IO system, since that maintains the
@@ -149,7 +149,7 @@ def test_operator_type_conversion(opname, ltype, rtype, expected, sys_dict):
149149

150150
type_list = ['ss', 'tf', 'tfx', 'frd', 'lio', 'ios', 'arr', 'flt']
151151
conversion_table = [
152-
# L \ R ['ss', 'tf', 'tfx', 'frd', 'lio', 'ios', 'arr', 'flt']
152+
# L \ R ['ss', 'tf', 'tfx', 'frd', 'lio', 'ios', 'arr', 'flt']
153153
('ss', ['ss', 'ss', 'tf' 'frd', 'lio', 'ios', 'ss', 'ss' ]),
154154
('tf', ['tf', 'tf', 'tf' 'frd', 'lio', 'ios', 'tf', 'tf' ]),
155155
('tfx', ['tf', 'tf', 'tf', 'frd', 'E', 'E', 'tf', 'tf' ]),

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy