Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
1a6d806
eliminate _evalfr in lti system classes, replaced with __call__, whic…
sawyerbfuller Aug 14, 2020
b05492c
fixed remaining failing unit tests and cleanup of docfiles
sawyerbfuller Aug 16, 2020
2c4ac62
minor fix following suggested change in frd str method
sawyerbfuller Aug 16, 2020
6213a54
fixed a few bugs that slipped through
sawyerbfuller Aug 16, 2020
8486b7a
Update control/frdata.py
sawyerbfuller Aug 17, 2020
c8bf92f
Update control/frdata.py
sawyerbfuller Aug 17, 2020
371986c
Update control/lti.py
sawyerbfuller Aug 17, 2020
d8fbaa0
Update control/statesp.py
sawyerbfuller Aug 17, 2020
17bc2a8
Apply suggestions from code review
sawyerbfuller Aug 17, 2020
4ad33db
suggested changes in docstrings
sawyerbfuller Aug 17, 2020
94322c8
Merge remote-tracking branch 'origin/call-method' into call-method
sawyerbfuller Aug 17, 2020
60433e0
better docstrings conforming to numpydoc
sawyerbfuller Aug 17, 2020
262fde6
converted statesp.horner to take care of trying to use slycot_horner …
sawyerbfuller Aug 17, 2020
4c3ed0b
renamed slycot_horner to slycot_laub
sawyerbfuller Aug 17, 2020
b37c16b
docs more consistent
sawyerbfuller Aug 17, 2020
4e4b97f
forgot to change a variable name everywhere in a function
sawyerbfuller Aug 17, 2020
5626a3a
revert doc/control.rst
bnavigator Aug 18, 2020
5caffa4
Update control/xferfcn.py
sawyerbfuller Aug 19, 2020
c888b6e
numpydoc convention updates
sawyerbfuller Aug 19, 2020
b64145d
small update to facilitate merge maybe
sawyerbfuller Aug 19, 2020
3cf80af
small numpydoc change in trasnferfunction to resolve merge
sawyerbfuller Aug 19, 2020
330e517
unit tests now test for deprecation of sys.freqresp
sawyerbfuller Aug 19, 2020
8c9571d
Merge branch 'master' into call-method
sawyerbfuller Aug 19, 2020
195bec3
rebase sawyerbfuller-call-method against master
murrayrm Jan 6, 2021
6b82c3c
Merge commit '195bec3' into call-method-mergeinto
bnavigator Jan 6, 2021
bddc792
fix merge strategy 'errors'
bnavigator Jan 6, 2021
e9bff6a
restore test_phase_crossover_frequencies_mimo
bnavigator Jan 6, 2021
73fc6a6
reinstate second check in frd_test test_eval()
bnavigator Jan 6, 2021
7acfc77
statesp_test: rename from test_evalfr to test_call
bnavigator Jan 6, 2021
a26520d
statesp_test: remove duplicate test_pole_static and test_copy_constru…
bnavigator Jan 6, 2021
dae8d42
Update control/xferfcn.py
sawyerbfuller Jan 6, 2021
3b10af0
check for warning the pytest way
bnavigator Jan 6, 2021
1bf445b
add unit tests for freqresp/evalfr warnings/errors
murrayrm Jan 7, 2021
3dbaacd
test frd freqresp deprecation
bnavigator Jan 7, 2021
178bfa0
doc updates to specify frequency response outputs more precisely
sawyerbfuller Jan 7, 2021
a631098
missed a couple of doc updates in xferfcn
sawyerbfuller Jan 7, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
docs more consistent
  • Loading branch information
sawyerbfuller committed Aug 17, 2020
commit b37c16b6d53e9ec0f7d63b2645cb3ef0a75b8ae7
31 changes: 18 additions & 13 deletions control/frdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,21 +344,27 @@ def __pow__(self, other):
# update Sawyer B. Fuller 2020.08.14: __call__ added to provide a uniform
# interface to systems in general and the lti.frequency_response method
def eval(self, omega, squeeze=True):
"""Evaluate a transfer function at a single angular frequency.

self.evalfr(omega) returns the value of the frequency response
at frequency omega.
"""Evaluate a transfer function at angular frequency omega.

Note that a "normal" FRD only returns values for which there is an
entry in the omega vector. An interpolating FRD can return
intermediate values.

Parameters
----------
omega: float or array_like
Frequencies in radians per second
squeeze: bool, optional (default=True)
If True and sys is single input, single output (SISO), return a
1D array or scalar the same length as omega.
"""
If True and sys is single input single output (SISO), returns a
1D array or scalar depending on the length of omega

Returns
-------
fresp : (num_outputs, num_inputs, len(x)) or len(x) complex ndarray
The frequency response of the system. Array is len(x) if and only if
system is SISO and squeeze=True.

"""
omega_array = np.array(omega, ndmin=1) # array-like version of omega
if any(omega_array.imag > 0):
raise ValueError("FRD.eval can only accept real-valued omega")
Expand Down Expand Up @@ -389,16 +395,15 @@ def eval(self, omega, squeeze=True):
def __call__(self, s, squeeze=True):
Comment thread
sawyerbfuller marked this conversation as resolved.
"""Evaluate system's transfer function at complex frequencies.

Returns the complex frequency response `sys(x)` where `x` is `s` for
continuous-time systems and `x` is `z` for discrete-time systems.
Returns the complex frequency response `sys(s)`.

To evaluate at a frequency omega in radians per second, enter
x = omega*j.
x = omega*1j or use FRD.eval(omega)

Parameters
----------
Comment thread
sawyerbfuller marked this conversation as resolved.
x: complex scalar or array_like
Complex frequency(s)
s: complex scalar or array_like
Complex frequencies
squeeze: bool, optional (default=True)
If True and sys is single input single output (SISO), returns a
1D array or scalar depending on the length of x.
Expand Down
26 changes: 13 additions & 13 deletions control/lti.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,10 @@ def frequency_response(self, omega, squeeze=True):

Returns
-------
mag : (self.outputs, self.inputs, len(omega)) ndarray
mag : (self.outputs, self.inputs, len(omega)) or len(omega) ndarray
Comment thread
sawyerbfuller marked this conversation as resolved.
Outdated
The magnitude (absolute value, not dB or log10) of the system
frequency response.
phase : (self.outputs, self.inputs, len(omega)) ndarray
phase : (self.outputs, self.inputs, len(omega)) or len(omega) ndarray
Comment thread
sawyerbfuller marked this conversation as resolved.
Outdated
The wrapped phase in radians of the system frequency response.
omega : ndarray
The (sorted) frequencies at which the response was evaluated.
Expand Down Expand Up @@ -430,10 +430,10 @@ def evalfr(sys, x, squeeze=True):
Evaluate the transfer function of an LTI system for complex frequency x.

Returns the complex frequency response `sys(x)` where `x` is `s` for
continuous-time systems and `x` is `z` for discrete-time systems.
continuous-time systems and `z` for discrete-time systems.

To evaluate at a frequency omega in radians per second, enter x = omega*j,
for continuous-time systems, or x = exp(j*omega*dt) for discrete-time
To evaluate at a frequency omega in radians per second, enter x = omega*1j,
for continuous-time systems, or x = exp(1j*omega*dt) for discrete-time
systems.

Parameters
Expand All @@ -448,9 +448,9 @@ def evalfr(sys, x, squeeze=True):

Returns
-------
fresp : (num_outputs, num_inputs, len(x)) or len(x) complex ndarray
The frequency response of the system. If system is SISO and squeezeThe size of the array depends on
whether system is SISO and squeeze keyword.
fresp : (sys.outputs, sys.inputs, len(x)) or len(x) complex ndarray
Comment thread
bnavigator marked this conversation as resolved.
Outdated
The frequency response of the system. Array is len(x) if and only if
Comment thread
sawyerbfuller marked this conversation as resolved.
Outdated
system is SISO and squeeze=True.

See Also
--------
Expand Down Expand Up @@ -481,22 +481,22 @@ def freqresp(sys, omega, squeeze=True):
----------
sys: StateSpace or TransferFunction
Linear system
omega: array_like
omega: float or array_like
Comment thread
sawyerbfuller marked this conversation as resolved.
Outdated
A list of frequencies in radians/sec at which the system should be
evaluated. The list can be either a python list or a numpy array
and will be sorted before evaluation.
squeeze: bool, optional (default=True)
If True and sys is single input, single output (SISO), return a
If True and sys is single input, single output (SISO), returns
1D array or scalar depending on omega's length.

Returns
-------
mag : (self.outputs, self.inputs, len(omega)) ndarray
mag : (sys.outputs, sys.inputs, len(omega)) or len(omega) ndarray
Comment thread
bnavigator marked this conversation as resolved.
Outdated
The magnitude (absolute value, not dB or log10) of the system
frequency response.
Comment thread
sawyerbfuller marked this conversation as resolved.
Outdated
phase : (self.outputs, self.inputs, len(omega)) ndarray
phase : (sys.outputs, sys.inputs, len(omega)) or len(omega) ndarray
Comment thread
sawyerbfuller marked this conversation as resolved.
Outdated
Comment thread
sawyerbfuller marked this conversation as resolved.
Outdated
The wrapped phase in radians of the system frequency response.
omega : ndarray or list or tuple
omega : ndarray
The list of sorted frequencies at which the response was
evaluated.

Expand Down
40 changes: 19 additions & 21 deletions control/statesp.py
Original file line number Diff line number Diff line change
Expand Up @@ -438,26 +438,26 @@ def __rdiv__(self, other):
raise NotImplementedError("StateSpace.__rdiv__ is not implemented yet.")

def __call__(self, x, squeeze=True):
"""Evaluate system's transfer function at complex frequencies.
"""Evaluate system's transfer function at complex frequency.

Returns the complex frequency response `sys(x)` where `x` is `s` for
continuous-time systems and `x` is `z` for discrete-time systems.
continuous-time systems and `z` for discrete-time systems.

To evaluate at a frequency omega in radians per second, enter
x = omega*j, for continuous-time systems, or x = exp(j*omega*dt) for
x = omega*1j, for continuous-time systems, or x = exp(1j*omega*dt) for
discrete-time systems.

Parameters
----------
x: complex scalar or array_like
Complex frequency(s)
x: complex or complex array_like
Complex frequencies
squeeze: bool, optional (default=True)
If True and sys is single input single output (SISO), returns a
1D array or scalar depending on the length of x.

Returns
-------
fresp : (num_outputs, num_inputs, len(x)) or len(x) complex ndarray
fresp : (self.outputs, self.inputs, len(x)) or len(x) complex ndarray
Comment thread
sawyerbfuller marked this conversation as resolved.
Outdated
Comment thread
sawyerbfuller marked this conversation as resolved.
Outdated
The frequency response of the system. Array is len(x) if and only if
system is SISO and squeeze=True.

Expand All @@ -472,36 +472,34 @@ def __call__(self, x, squeeze=True):
else:
return out

def slycot_laub(self, s):
"""Evaluate system's transfer function at complex frequencies s
def slycot_laub(self, x):
"""Evaluate system's transfer function at complex frequency
using Laub's method from Slycot.

Expects inputs and outputs to be formatted correctly. Use __call__
for a more user-friendly interface.

Parameters
----------
s : array_like
x : complex array_like or complex
Complex frequency

Returns
-------
output : (outputs, inputs, len(s)) complex ndarray
output : (number_outputs, number_inputs, len(x)) complex ndarray
Frequency response
"""
from slycot import tb05ad

# preallocate
s_arr = np.array(s, ndmin=1) # array-like version of s
out = np.empty((self.outputs, self.inputs, len(s_arr)),
dtype=complex)
x_arr = np.array(x, ndmin=1) # array-like version of s
n = self.states
m = self.inputs
p = self.outputs
out = np.empty((p, m, len(x_arr)), dtype=complex)
# The first call both evaluates C(sI-A)^-1 B and also returns
# Hessenberg transformed matrices at, bt, ct.
result = tb05ad(n, m, p, s_arr[0], self.A,
self.B, self.C, job='NG')
result = tb05ad(n, m, p, x_arr[0], self.A, self.B, self.C, job='NG')
# When job='NG', result = (at, bt, ct, g_i, hinvb, info)
at = result[0]
bt = result[1]
Expand All @@ -514,8 +512,8 @@ def slycot_laub(self, s):
# transformed state matrices, at, bt, ct.

# Start at the second frequency, already have the first.
for kk, s_kk in enumerate(s_arr[1:len(s_arr)]):
result = tb05ad(n, m, p, s_kk, at, bt, ct, job='NH')
for kk, x_kk in enumerate(x_arr[1:len(x_arr)]):
result = tb05ad(n, m, p, x_kk, at, bt, ct, job='NH')
# When job='NH', result = (g_i, hinvb, info)

# kk+1 because enumerate starts at kk = 0.
Expand All @@ -524,23 +522,23 @@ def slycot_laub(self, s):
return out

def horner(self, x):
"""Evaluate system's transfer function at complex frequencies x
"""Evaluate system's transfer function at complex frequency
using Laub's or Horner's method.

Evaluates sys(x) where `x` is `s` for continuous-time systems and `z`
Evaluates `sys(x)` where `x` is `s` for continuous-time systems and `z`
for discrete-time systems.

Expects inputs and outputs to be formatted correctly. Use __call__
for a more user-friendly interface.

Parameters
----------
x : array_like
x : complex array_like or complex
Complex frequencies

Returns
-------
output : (outputs, inputs, len(s)) complex ndarray
output : (number_outputs, number_inputs, len(x)) complex ndarray
Frequency response

Notes
Expand Down
24 changes: 14 additions & 10 deletions control/xferfcn.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,23 +208,23 @@ def __call__(self, x, squeeze=True):
"""Evaluate system's transfer function at complex frequencies.

Returns the complex frequency response `sys(x)` where `x` is `s` for
continuous-time systems and `x` is `z` for discrete-time systems.
continuous-time systems and `z` for discrete-time systems.

To evaluate at a frequency omega in radians per second, enter
x = omega*j, for continuous-time systems, or x = exp(j*omega*dt) for
x = omega*1j, for continuous-time systems, or x = exp(1j*omega*dt) for
discrete-time systems.

Parameters
----------
x: complex scalar or array_like
Complex frequency(s)
x: complex array_like or complex
Comment thread
sawyerbfuller marked this conversation as resolved.
Outdated
Complex frequencies
squeeze: bool, optional (default=True)
Comment thread
sawyerbfuller marked this conversation as resolved.
Outdated
If True and sys is single input single output (SISO), returns a
1D array or scalar depending on the length of x.

Returns
-------
fresp : (num_outputs, num_inputs, len(x)) or len(x) complex ndarray
fresp : (self.outputs, self.inputs, len(x)) or len(x) complex ndarray
Comment thread
sawyerbfuller marked this conversation as resolved.
Outdated
The frequency response of the system. Array is len(x) if and only if
system is SISO and squeeze=True.

Expand All @@ -239,22 +239,26 @@ def __call__(self, x, squeeze=True):
else:
return out

def horner(self, s):
"""Evaluates system's transfer function at complex frequencies s
using Horner's method.
def horner(self, x):
"""Evaluate system's transfer function at complex frequency
using Horner's method.

Evaluates `sys(x)` where `x` is `s` for continuous-time systems and `z`
for discrete-time systems.

Expects inputs and outputs to be formatted correctly. Use __call__
for a more user-friendly interface.

Parameters
----------
s : array_like or scalar
x : complex array_like or complex
Comment thread
sawyerbfuller marked this conversation as resolved.
Outdated
Complex frequencies

Returns
-------
output : (outputs, inputs, len(s)) complex ndarray
output : (self.outputs, self.inputs, len(x)) complex ndarray
Frequency response

"""
s_arr = np.array(s, ndmin=1) # force to be an array
out = empty((self.outputs, self.inputs, len(s_arr)), dtype=complex)
Expand Down