Skip to content

Commit 2102181

Browse files
authored
Merge pull request #721 from murrayrm/ioclass_25Mar2022
Updated system class functionality
2 parents 983726c + 135734a commit 2102181

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+2368
-1143
lines changed

.github/workflows/python-package-conda.yml

+6-1
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,20 @@ on: [push, pull_request]
44

55
jobs:
66
test-linux:
7-
name: Python ${{ matrix.python-version }}${{ matrix.slycot && format(' with Slycot from {0}', matrix.slycot) || ' without Slycot' }}${{ matrix.array-and-matrix == 1 && ', array and matrix' || '' }}
7+
name: Python ${{ matrix.python-version }}${{ matrix.slycot && format(' with Slycot from {0}', matrix.slycot) || ' without Slycot' }}${{ matrix.pandas && ', with pandas' || '' }}${{ matrix.array-and-matrix == 1 && ', array and matrix' || '' }}
88
runs-on: ubuntu-latest
99

1010
strategy:
1111
max-parallel: 5
1212
matrix:
1313
python-version: [3.7, 3.9]
1414
slycot: ["", "conda"]
15+
pandas: [""]
1516
array-and-matrix: [0]
1617
include:
1718
- python-version: 3.9
1819
slycot: conda
20+
pandas: conda
1921
array-and-matrix: 1
2022

2123
steps:
@@ -41,6 +43,9 @@ jobs:
4143
if [[ '${{matrix.slycot}}' == 'conda' ]]; then
4244
conda install -c conda-forge slycot
4345
fi
46+
if [[ '${{matrix.pandas}}' == 'conda' ]]; then
47+
conda install -c conda-forge pandas
48+
fi
4449
4550
- name: Test with pytest
4651
env:

control/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
from .margins import *
5656
from .mateqn import *
5757
from .modelsimp import *
58+
from .namedio import *
5859
from .nichols import *
5960
from .phaseplot import *
6061
from .pzmap import *

control/canonical.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# RMM, 10 Nov 2012
33

44
from .exception import ControlNotImplemented, ControlSlycot
5-
from .lti import issiso
5+
from .namedio import issiso
66
from .statesp import StateSpace, _convert_to_statespace
77
from .statefbk import ctrb, obsv
88

control/dtime.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
4848
"""
4949

50-
from .lti import isctime
50+
from .namedio import isctime
5151
from .statesp import StateSpace
5252

5353
__all__ = ['sample_system', 'c2d']

control/exception.py

+13
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,16 @@ def slycot_check():
7171
except:
7272
slycot_installed = False
7373
return slycot_installed
74+
75+
76+
# Utility function to see if pandas is installed
77+
pandas_installed = None
78+
def pandas_check():
79+
global pandas_installed
80+
if pandas_installed is None:
81+
try:
82+
import pandas
83+
pandas_installed = True
84+
except:
85+
pandas_installed = False
86+
return pandas_installed

control/flatsys/flatsys.py

+5
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,11 @@ def __init__(self,
156156

157157
# Save the length of the flat flag
158158

159+
def __str__(self):
160+
return f"{NonlinearIOSystem.__str__(self)}\n\n" \
161+
+ f"Forward: {self.forward}\n" \
162+
+ f"Reverse: {self.reverse}"
163+
159164
def forward(self, x, u, params={}):
160165

161166
"""Compute the flat flag given the states and input.

control/flatsys/linflat.py

+10
Original file line numberDiff line numberDiff line change
@@ -140,3 +140,13 @@ def reverse(self, zflag, params):
140140
x = self.Tinv @ z
141141
u = zflag[0][-1] - self.F @ z
142142
return np.reshape(x, self.nstates), np.reshape(u, self.ninputs)
143+
144+
# Update function
145+
def _rhs(self, t, x, u, params={}):
146+
# Use LinearIOSystem._rhs instead of default (MRO) NonlinearIOSystem
147+
return LinearIOSystem._rhs(self, t, x, u)
148+
149+
# output function
150+
def _out(self, t, x, u, params={}):
151+
# Use LinearIOSystem._out instead of default (MRO) NonlinearIOSystem
152+
return LinearIOSystem._out(self, t, x, u)

control/flatsys/systraj.py

+73
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
# SUCH DAMAGE.
3838

3939
import numpy as np
40+
from ..timeresp import TimeResponseData
4041

4142
class SystemTrajectory:
4243
"""Class representing a system trajectory.
@@ -117,3 +118,75 @@ def eval(self, tlist):
117118
self.system.reverse(zflag, self.params)
118119

119120
return xd, ud
121+
122+
# Return the system trajectory as a TimeResponseData object
123+
def response(self, tlist, transpose=False, return_x=False, squeeze=None):
124+
"""Return the trajectory of a system as a TimeResponseData object
125+
126+
Evaluate the trajectory at a list of time points, returning the state
127+
and input vectors for the trajectory:
128+
129+
response = traj.response(tlist)
130+
time, yd, ud = response.time, response.outputs, response.inputs
131+
132+
Parameters
133+
----------
134+
tlist : 1D array
135+
List of times to evaluate the trajectory.
136+
137+
transpose : bool, optional
138+
If True, transpose all input and output arrays (for backward
139+
compatibility with MATLAB and :func:`scipy.signal.lsim`).
140+
Default value is False.
141+
142+
return_x : bool, optional
143+
If True, return the state vector when assigning to a tuple
144+
(default = False). See :func:`forced_response` for more details.
145+
146+
squeeze : bool, optional
147+
By default, if a system is single-input, single-output (SISO) then
148+
the output response is returned as a 1D array (indexed by time).
149+
If squeeze=True, remove single-dimensional entries from the shape
150+
of the output even if the system is not SISO. If squeeze=False,
151+
keep the output as a 3D array (indexed by the output, input, and
152+
time) even if the system is SISO. The default value can be set
153+
using config.defaults['control.squeeze_time_response'].
154+
155+
Returns
156+
-------
157+
results : TimeResponseData
158+
Time response represented as a :class:`TimeResponseData` object
159+
containing the following properties:
160+
161+
* time (array): Time values of the output.
162+
163+
* outputs (array): Response of the system. If the system is SISO
164+
and squeeze is not True, the array is 1D (indexed by time). If
165+
the system is not SISO or ``squeeze`` is False, the array is 3D
166+
(indexed by the output, trace, and time).
167+
168+
* states (array): Time evolution of the state vector, represented
169+
as either a 2D array indexed by state and time (if SISO) or a 3D
170+
array indexed by state, trace, and time. Not affected by
171+
``squeeze``.
172+
173+
* inputs (array): Input(s) to the system, indexed in the same
174+
manner as ``outputs``.
175+
176+
The return value of the system can also be accessed by assigning
177+
the function to a tuple of length 2 (time, output) or of length 3
178+
(time, output, state) if ``return_x`` is ``True``.
179+
180+
"""
181+
# Compute the state and input response using the eval function
182+
sys = self.system
183+
xout, uout = self.eval(tlist)
184+
yout = np.array([
185+
sys.output(tlist[i], xout[:, i], uout[:, i])
186+
for i in range(len(tlist))]).transpose()
187+
188+
return TimeResponseData(
189+
tlist, yout, xout, uout, issiso=sys.issiso(),
190+
input_labels=sys.input_labels, output_labels=sys.output_labels,
191+
state_labels=sys.state_labels,
192+
transpose=transpose, return_x=return_x, squeeze=squeeze)

0 commit comments

Comments
 (0)