From fb6545939e78ae7d9ae9ad358619a93c5ad27870 Mon Sep 17 00:00:00 2001 From: Richard Murray Date: Fri, 11 Jun 2021 21:36:15 -0700 Subject: [PATCH 1/7] DOC: fix forced_response return arguments in documentation --- doc/conventions.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/conventions.rst b/doc/conventions.rst index adcdbe96f..63f3fac2c 100644 --- a/doc/conventions.rst +++ b/doc/conventions.rst @@ -168,7 +168,7 @@ As all simulation functions return *arrays*, plotting is convenient:: The output of a MIMO system can be plotted like this:: - t, y, x = forced_response(sys, u, t) + t, y = forced_response(sys, u, t) plot(t, y[0], label='y_0') plot(t, y[1], label='y_1') From d89dfd863dafc901e60254da75ef353f789905d5 Mon Sep 17 00:00:00 2001 From: Richard Murray Date: Fri, 11 Jun 2021 21:53:40 -0700 Subject: [PATCH 2/7] DOC: update iosys docstrings - params not optional for updfcn, outfcn --- control/iosys.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/control/iosys.py b/control/iosys.py index 526da4cdb..e31bc95e5 100644 --- a/control/iosys.py +++ b/control/iosys.py @@ -790,17 +790,17 @@ def __init__(self, updfcn, outfcn=None, inputs=None, outputs=None, updfcn : callable Function returning the state update function - `updfcn(t, x, u[, param]) -> array` + `updfcn(t, x, u, params) -> array` where `x` is a 1-D array with shape (nstates,), `u` is a 1-D array with shape (ninputs,), `t` is a float representing the currrent - time, and `param` is an optional dict containing the values of - parameters used by the function. + time, and `params` is a dict containing the values of parameters + used by the function. outfcn : callable Function returning the output at the given state - `outfcn(t, x, u[, param]) -> array` + `outfcn(t, x, u, params) -> array` where the arguments are the same as for `upfcn`. From 695cdfc45cee5ca8570ad9bf3410a836e353539a Mon Sep 17 00:00:00 2001 From: Richard Murray Date: Sat, 12 Jun 2021 13:04:07 -0700 Subject: [PATCH 3/7] DOC: fix LTI system attribute docstrings, add __call__, PEP8 cleanup --- control/frdata.py | 42 +++++++++++--- control/iosys.py | 59 ++++++++++++++++--- control/lti.py | 34 ++++++++--- control/statesp.py | 112 +++++++++++++++++++++++++++--------- control/tests/iosys_test.py | 4 +- control/xferfcn.py | 104 ++++++++++++++++++++++++++++----- doc/classes.rst | 1 + doc/conf.py | 11 ++-- doc/control.rst | 5 +- doc/flatsys.rst | 1 + doc/matlab.rst | 1 + doc/optimal.rst | 1 + 12 files changed, 303 insertions(+), 72 deletions(-) diff --git a/control/frdata.py b/control/frdata.py index c620984f6..625e84b75 100644 --- a/control/frdata.py +++ b/control/frdata.py @@ -77,13 +77,35 @@ class FrequencyResponseData(LTI): above, i.e. the rows represent the outputs and the columns represent the inputs. + A frequency response data object is callable and returns the value of the + transfer function evaluated at a point in the complex plane (must be on + the imaginary access). See :meth:`~control.FrequencyResponseData.__call__` + for a more detailed description. + """ # Allow NDarray * StateSpace to give StateSpace._rmul_() priority # https://docs.scipy.org/doc/numpy/reference/arrays.classes.html __array_priority__ = 11 # override ndarray and matrix types - epsw = 1e-8 + # + # Class attributes + # + # These attributes are defined as class attributes so that they are + # documented properly. They are "overwritten" in __init__. + # + + #: Number of system inputs. + #: + #: :meta hide-value: + ninputs = 1 + + #: Number of system outputs. + #: + #: :meta hide-value: + noutputs = 1 + + _epsw = 1e-8 #: Bound for exact frequency match def __init__(self, *args, **kwargs): """Construct an FRD object. @@ -141,7 +163,8 @@ def __init__(self, *args, **kwargs): self.omega = args[0].omega self.fresp = args[0].fresp else: - raise ValueError("Needs 1 or 2 arguments; received %i." % len(args)) + raise ValueError( + "Needs 1 or 2 arguments; received %i." % len(args)) # create interpolation functions if smooth: @@ -378,7 +401,7 @@ def eval(self, omega, squeeze=None): then single-dimensional axes are removed. """ - omega_array = np.array(omega, ndmin=1) # array-like version of omega + omega_array = np.array(omega, ndmin=1) # array-like version of omega # Make sure that we are operating on a simple list if len(omega_array.shape) > 1: @@ -389,7 +412,7 @@ def eval(self, omega, squeeze=None): raise ValueError("FRD.eval can only accept real-valued omega") if self.ifunc is None: - elements = np.isin(self.omega, omega) # binary array + elements = np.isin(self.omega, omega) # binary array if sum(elements) < len(omega_array): raise ValueError( "not all frequencies omega are in frequency list of FRD " @@ -398,7 +421,7 @@ def eval(self, omega, squeeze=None): out = self.fresp[:, :, elements] else: out = empty((self.noutputs, self.ninputs, len(omega_array)), - dtype=complex) + dtype=complex) for i in range(self.noutputs): for j in range(self.ninputs): for k, w in enumerate(omega_array): @@ -417,6 +440,9 @@ def __call__(self, s, squeeze=None): To evaluate at a frequency omega in radians per second, enter ``s = omega * 1j`` or use ``sys.eval(omega)`` + For a frequency response data object, the argument must be an + imaginary number (since only the frequency response is defined). + Parameters ---------- s : complex scalar or 1D array_like @@ -444,6 +470,7 @@ def __call__(self, s, squeeze=None): If `s` is not purely imaginary, because :class:`FrequencyDomainData` systems are only defined at imaginary frequency values. + """ # Make sure that we are operating on a simple list if len(np.atleast_1d(s).shape) > 1: @@ -451,7 +478,7 @@ def __call__(self, s, squeeze=None): if any(abs(np.atleast_1d(s).real) > 0): raise ValueError("__call__: FRD systems can only accept " - "purely imaginary frequencies") + "purely imaginary frequencies") # need to preserve array or scalar status if hasattr(s, '__len__'): @@ -510,6 +537,7 @@ def feedback(self, other=1, sign=-1): # fixes this problem. # + FRD = FrequencyResponseData @@ -534,7 +562,7 @@ def _convert_to_FRD(sys, omega, inputs=1, outputs=1): if isinstance(sys, FRD): omega.sort() if len(omega) == len(sys.omega) and \ - (abs(omega - sys.omega) < FRD.epsw).all(): + (abs(omega - sys.omega) < FRD._epsw).all(): # frequencies match, and system was already frd; simply use return sys diff --git a/control/iosys.py b/control/iosys.py index e31bc95e5..7dfd8756c 100644 --- a/control/iosys.py +++ b/control/iosys.py @@ -95,11 +95,10 @@ class for a set of subclasses that are used to implement specific Dictionary of signal names for the inputs, outputs and states and the index of the corresponding array dt : None, True or float - System timebase. 0 (default) indicates continuous - time, True indicates discrete time with unspecified sampling - time, positive number is discrete time with specified - sampling time, None indicates unspecified timebase (either - continuous or discrete time). + System timebase. 0 (default) indicates continuous time, True indicates + discrete time with unspecified sampling time, positive number is + discrete time with specified sampling time, None indicates unspecified + timebase (either continuous or discrete time). params : dict, optional Parameter values for the systems. Passed to the evaluation functions for the system as default values, overriding internal defaults. @@ -120,12 +119,12 @@ class for a set of subclasses that are used to implement specific """ - idCounter = 0 + _idCounter = 0 def name_or_default(self, name=None): if name is None: - name = "sys[{}]".format(InputOutputSystem.idCounter) - InputOutputSystem.idCounter += 1 + name = "sys[{}]".format(InputOutputSystem._idCounter) + InputOutputSystem._idCounter += 1 return name def __init__(self, inputs=None, outputs=None, states=None, params={}, @@ -187,6 +186,28 @@ def __init__(self, inputs=None, outputs=None, states=None, params={}, self.set_outputs(outputs) self.set_states(states) + # + # Class attributes + # + # These attributes are defined as class attributes so that they are + # documented properly. They are "overwritten" in __init__. + # + + #: Number of system inputs. + #: + #: :meta hide-value: + ninputs = 0 + + #: Number of system outputs. + #: + #: :meta hide-value: + noutputs = 0 + + #: Number of system states. + #: + #: :meta hide-value: + nstates = 0 + def __repr__(self): return self.name if self.name is not None else str(type(self)) @@ -751,6 +772,17 @@ def __init__(self, linsys, inputs=None, outputs=None, states=None, if nstates is not None and linsys.nstates != nstates: raise ValueError("Wrong number/type of states given.") + # The following text needs to be replicated from StateSpace in order for + # this entry to show up properly in sphinx doccumentation (not sure why, + # but it was the only way to get it to work). + # + #: Deprecated attribute; use :attr:`nstates` instead. + #: + #: The ``state`` attribute was used to store the number of states for : a + #: state space system. It is no longer used. If you need to access the + #: number of states, use :attr:`nstates`. + states = property(StateSpace._get_states, StateSpace._set_states) + def _update_params(self, params={}, warning=True): # Parameters not supported; issue a warning if params and warning: @@ -1473,6 +1505,17 @@ def __init__(self, io_sys, ss_sys=None): else: raise TypeError("Second argument must be a state space system.") + # The following text needs to be replicated from StateSpace in order for + # this entry to show up properly in sphinx doccumentation (not sure why, + # but it was the only way to get it to work). + # + #: Deprecated attribute; use :attr:`nstates` instead. + #: + #: The ``state`` attribute was used to store the number of states for : a + #: state space system. It is no longer used. If you need to access the + #: number of states, use :attr:`nstates`. + states = property(StateSpace._get_states, StateSpace._set_states) + def input_output_response( sys, T, U=0., X0=0, params={}, diff --git a/control/lti.py b/control/lti.py index 52f6b2e72..b9adf644f 100644 --- a/control/lti.py +++ b/control/lti.py @@ -59,34 +59,52 @@ def __init__(self, inputs=1, outputs=1, dt=None): # future warning, so that users will see it. # - @property - def inputs(self): + def _get_inputs(self): warn("The LTI `inputs` attribute will be deprecated in a future " "release. Use `ninputs` instead.", DeprecationWarning, stacklevel=2) return self.ninputs - @inputs.setter - def inputs(self, value): + def _set_inputs(self, value): warn("The LTI `inputs` attribute will be deprecated in a future " "release. Use `ninputs` instead.", DeprecationWarning, stacklevel=2) self.ninputs = value - @property - def outputs(self): + #: Deprecated + inputs = property( + _get_inputs, _set_inputs, doc= + """ + Deprecated attribute; use :attr:`ninputs` instead. + + The ``input`` attribute was used to store the number of system inputs. + It is no longer used. If you need access to the number of inputs for + an LTI system, use :attr:`ninputs`. + """) + + def _get_outputs(self): warn("The LTI `outputs` attribute will be deprecated in a future " "release. Use `noutputs` instead.", DeprecationWarning, stacklevel=2) return self.noutputs - @outputs.setter - def outputs(self, value): + def _set_outputs(self, value): warn("The LTI `outputs` attribute will be deprecated in a future " "release. Use `noutputs` instead.", DeprecationWarning, stacklevel=2) self.noutputs = value + #: Deprecated + outputs = property( + _get_outputs, _set_outputs, doc= + """ + Deprecated attribute; use :attr:`noutputs` instead. + + The ``output`` attribute was used to store the number of system + outputs. It is no longer used. If you need access to the number of + outputs for an LTI system, use :attr:`noutputs`. + """) + def isdtime(self, strict=False): """ Check to see if a system is a discrete-time system diff --git a/control/statesp.py b/control/statesp.py index 92834b3e4..9e009fa85 100644 --- a/control/statesp.py +++ b/control/statesp.py @@ -166,7 +166,7 @@ class StateSpace(LTI): linear time-invariant (LTI) systems: dx/dt = A x + B u - y = C x + D u + y = C x + D u where u is the input, y is the output, and x is the state. @@ -195,6 +195,10 @@ class StateSpace(LTI): The default value of dt can be changed by changing the value of ``control.config.defaults['control.default_dt']``. + A state space system is callable and returns the value of the transfer + function evaluated at a point in the complex plane. See + :meth:`~control.StateSpace.__call__` for a more detailed description. + StateSpace instances have support for IPython LaTeX output, intended for pretty-printing in Jupyter notebooks. The LaTeX output can be configured using @@ -212,6 +216,7 @@ class StateSpace(LTI): `'partitioned'` or `'separate'`. If `'partitioned'`, the A, B, C, D matrices are shown as a single, partitioned matrix; if `'separate'`, the matrices are shown separately. + """ # Allow ndarray * StateSpace to give StateSpace._rmul_() priority @@ -296,7 +301,8 @@ def __init__(self, *args, **kwargs): elif len(args) == 5: dt = args[4] if 'dt' in kwargs: - warn('received multiple dt arguments, using positional arg dt=%s'%dt) + warn("received multiple dt arguments, " + "using positional arg dt = %s" % dt) elif len(args) == 1: try: dt = args[0].dt @@ -331,6 +337,48 @@ def __init__(self, *args, **kwargs): if remove_useless_states: self._remove_useless_states() + # + # Class attributes + # + # These attributes are defined as class attributes so that they are + # documented properly. They are "overwritten" in __init__. + # + + #: Number of system inputs. + #: + #: :meta hide-value: + ninputs = 0 + + #: Number of system outputs. + #: + #: :meta hide-value: + noutputs = 0 + + #: Number of system states. + #: + #: :meta hide-value: + nstates = 0 + + #: Dynamics matrix. + #: + #: :meta hide-value: + A = [] + + #: Input matrix. + #: + #: :meta hide-value: + B = [] + + #: Output matrix. + #: + #: :meta hide-value: + C = [] + + #: Direct term. + #: + #: :meta hide-value: + D = [] + # # Getter and setter functions for legacy state attributes # @@ -339,20 +387,25 @@ def __init__(self, *args, **kwargs): # future warning, so that users will see it. # - @property - def states(self): + def _get_states(self): warn("The StateSpace `states` attribute will be deprecated in a " "future release. Use `nstates` instead.", DeprecationWarning, stacklevel=2) return self.nstates - @states.setter - def states(self, value): + def _set_states(self, value): warn("The StateSpace `states` attribute will be deprecated in a " "future release. Use `nstates` instead.", DeprecationWarning, stacklevel=2) self.nstates = value + #: Deprecated attribute; use :attr:`nstates` instead. + #: + #: The ``state`` attribute was used to store the number of states for : a + #: state space system. It is no longer used. If you need to access the + #: number of states, use :attr:`nstates`. + states = property(_get_states, _set_states) + def _remove_useless_states(self): """Check for states that don't do anything, and remove them. @@ -626,8 +679,10 @@ def __mul__(self, other): # Check to make sure the dimensions are OK if self.ninputs != other.noutputs: - raise ValueError("C = A * B: A has %i column(s) (input(s)), \ - but B has %i row(s)\n(output(s))." % (self.ninputs, other.noutputs)) + raise ValueError( + "C = A * B: A has %i column(s) (input(s)), " + "but B has %i row(s)\n(output(s))." % + (self.ninputs, other.noutputs)) dt = common_timebase(self.dt, other.dt) # Concatenate the various arrays @@ -821,10 +876,10 @@ def horner(self, x, warn_infinite=True): out = empty((self.noutputs, self.ninputs, len(x_arr)), dtype=complex) - #TODO: can this be vectorized? + # TODO: can this be vectorized? for idx, x_idx in enumerate(x_arr): try: - out[:,:,idx] = np.dot( + out[:, :, idx] = np.dot( self.C, solve(x_idx * eye(self.nstates) - self.A, self.B)) \ + self.D @@ -837,9 +892,9 @@ def horner(self, x, warn_infinite=True): # Evaluating at a pole. Return value depends if there # is a zero at the same point or not. if x_idx in self.zero(): - out[:,:,idx] = complex(np.nan, np.nan) + out[:, :, idx] = complex(np.nan, np.nan) else: - out[:,:,idx] = complex(np.inf, np.nan) + out[:, :, idx] = complex(np.inf, np.nan) return out @@ -914,7 +969,7 @@ def feedback(self, other=1, sign=-1): other = _convert_to_statespace(other) # Check to make sure the dimensions are OK - if (self.ninputs != other.noutputs) or (self.noutputs != other.ninputs): + if self.ninputs != other.noutputs or self.noutputs != other.ninputs: raise ValueError("State space systems don't have compatible " "inputs/outputs for feedback.") dt = common_timebase(self.dt, other.dt) @@ -1288,17 +1343,17 @@ def dynamics(self, t, x, u=None): ------- dx/dt or x[t+dt] : ndarray """ - x = np.reshape(x, (-1, 1)) # force to a column in case matrix + x = np.reshape(x, (-1, 1)) # force to a column in case matrix if np.size(x) != self.nstates: raise ValueError("len(x) must be equal to number of states") if u is None: - return self.A.dot(x).reshape((-1,)) # return as row vector - else: # received t, x, and u, ignore t - u = np.reshape(u, (-1, 1)) # force to a column in case matrix + return self.A.dot(x).reshape((-1,)) # return as row vector + else: # received t, x, and u, ignore t + u = np.reshape(u, (-1, 1)) # force to column in case matrix if np.size(u) != self.ninputs: raise ValueError("len(u) must be equal to number of inputs") return self.A.dot(x).reshape((-1,)) \ - + self.B.dot(u).reshape((-1,)) # return as row vector + + self.B.dot(u).reshape((-1,)) # return as row vector def output(self, t, x, u=None): """Compute the output of the system @@ -1312,8 +1367,8 @@ def output(self, t, x, u=None): The first argument `t` is ignored because :class:`StateSpace` systems are time-invariant. It is included so that the dynamics can be passed - to most numerical integrators, such as scipy's `integrate.solve_ivp` and - for consistency with :class:`IOSystem` systems. + to most numerical integrators, such as scipy's `integrate.solve_ivp` + and for consistency with :class:`IOSystem` systems. The inputs `x` and `u` must be of the correct length for the system. @@ -1330,18 +1385,18 @@ def output(self, t, x, u=None): ------- y : ndarray """ - x = np.reshape(x, (-1, 1)) # force to a column in case matrix + x = np.reshape(x, (-1, 1)) # force to a column in case matrix if np.size(x) != self.nstates: raise ValueError("len(x) must be equal to number of states") if u is None: - return self.C.dot(x).reshape((-1,)) # return as row vector - else: # received t, x, and u, ignore t - u = np.reshape(u, (-1, 1)) # force to a column in case matrix + return self.C.dot(x).reshape((-1,)) # return as row vector + else: # received t, x, and u, ignore t + u = np.reshape(u, (-1, 1)) # force to a column in case matrix if np.size(u) != self.ninputs: raise ValueError("len(u) must be equal to number of inputs") return self.C.dot(x).reshape((-1,)) \ - + self.D.dot(u).reshape((-1,)) # return as row vector + + self.D.dot(u).reshape((-1,)) # return as row vector def _isstatic(self): """True if and only if the system has no dynamics, that is, @@ -1349,7 +1404,6 @@ def _isstatic(self): return not np.any(self.A) and not np.any(self.B) - # TODO: add discrete time check def _convert_to_statespace(sys, **kw): """Convert a system to state space form (if needed). @@ -1446,7 +1500,7 @@ def _convert_to_statespace(sys, **kw): try: D = _ssmatrix(sys) return StateSpace([], [], [], D) - except: + except Exception: raise TypeError("Can't convert given type to StateSpace system.") @@ -1679,6 +1733,7 @@ def _mimo2simo(sys, input, warn_conversion=False): return sys + def ss(*args, **kwargs): """ss(A, B, C, D[, dt]) @@ -1767,7 +1822,8 @@ def ss(*args, **kwargs): raise TypeError("ss(sys): sys must be a StateSpace or " "TransferFunction object. It is %s." % type(sys)) else: - raise ValueError("Needs 1, 4, or 5 arguments; received %i." % len(args)) + raise ValueError( + "Needs 1, 4, or 5 arguments; received %i." % len(args)) def tf2ss(*args): diff --git a/control/tests/iosys_test.py b/control/tests/iosys_test.py index c1c4d8006..8acd83632 100644 --- a/control/tests/iosys_test.py +++ b/control/tests/iosys_test.py @@ -963,7 +963,7 @@ def test_sys_naming_convention(self, tsys): ct.config.use_legacy_defaults('0.8.4') # changed delims in 0.9.0 ct.config.use_numpy_matrix(False) # np.matrix deprecated - ct.InputOutputSystem.idCounter = 0 + ct.InputOutputSystem._idCounter = 0 sys = ct.LinearIOSystem(tsys.mimo_linsys1) assert sys.name == "sys[0]" @@ -1027,7 +1027,7 @@ def test_signals_naming_convention_0_8_4(self, tsys): ct.config.use_legacy_defaults('0.8.4') # changed delims in 0.9.0 ct.config.use_numpy_matrix(False) # np.matrix deprecated - ct.InputOutputSystem.idCounter = 0 + ct.InputOutputSystem._idCounter = 0 sys = ct.LinearIOSystem(tsys.mimo_linsys1) for statename in ["x[0]", "x[1]"]: assert statename in sys.state_index diff --git a/control/xferfcn.py b/control/xferfcn.py index 99603b253..117edd120 100644 --- a/control/xferfcn.py +++ b/control/xferfcn.py @@ -72,6 +72,7 @@ # Define module default parameter values _xferfcn_defaults = {} + class TransferFunction(LTI): """TransferFunction(num, den[, dt]) @@ -105,6 +106,10 @@ class TransferFunction(LTI): The default value of dt can be changed by changing the value of ``control.config.defaults['control.default_dt']``. + A transfer function is callable and returns the value of the transfer + function evaluated at a point in the complex plane. See + :meth:`~control.TransferFunction.__call__` for a more detailed description. + The TransferFunction class defines two constants ``s`` and ``z`` that represent the differentiation and delay operators in continuous and discrete time. These can be used to create variables that allow algebraic @@ -112,6 +117,7 @@ class TransferFunction(LTI): >>> s = TransferFunction.s >>> G = (s + 1)/(s**2 + 2*s + 1) + """ # Give TransferFunction._rmul_() priority for ndarray * TransferFunction @@ -234,6 +240,45 @@ def __init__(self, *args, **kwargs): dt = config.defaults['control.default_dt'] self.dt = dt + # + # Class attributes + # + # These attributes are defined as class attributes so that they are + # documented properly. They are "overwritten" in __init__. + # + + #: Number of system inputs. + #: + #: :meta hide-value: + ninputs = 1 + + #: Number of system outputs. + #: + #: :meta hide-value: + noutputs = 1 + + #: Transfer function numerator polynomial (array) + #: + #: The numerator of the transfer function is store as an 2D list of + #: arrays containing MIMO numerator coefficients, indexed by outputs and + #: inputs. For example, ``num[2][5]`` is the array of coefficients for + #: the numerator of the transfer function from the sixth input to the + #: third output. + #: + #: :meta hide-value: + num = [[0]] + + #: Transfer function denominator polynomial (array) + #: + #: The numerator of the transfer function is store as an 2D list of + #: arrays containing MIMO numerator coefficients, indexed by outputs and + #: inputs. For example, ``den[2][5]`` is the array of coefficients for + #: the denominator of the transfer function from the sixth input to the + #: third output. + #: + #: :meta hide-value: + den = [[0]] + def __call__(self, x, squeeze=None, warn_infinite=True): """Evaluate system's transfer function at complex frequencies. @@ -390,11 +435,13 @@ def __repr__(self): if self.issiso(): return "TransferFunction({num}, {den}{dt})".format( num=self.num[0][0].__repr__(), den=self.den[0][0].__repr__(), - dt=(isdtime(self, strict=True) and ', {}'.format(self.dt)) or '') + dt=', {}'.format(self.dt) if isdtime(self, strict=True) + else '') else: return "TransferFunction({num}, {den}{dt})".format( num=self.num.__repr__(), den=self.den.__repr__(), - dt=(isdtime(self, strict=True) and ', {}'.format(self.dt)) or '') + dt=', {}'.format(self.dt) if isdtime(self, strict=True) + else '') def _repr_latex_(self, var=None): """LaTeX representation of transfer function, for Jupyter notebook""" @@ -1047,7 +1094,7 @@ def sample(self, Ts, method='zoh', alpha=None, prewarp_frequency=None): if method == "matched": return _c2d_matched(self, Ts) sys = (self.num[0][0], self.den[0][0]) - if (method=='bilinear' or (method=='gbt' and alpha==0.5)) and \ + if (method == 'bilinear' or (method == 'gbt' and alpha == 0.5)) and \ prewarp_frequency is not None: Twarp = 2*np.tan(prewarp_frequency*Ts/2)/prewarp_frequency else: @@ -1084,15 +1131,45 @@ def dcgain(self, warn_infinite=False): return self._dcgain(warn_infinite) def _isstatic(self): - """returns True if and only if all of the numerator and denominator - polynomials of the (possibly MIMO) transfer function are zeroth order, - that is, if the system has no dynamics. """ - for list_of_polys in self.num, self.den: - for row in list_of_polys: - for poly in row: - if len(poly) > 1: - return False - return True + """returns True if and only if all of the numerator and denominator + polynomials of the (possibly MIMO) transfer function are zeroth order, + that is, if the system has no dynamics. """ + for list_of_polys in self.num, self.den: + for row in list_of_polys: + for poly in row: + if len(poly) > 1: + return False + return True + + # Attributes for differentiation and delay + # + # These attributes are created here with sphinx docstrings so that the + # autodoc generated documentation has a description. The actual values of + # the class attributes are set at the bottom of the file to avoid problems + # with recursive calls. + + #: Differentation operator (continuous time) + #: + #: The ``s`` constant can be used to create continuous time transfer + #: functions using algebraic expressions. + #: + #: Example + #: ------- + #: >>> s = TransferFunction.s + #: >>> G = (s + 1)/(s**2 + 2*s + 1) + s = None + + #: Delay operator (discrete time) + #: + #: The ``z`` constant can be used to create discrete time transfer + #: functions using algebraic expressions. + #: + #: Example + #: ------- + #: >>> z = TransferFunction.z + #: >>> G = 2 * z / (4 * z**3 + 3*z - 1) + z = None + # c2d function contributed by Benjamin White, Oct 2012 def _c2d_matched(sysC, Ts): @@ -1297,7 +1374,7 @@ def _convert_to_transfer_function(sys, **kw): num = [[[D[i, j]] for j in range(inputs)] for i in range(outputs)] den = [[[1] for j in range(inputs)] for i in range(outputs)] return TransferFunction(num, den) - except: + except Exception: raise TypeError("Can't convert given type to TransferFunction system.") @@ -1563,6 +1640,7 @@ def _clean_part(data): return data + # Define constants to represent differentiation, unit delay TransferFunction.s = TransferFunction([1, 0], [1], 0) TransferFunction.z = TransferFunction([1, 0], [1], True) diff --git a/doc/classes.rst b/doc/classes.rst index fdf39a457..2217c7bff 100644 --- a/doc/classes.rst +++ b/doc/classes.rst @@ -12,6 +12,7 @@ these directly. .. autosummary:: :toctree: generated/ + :recursive: TransferFunction StateSpace diff --git a/doc/conf.py b/doc/conf.py index ebff50858..6fb670869 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -48,7 +48,7 @@ # If your documentation needs a minimal Sphinx version, state it here. # -# needs_sphinx = '1.0' +needs_sphinx = '3.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom @@ -64,11 +64,14 @@ # list of autodoc directive flags that should be automatically applied # to all autodoc directives. -autodoc_default_options = {'members': True, - 'inherited-members': True} +autodoc_default_options = { + 'members': True, + 'inherited-members': True, + 'special-members': '__call__', +} # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +# templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: diff --git a/doc/control.rst b/doc/control.rst index e8a29deb9..a3e28881b 100644 --- a/doc/control.rst +++ b/doc/control.rst @@ -6,8 +6,9 @@ Function reference .. Include header information from the main control module .. automodule:: control - :no-members: - :no-inherited-members: + :no-members: + :no-inherited-members: + :no-special-members: System creation =============== diff --git a/doc/flatsys.rst b/doc/flatsys.rst index b6d2fe962..cd8a4b6ce 100644 --- a/doc/flatsys.rst +++ b/doc/flatsys.rst @@ -7,6 +7,7 @@ Differentially flat systems .. automodule:: control.flatsys :no-members: :no-inherited-members: + :no-special-members: Overview of differential flatness ================================= diff --git a/doc/matlab.rst b/doc/matlab.rst index ae5688dde..c14a67e1f 100644 --- a/doc/matlab.rst +++ b/doc/matlab.rst @@ -7,6 +7,7 @@ .. automodule:: control.matlab :no-members: :no-inherited-members: + :no-special-members: Creating linear models ====================== diff --git a/doc/optimal.rst b/doc/optimal.rst index 9538c28c2..97dbbed0b 100644 --- a/doc/optimal.rst +++ b/doc/optimal.rst @@ -7,6 +7,7 @@ Optimal control .. automodule:: control.optimal :no-members: :no-inherited-members: + :no-special-members: Problem setup ============= From bf2472f7f78ab748d41ed032ced9cc8f33bcfc11 Mon Sep 17 00:00:00 2001 From: Richard Murray Date: Sat, 12 Jun 2021 15:34:45 -0700 Subject: [PATCH 4/7] DOC: move constructor docs from __init__ to class; remove attr docs --- control/flatsys/basis.py | 5 + control/flatsys/linflat.py | 73 +++++++------- control/frdata.py | 23 +++++ control/iosys.py | 117 +++++++++-------------- control/lti.py | 12 +-- control/statesp.py | 42 ++++++-- control/xferfcn.py | 35 ++++++- doc/_templates/custom-class-template.rst | 23 +++++ doc/classes.rst | 4 +- doc/conf.py | 6 +- doc/descfcn.rst | 1 + doc/flatsys.rst | 1 + doc/optimal.rst | 5 + 13 files changed, 222 insertions(+), 125 deletions(-) create mode 100644 doc/_templates/custom-class-template.rst diff --git a/control/flatsys/basis.py b/control/flatsys/basis.py index 7592b79a2..1ea957f52 100644 --- a/control/flatsys/basis.py +++ b/control/flatsys/basis.py @@ -47,6 +47,11 @@ class BasisFamily: :math:`z_i^{(q)}(t)` = basis.eval_deriv(self, i, j, t) + Parameters + ---------- + N : int + Order of the basis set. + """ def __init__(self, N): """Create a basis family of order N.""" diff --git a/control/flatsys/linflat.py b/control/flatsys/linflat.py index 6e74ed581..1deb71960 100644 --- a/control/flatsys/linflat.py +++ b/control/flatsys/linflat.py @@ -42,6 +42,46 @@ class LinearFlatSystem(FlatSystem, LinearIOSystem): + """Base class for a linear, differentially flat system. + + This class is used to create a differentially flat system representation + from a linear system. + + Parameters + ---------- + linsys : StateSpace + LTI StateSpace system to be converted + inputs : int, list of str or None, optional + Description of the system inputs. This can be given as an integer + count or as a list of strings that name the individual signals. + If an integer count is specified, the names of the signal will be + of the form `s[i]` (where `s` is one of `u`, `y`, or `x`). If + this parameter is not given or given as `None`, the relevant + quantity will be determined when possible based on other + information provided to functions using the system. + outputs : int, list of str or None, optional + Description of the system outputs. Same format as `inputs`. + states : int, list of str, or None, optional + Description of the system states. Same format as `inputs`. + dt : None, True or float, optional + System timebase. None (default) indicates continuous + time, True indicates discrete time with undefined sampling + time, positive number is discrete time with specified + sampling time. + params : dict, optional + Parameter values for the systems. Passed to the evaluation + functions for the system as default values, overriding internal + defaults. + name : string, optional + System name (used for specifying signals) + + Returns + ------- + iosys : LinearFlatSystem + Linear system represented as an flat input/output system + + """ + def __init__(self, linsys, inputs=None, outputs=None, states=None, name=None): """Define a flat system from a SISO LTI system. @@ -49,39 +89,6 @@ def __init__(self, linsys, inputs=None, outputs=None, states=None, Given a reachable, single-input/single-output, linear time-invariant system, create a differentially flat system representation. - Parameters - ---------- - linsys : StateSpace - LTI StateSpace system to be converted - inputs : int, list of str or None, optional - Description of the system inputs. This can be given as an integer - count or as a list of strings that name the individual signals. - If an integer count is specified, the names of the signal will be - of the form `s[i]` (where `s` is one of `u`, `y`, or `x`). If - this parameter is not given or given as `None`, the relevant - quantity will be determined when possible based on other - information provided to functions using the system. - outputs : int, list of str or None, optional - Description of the system outputs. Same format as `inputs`. - states : int, list of str, or None, optional - Description of the system states. Same format as `inputs`. - dt : None, True or float, optional - System timebase. None (default) indicates continuous - time, True indicates discrete time with undefined sampling - time, positive number is discrete time with specified - sampling time. - params : dict, optional - Parameter values for the systems. Passed to the evaluation - functions for the system as default values, overriding internal - defaults. - name : string, optional - System name (used for specifying signals) - - Returns - ------- - iosys : LinearFlatSystem - Linear system represented as an flat input/output system - """ # Make sure we can handle the system if (not control.isctime(linsys)): diff --git a/control/frdata.py b/control/frdata.py index 625e84b75..5e9591c55 100644 --- a/control/frdata.py +++ b/control/frdata.py @@ -64,6 +64,29 @@ class FrequencyResponseData(LTI): The FrequencyResponseData (FRD) class is used to represent systems in frequency response data form. + Parameters + ---------- + d : 1D or 3D complex array_like + The frequency response at each frequency point. If 1D, the system is + assumed to be SISO. If 3D, the system is MIMO, with the first + dimension corresponding to the output index of the FRD, the second + dimension corresponding to the input index, and the 3rd dimension + corresponding to the frequency points in omega + w : iterable of real frequencies + List of frequency points for which data are available. + smooth : bool, optional + If ``True``, create an interpoloation function that allows the + frequency response to be computed at any frequency within the range of + frquencies give in ``w``. If ``False`` (default), frequency response + can only be obtained at the frequencies specified in ``w``. + + Attributes + ---------- + ninputs, noutputs : int + Number of input and output variables. + + Notes + ----- The main data members are 'omega' and 'fresp', where `omega` is a 1D array with the frequency points of the response, and `fresp` is a 3D array, with the first dimension corresponding to the output index of the FRD, the diff --git a/control/iosys.py b/control/iosys.py index 7dfd8756c..b1cdfadf3 100644 --- a/control/iosys.py +++ b/control/iosys.py @@ -121,7 +121,7 @@ class for a set of subclasses that are used to implement specific _idCounter = 0 - def name_or_default(self, name=None): + def _name_or_default(self, name=None): if name is None: name = "sys[{}]".format(InputOutputSystem._idCounter) InputOutputSystem._idCounter += 1 @@ -138,39 +138,6 @@ def __init__(self, inputs=None, outputs=None, states=None, params={}, :class:`~control.LinearIOSystem`, :class:`~control.NonlinearIOSystem`, :class:`~control.InterconnectedSystem`. - Parameters - ---------- - inputs : int, list of str, or None - Description of the system inputs. This can be given as an integer - count or as a list of strings that name the individual signals. - If an integer count is specified, the names of the signal will be - of the form `s[i]` (where `s` is one of `u`, `y`, or `x`). If - this parameter is not given or given as `None`, the relevant - quantity will be determined when possible based on other - information provided to functions using the system. - outputs : int, list of str, or None - Description of the system outputs. Same format as `inputs`. - states : int, list of str, or None - Description of the system states. Same format as `inputs`. - dt : None, True or float, optional - System timebase. 0 (default) indicates continuous - time, True indicates discrete time with unspecified sampling - time, positive number is discrete time with specified - sampling time, None indicates unspecified timebase (either - continuous or discrete time). - params : dict, optional - Parameter values for the systems. Passed to the evaluation - functions for the system as default values, overriding internal - defaults. - name : string, optional - System name (used for specifying signals). If unspecified, a - generic name is generated with a unique integer id. - - Returns - ------- - InputOutputSystem - Input/output system object - """ # Store the input arguments @@ -179,7 +146,7 @@ def __init__(self, inputs=None, outputs=None, states=None, params={}, # timebase self.dt = kwargs.get('dt', config.defaults['control.default_dt']) # system name - self.name = self.name_or_default(name) + self.name = self._name_or_default(name) # Parse and store the number of inputs, outputs, and states self.set_inputs(inputs) @@ -686,7 +653,7 @@ def copy(self, newname=None): dup_prefix = config.defaults['iosys.duplicate_system_name_prefix'] dup_suffix = config.defaults['iosys.duplicate_system_name_suffix'] newsys = copy.copy(self) - newsys.name = self.name_or_default( + newsys.name = self._name_or_default( dup_prefix + self.name + dup_suffix if not newname else newname) return newsys @@ -697,6 +664,47 @@ class LinearIOSystem(InputOutputSystem, StateSpace): This class is used to implementat a system that is a linear state space system (defined by the StateSpace system object). + Parameters + ---------- + linsys : StateSpace + LTI StateSpace system to be converted + inputs : int, list of str or None, optional + Description of the system inputs. This can be given as an integer + count or as a list of strings that name the individual signals. If an + integer count is specified, the names of the signal will be of the + form `s[i]` (where `s` is one of `u`, `y`, or `x`). If this parameter + is not given or given as `None`, the relevant quantity will be + determined when possible based on other information provided to + functions using the system. + outputs : int, list of str or None, optional + Description of the system outputs. Same format as `inputs`. + states : int, list of str, or None, optional + Description of the system states. Same format as `inputs`. + dt : None, True or float, optional + System timebase. 0 (default) indicates continuous time, True indicates + discrete time with unspecified sampling time, positive number is + discrete time with specified sampling time, None indicates unspecified + timebase (either continuous or discrete time). + params : dict, optional + Parameter values for the systems. Passed to the evaluation functions + for the system as default values, overriding internal defaults. + name : string, optional + System name (used for specifying signals). If unspecified, a + generic name is generated with a unique integer id. + + Attributes + ---------- + ninputs, noutputs, nstates, dt, etc + See :class:`InputOutputSystem` for inherited attributes. + + A, B, C, D + See :class:`~control.StateSpace` for inherited attributes. + + Returns + ------- + iosys : LinearIOSystem + Linear system represented as an input/output system + """ def __init__(self, linsys, inputs=None, outputs=None, states=None, name=None, **kwargs): @@ -704,42 +712,7 @@ def __init__(self, linsys, inputs=None, outputs=None, states=None, Converts a :class:`~control.StateSpace` system into an :class:`~control.InputOutputSystem` with the same inputs, outputs, and - states. The new system can be a continuous or discrete time system - - Parameters - ---------- - linsys : StateSpace - LTI StateSpace system to be converted - inputs : int, list of str or None, optional - Description of the system inputs. This can be given as an integer - count or as a list of strings that name the individual signals. - If an integer count is specified, the names of the signal will be - of the form `s[i]` (where `s` is one of `u`, `y`, or `x`). If - this parameter is not given or given as `None`, the relevant - quantity will be determined when possible based on other - information provided to functions using the system. - outputs : int, list of str or None, optional - Description of the system outputs. Same format as `inputs`. - states : int, list of str, or None, optional - Description of the system states. Same format as `inputs`. - dt : None, True or float, optional - System timebase. 0 (default) indicates continuous - time, True indicates discrete time with unspecified sampling - time, positive number is discrete time with specified - sampling time, None indicates unspecified timebase (either - continuous or discrete time). - params : dict, optional - Parameter values for the systems. Passed to the evaluation - functions for the system as default values, overriding internal - defaults. - name : string, optional - System name (used for specifying signals). If unspecified, a - generic name is generated with a unique integer id. - - Returns - ------- - iosys : LinearIOSystem - Linear system represented as an input/output system + states. The new system can be a continuous or discrete time system. """ if not isinstance(linsys, StateSpace): diff --git a/control/lti.py b/control/lti.py index b9adf644f..ef5d5569a 100644 --- a/control/lti.py +++ b/control/lti.py @@ -24,13 +24,13 @@ class LTI: """LTI is a parent class to linear time-invariant (LTI) system objects. - LTI is the parent to the StateSpace and TransferFunction child - classes. It contains the number of inputs and outputs, and the - timebase (dt) for the system. + LTI is the parent to the StateSpace and TransferFunction child classes. It + contains the number of inputs and outputs, and the timebase (dt) for the + system. This function is not generally called directly by the user. - The timebase for the system, dt, is used to specify whether the - system is operating in continuous or discrete time. It can have - the following values: + The timebase for the system, dt, is used to specify whether the system + is operating in continuous or discrete time. It can have the following + values: * dt = None No timebase specified * dt = 0 Continuous time system diff --git a/control/statesp.py b/control/statesp.py index 9e009fa85..6b3a1dff3 100644 --- a/control/statesp.py +++ b/control/statesp.py @@ -160,7 +160,7 @@ def _f2s(f): class StateSpace(LTI): """StateSpace(A, B, C, D[, dt]) - A class for representing state-space models + A class for representing state-space models. The StateSpace class is used to represent state-space realizations of linear time-invariant (LTI) systems: @@ -170,13 +170,39 @@ class StateSpace(LTI): where u is the input, y is the output, and x is the state. - The main data members are the A, B, C, and D matrices. The class also - keeps track of the number of states (i.e., the size of A). The data - format used to store state space matrices is set using the value of - `config.defaults['use_numpy_matrix']`. If True (default), the state space - elements are stored as `numpy.matrix` objects; otherwise they are - `numpy.ndarray` objects. The :func:`~control.use_numpy_matrix` function - can be used to set the storage type. + Parameters + ---------- + A, B, C, D: array_like + System matrices of the appropriate dimensions. + dt : None, True or float, optional + System timebase. 0 (default) indicates continuous + time, True indicates discrete time with unspecified sampling + time, positive number is discrete time with specified + sampling time, None indicates unspecified timebase (either + continuous or discrete time). + + Attributes + ---------- + ninputs, noutputs, nstates : int + Number of input, output and state variables. + A, B, C, D : 2D arrays + System matrices defining the input/output dynamics. + dt : None, True or float + System timebase. 0 (default) indicates continuous time, True indicates + discrete time with unspecified sampling time, positive number is + discrete time with specified sampling time, None indicates unspecified + timebase (either continuous or discrete time). + + Notes + ----- + The main data members in the ``StateSpace`` class are the A, B, C, and D + matrices. The class also keeps track of the number of states (i.e., + the size of A). The data format used to store state space matrices is + set using the value of `config.defaults['use_numpy_matrix']`. If True + (default), the state space elements are stored as `numpy.matrix` objects; + otherwise they are `numpy.ndarray` objects. The + :func:`~control.use_numpy_matrix` function can be used to set the storage + type. A discrete time system is created by specifying a nonzero 'timebase', dt when the system is constructed: diff --git a/control/xferfcn.py b/control/xferfcn.py index 117edd120..4871ca5b8 100644 --- a/control/xferfcn.py +++ b/control/xferfcn.py @@ -76,11 +76,38 @@ class TransferFunction(LTI): """TransferFunction(num, den[, dt]) - A class for representing transfer functions + A class for representing transfer functions. The TransferFunction class is used to represent systems in transfer function form. + Parameters + ---------- + num : array_like, or list of list of array_like + Polynomial coefficients of the numerator + den : array_like, or list of list of array_like + Polynomial coefficients of the denominator + dt : None, True or float, optional + System timebase. 0 (default) indicates continuous + time, True indicates discrete time with unspecified sampling + time, positive number is discrete time with specified + sampling time, None indicates unspecified timebase (either + continuous or discrete time). + + Attributes + ---------- + ninputs, noutputs, nstates : int + Number of input, output and state variables. + num, den : 2D list of array + Polynomial coeffients of the numerator and denominator. + dt : None, True or float + System timebase. 0 (default) indicates continuous time, True indicates + discrete time with unspecified sampling time, positive number is + discrete time with specified sampling time, None indicates unspecified + timebase (either continuous or discrete time). + + Notes + ----- The main data members are 'num' and 'den', which are 2-D lists of arrays containing MIMO numerator and denominator coefficients. For example, @@ -259,7 +286,7 @@ def __init__(self, *args, **kwargs): #: Transfer function numerator polynomial (array) #: - #: The numerator of the transfer function is store as an 2D list of + #: The numerator of the transfer function is stored as an 2D list of #: arrays containing MIMO numerator coefficients, indexed by outputs and #: inputs. For example, ``num[2][5]`` is the array of coefficients for #: the numerator of the transfer function from the sixth input to the @@ -1157,6 +1184,8 @@ def _isstatic(self): #: ------- #: >>> s = TransferFunction.s #: >>> G = (s + 1)/(s**2 + 2*s + 1) + #: + #: :meta hide-value: s = None #: Delay operator (discrete time) @@ -1168,6 +1197,8 @@ def _isstatic(self): #: ------- #: >>> z = TransferFunction.z #: >>> G = 2 * z / (4 * z**3 + 3*z - 1) + #: + #: :meta hide-value: z = None diff --git a/doc/_templates/custom-class-template.rst b/doc/_templates/custom-class-template.rst new file mode 100644 index 000000000..53a76e905 --- /dev/null +++ b/doc/_templates/custom-class-template.rst @@ -0,0 +1,23 @@ +{{ fullname | escape | underline}} + +.. currentmodule:: {{ module }} + +.. autoclass:: {{ objname }} + :members: + :show-inheritance: + :inherited-members: + :special-members: + + {% block methods %} + {% if methods %} + .. rubric:: {{ _('Methods') }} + + .. autosummary:: + :nosignatures: + {% for item in methods %} + {%- if not item.startswith('_') %} + ~{{ name }}.{{ item }} + {%- endif -%} + {%- endfor %} + {% endif %} + {% endblock %} diff --git a/doc/classes.rst b/doc/classes.rst index 2217c7bff..a1c0c3c39 100644 --- a/doc/classes.rst +++ b/doc/classes.rst @@ -12,7 +12,7 @@ these directly. .. autosummary:: :toctree: generated/ - :recursive: + :template: custom-class-template.rst TransferFunction StateSpace @@ -26,6 +26,7 @@ that allow for linear, nonlinear, and interconnected elements: .. autosummary:: :toctree: generated/ + :template: custom-class-template.rst InterconnectedSystem LinearICSystem @@ -35,6 +36,7 @@ that allow for linear, nonlinear, and interconnected elements: Additional classes ================== .. autosummary:: + :template: custom-class-template.rst flatsys.BasisFamily flatsys.FlatSystem diff --git a/doc/conf.py b/doc/conf.py index 6fb670869..19c2970e1 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -48,7 +48,7 @@ # If your documentation needs a minimal Sphinx version, state it here. # -needs_sphinx = '3.0' +needs_sphinx = '3.1' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom @@ -67,11 +67,11 @@ autodoc_default_options = { 'members': True, 'inherited-members': True, - 'special-members': '__call__', + 'exclude-members': '__init__, __weakref__, __repr__, __str__' } # Add any paths that contain templates here, relative to this directory. -# templates_path = ['_templates'] +templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: diff --git a/doc/descfcn.rst b/doc/descfcn.rst index 05f6bd94a..cc3b8668d 100644 --- a/doc/descfcn.rst +++ b/doc/descfcn.rst @@ -79,6 +79,7 @@ Module classes and functions ============================ .. autosummary:: :toctree: generated/ + :template: custom-class-template.rst ~control.DescribingFunctionNonlinearity ~control.friction_backlash_nonlinearity diff --git a/doc/flatsys.rst b/doc/flatsys.rst index cd8a4b6ce..4db754717 100644 --- a/doc/flatsys.rst +++ b/doc/flatsys.rst @@ -260,6 +260,7 @@ Flat systems classes -------------------- .. autosummary:: :toctree: generated/ + :template: custom-class-template.rst BasisFamily BezierFamily diff --git a/doc/optimal.rst b/doc/optimal.rst index 97dbbed0b..133163cdd 100644 --- a/doc/optimal.rst +++ b/doc/optimal.rst @@ -277,8 +277,13 @@ Module classes and functions ============================ .. autosummary:: :toctree: generated/ + :template: custom-class-template.rst ~control.optimal.OptimalControlProblem + +.. autosummary:: + :toctree: generated/ + ~control.optimal.solve_ocp ~control.optimal.create_mpc_iosystem ~control.optimal.input_poly_constraint From 70a6cf91559ac6d5947743a980bc308e6a336239 Mon Sep 17 00:00:00 2001 From: Richard Murray Date: Sun, 13 Jun 2021 11:51:25 -0700 Subject: [PATCH 5/7] TRV: fix typos pointed out by @namannimmo10 --- control/frdata.py | 4 ++-- control/xferfcn.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/control/frdata.py b/control/frdata.py index 5e9591c55..5e00798af 100644 --- a/control/frdata.py +++ b/control/frdata.py @@ -75,9 +75,9 @@ class FrequencyResponseData(LTI): w : iterable of real frequencies List of frequency points for which data are available. smooth : bool, optional - If ``True``, create an interpoloation function that allows the + If ``True``, create an interpolation function that allows the frequency response to be computed at any frequency within the range of - frquencies give in ``w``. If ``False`` (default), frequency response + frequencies give in ``w``. If ``False`` (default), frequency response can only be obtained at the frequencies specified in ``w``. Attributes diff --git a/control/xferfcn.py b/control/xferfcn.py index 4871ca5b8..399def909 100644 --- a/control/xferfcn.py +++ b/control/xferfcn.py @@ -99,7 +99,7 @@ class TransferFunction(LTI): ninputs, noutputs, nstates : int Number of input, output and state variables. num, den : 2D list of array - Polynomial coeffients of the numerator and denominator. + Polynomial coefficients of the numerator and denominator. dt : None, True or float System timebase. 0 (default) indicates continuous time, True indicates discrete time with unspecified sampling time, positive number is @@ -143,7 +143,7 @@ class TransferFunction(LTI): creation of transfer functions. For example, >>> s = TransferFunction.s - >>> G = (s + 1)/(s**2 + 2*s + 1) + >>> G = (s + 1)/(s**2 + 2*s + 1) """ From bda7d82f2016fc89a0ddc4f0f6ccffa2a09e2d37 Mon Sep 17 00:00:00 2001 From: Richard Murray Date: Mon, 14 Jun 2021 21:19:24 -0700 Subject: [PATCH 6/7] DOC: fix inconsistencies in class constructor + class list documentation --- control/descfcn.py | 16 ++--- control/flatsys/bezier.py | 2 +- control/flatsys/flatsys.py | 114 ++++++++++++++++---------------- control/flatsys/linflat.py | 5 -- control/flatsys/systraj.py | 41 ++++++------ control/frdata.py | 17 +++-- control/iosys.py | 129 ++++++++++++++++--------------------- control/optimal.py | 124 +++++++++++++++++------------------ control/xferfcn.py | 8 +-- doc/classes.rst | 7 +- doc/flatsys.rst | 18 ++---- doc/iosys.rst | 7 +- doc/optimal.rst | 1 + 13 files changed, 232 insertions(+), 257 deletions(-) diff --git a/control/descfcn.py b/control/descfcn.py index 14a345495..2ebb18569 100644 --- a/control/descfcn.py +++ b/control/descfcn.py @@ -26,7 +26,7 @@ # Class for nonlinearities with a built-in describing function class DescribingFunctionNonlinearity(): - """Base class for nonlinear systems with a describing function + """Base class for nonlinear systems with a describing function. This class is intended to be used as a base class for nonlinear functions that have an analytically defined describing function. Subclasses should @@ -36,16 +36,16 @@ class DescribingFunctionNonlinearity(): """ def __init__(self): - """Initailize a describing function nonlinearity (optional)""" + """Initailize a describing function nonlinearity (optional).""" pass def __call__(self, A): - """Evaluate the nonlinearity at a (scalar) input value""" + """Evaluate the nonlinearity at a (scalar) input value.""" raise NotImplementedError( "__call__() not implemented for this function (internal error)") def describing_function(self, A): - """Return the describing function for a nonlinearity + """Return the describing function for a nonlinearity. This method is used to allow analytical representations of the describing function for a nonlinearity. It turns the (complex) value @@ -56,7 +56,7 @@ def describing_function(self, A): "describing function not implemented for this function") def _isstatic(self): - """Return True if the function has no internal state (memoryless) + """Return True if the function has no internal state (memoryless). This internal function is used to optimize numerical computation of the describing function. It can be set to `True` if the instance @@ -329,7 +329,7 @@ def _find_intersection(L1a, L1b, L2a, L2b): # Saturation nonlinearity class saturation_nonlinearity(DescribingFunctionNonlinearity): - """Create a saturation nonlinearity for use in describing function analysis + """Create saturation nonlinearity for use in describing function analysis. This class creates a nonlinear function representing a saturation with given upper and lower bounds, including the describing function for the @@ -381,7 +381,7 @@ def describing_function(self, A): # Relay with hysteresis (FBS2e, Example 10.12) class relay_hysteresis_nonlinearity(DescribingFunctionNonlinearity): - """Relay w/ hysteresis nonlinearity for use in describing function analysis + """Relay w/ hysteresis nonlinearity for describing function analysis. This class creates a nonlinear function representing a a relay with symmetric upper and lower bounds of magnitude `b` and a hysteretic region @@ -437,7 +437,7 @@ def describing_function(self, A): # Friction-dominated backlash nonlinearity (#48 in Gelb and Vander Velde, 1968) class friction_backlash_nonlinearity(DescribingFunctionNonlinearity): - """Backlash nonlinearity for use in describing function analysis + """Backlash nonlinearity for describing function analysis. This class creates a nonlinear function representing a friction-dominated backlash nonlinearity ,including the describing function for the diff --git a/control/flatsys/bezier.py b/control/flatsys/bezier.py index 5d0d551de..45a28995f 100644 --- a/control/flatsys/bezier.py +++ b/control/flatsys/bezier.py @@ -43,7 +43,7 @@ from .basis import BasisFamily class BezierFamily(BasisFamily): - r"""Polynomial basis functions. + r"""Bezier curve basis functions. This class represents the family of polynomials of the form diff --git a/control/flatsys/flatsys.py b/control/flatsys/flatsys.py index 1905c4cb8..bbf1e7fc7 100644 --- a/control/flatsys/flatsys.py +++ b/control/flatsys/flatsys.py @@ -54,8 +54,59 @@ class FlatSystem(NonlinearIOSystem): """Base class for representing a differentially flat system. The FlatSystem class is used as a base class to describe differentially - flat systems for trajectory generation. The class must implement two - functions: + flat systems for trajectory generation. The output of the system does not + need to be the differentially flat output. + + Parameters + ---------- + forward : callable + A function to compute the flat flag given the states and input. + reverse : callable + A function to compute the states and input given the flat flag. + updfcn : callable, optional + Function returning the state update function + + `updfcn(t, x, u[, param]) -> array` + + where `x` is a 1-D array with shape (nstates,), `u` is a 1-D array + with shape (ninputs,), `t` is a float representing the currrent + time, and `param` is an optional dict containing the values of + parameters used by the function. If not specified, the state + space update will be computed using the flat system coordinates. + outfcn : callable + Function returning the output at the given state + + `outfcn(t, x, u[, param]) -> array` + + where the arguments are the same as for `upfcn`. If not + specified, the output will be the flat outputs. + inputs : int, list of str, or None + Description of the system inputs. This can be given as an integer + count or as a list of strings that name the individual signals. + If an integer count is specified, the names of the signal will be + of the form `s[i]` (where `s` is one of `u`, `y`, or `x`). If + this parameter is not given or given as `None`, the relevant + quantity will be determined when possible based on other + information provided to functions using the system. + outputs : int, list of str, or None + Description of the system outputs. Same format as `inputs`. + states : int, list of str, or None + Description of the system states. Same format as `inputs`. + dt : None, True or float, optional + System timebase. None (default) indicates continuous + time, True indicates discrete time with undefined sampling + time, positive number is discrete time with specified + sampling time. + params : dict, optional + Parameter values for the systems. Passed to the evaluation + functions for the system as default values, overriding internal + defaults. + name : string, optional + System name (used for specifying signals) + + Notes + ----- + The class must implement two functions: zflag = flatsys.foward(x, u) This function computes the flag (derivatives) of the flat output. @@ -83,65 +134,13 @@ def __init__(self, updfcn=None, outfcn=None, # I/O system inputs=None, outputs=None, states=None, params={}, dt=None, name=None): - """Create a differentially flat input/output system. + """Create a differentially flat I/O system. The FlatIOSystem constructor is used to create an input/output system - object that also represents a differentially flat system. The output - of the system does not need to be the differentially flat output. - - Parameters - ---------- - forward : callable - A function to compute the flat flag given the states and input. - reverse : callable - A function to compute the states and input given the flat flag. - updfcn : callable, optional - Function returning the state update function - - `updfcn(t, x, u[, param]) -> array` - - where `x` is a 1-D array with shape (nstates,), `u` is a 1-D array - with shape (ninputs,), `t` is a float representing the currrent - time, and `param` is an optional dict containing the values of - parameters used by the function. If not specified, the state - space update will be computed using the flat system coordinates. - outfcn : callable - Function returning the output at the given state - - `outfcn(t, x, u[, param]) -> array` - - where the arguments are the same as for `upfcn`. If not - specified, the output will be the flat outputs. - inputs : int, list of str, or None - Description of the system inputs. This can be given as an integer - count or as a list of strings that name the individual signals. - If an integer count is specified, the names of the signal will be - of the form `s[i]` (where `s` is one of `u`, `y`, or `x`). If - this parameter is not given or given as `None`, the relevant - quantity will be determined when possible based on other - information provided to functions using the system. - outputs : int, list of str, or None - Description of the system outputs. Same format as `inputs`. - states : int, list of str, or None - Description of the system states. Same format as `inputs`. - dt : None, True or float, optional - System timebase. None (default) indicates continuous - time, True indicates discrete time with undefined sampling - time, positive number is discrete time with specified - sampling time. - params : dict, optional - Parameter values for the systems. Passed to the evaluation - functions for the system as default values, overriding internal - defaults. - name : string, optional - System name (used for specifying signals) - - Returns - ------- - InputOutputSystem - Input/output system object + object that also represents a differentially flat system. """ + # TODO: specify default update and output functions if updfcn is None: updfcn = self._flat_updfcn if outfcn is None: outfcn = self._flat_outfcn @@ -158,6 +157,7 @@ def __init__(self, # Save the length of the flat flag def forward(self, x, u, params={}): + """Compute the flat flag given the states and input. Given the states and inputs for a system, compute the flat diff --git a/control/flatsys/linflat.py b/control/flatsys/linflat.py index 1deb71960..1e96a23d2 100644 --- a/control/flatsys/linflat.py +++ b/control/flatsys/linflat.py @@ -75,11 +75,6 @@ class LinearFlatSystem(FlatSystem, LinearIOSystem): name : string, optional System name (used for specifying signals) - Returns - ------- - iosys : LinearFlatSystem - Linear system represented as an flat input/output system - """ def __init__(self, linsys, inputs=None, outputs=None, states=None, diff --git a/control/flatsys/systraj.py b/control/flatsys/systraj.py index 4505d3563..c6ffb0867 100644 --- a/control/flatsys/systraj.py +++ b/control/flatsys/systraj.py @@ -41,30 +41,29 @@ class SystemTrajectory: """Class representing a system trajectory. - The `SystemTrajectory` class is used to represent the trajectory of - a (differentially flat) system. Used by the - :func:`~control.trajsys.point_to_point` function to return a - trajectory. + The `SystemTrajectory` class is used to represent the + trajectory of a (differentially flat) system. Used by the + :func:`~control.trajsys.point_to_point` function to return a trajectory. - """ - def __init__(self, sys, basis, coeffs=[], flaglen=[]): - """Initilize a system trajectory object. + Parameters + ---------- + sys : FlatSystem + Flat system object associated with this trajectory. + basis : BasisFamily + Family of basis vectors to use to represent the trajectory. + coeffs : list of 1D arrays, optional + For each flat output, define the coefficients of the basis + functions used to represent the trajectory. Defaults to an empty + list. + flaglen : list of ints, optional + For each flat output, the number of derivatives of the flat + output used to define the trajectory. Defaults to an empty + list. - Parameters - ---------- - sys : FlatSystem - Flat system object associated with this trajectory. - basis : BasisFamily - Family of basis vectors to use to represent the trajectory. - coeffs : list of 1D arrays, optional - For each flat output, define the coefficients of the basis - functions used to represent the trajectory. Defaults to an empty - list. - flaglen : list of ints, optional - For each flat output, the number of derivatives of the flat output - used to define the trajectory. Defaults to an empty list. + """ - """ + def __init__(self, sys, basis, coeffs=[], flaglen=[]): + """Initilize a system trajectory object.""" self.nstates = sys.nstates self.ninputs = sys.ninputs self.system = sys diff --git a/control/frdata.py b/control/frdata.py index 5e00798af..9eee5aa86 100644 --- a/control/frdata.py +++ b/control/frdata.py @@ -57,9 +57,9 @@ class FrequencyResponseData(LTI): - """FrequencyResponseData(d, w) + """FrequencyResponseData(d, w[, smooth]) - A class for models defined by frequency response data (FRD) + A class for models defined by frequency response data (FRD). The FrequencyResponseData (FRD) class is used to represent systems in frequency response data form. @@ -84,13 +84,18 @@ class FrequencyResponseData(LTI): ---------- ninputs, noutputs : int Number of input and output variables. + omega : 1D array + Frequency points of the response. + fresp : 3D array + Frequency response, indexed by output index, input index, and + frequency point. Notes ----- - The main data members are 'omega' and 'fresp', where `omega` is a 1D array - with the frequency points of the response, and `fresp` is a 3D array, with - the first dimension corresponding to the output index of the FRD, the - second dimension corresponding to the input index, and the 3rd dimension + The main data members are 'omega' and 'fresp', where 'omega' is a the 1D + arran yf frequency points and and 'fresp' is a 3D array, with the first + dimension corresponding to the output index of the FRD, the second + dimension corresponding to the input index, and the 3rd dimension corresponding to the frequency points in omega. For example, >>> frdata[2,5,:] = numpy.array([1., 0.8-0.2j, 0.2-0.8j]) diff --git a/control/iosys.py b/control/iosys.py index b1cdfadf3..c8469bce0 100644 --- a/control/iosys.py +++ b/control/iosys.py @@ -700,11 +700,6 @@ class LinearIOSystem(InputOutputSystem, StateSpace): A, B, C, D See :class:`~control.StateSpace` for inherited attributes. - Returns - ------- - iosys : LinearIOSystem - Linear system represented as an input/output system - """ def __init__(self, linsys, inputs=None, outputs=None, states=None, name=None, **kwargs): @@ -777,78 +772,68 @@ def _out(self, t, x, u): class NonlinearIOSystem(InputOutputSystem): """Nonlinear I/O system. - This class is used to implement a system that is a nonlinear state - space system (defined by and update function and an output function). - - """ - def __init__(self, updfcn, outfcn=None, inputs=None, outputs=None, - states=None, params={}, name=None, **kwargs): - """Create a nonlinear I/O system given update and output functions. - - Creates an :class:`~control.InputOutputSystem` for a nonlinear system - by specifying a state update function and an output function. The new - system can be a continuous or discrete time system (Note: - discrete-time systems not yet supported by most function.) - - Parameters - ---------- - updfcn : callable - Function returning the state update function + Creates an :class:`~control.InputOutputSystem` for a nonlinear system + by specifying a state update function and an output function. The new + system can be a continuous or discrete time system (Note: + discrete-time systems not yet supported by most function.) - `updfcn(t, x, u, params) -> array` + Parameters + ---------- + updfcn : callable + Function returning the state update function - where `x` is a 1-D array with shape (nstates,), `u` is a 1-D array - with shape (ninputs,), `t` is a float representing the currrent - time, and `params` is a dict containing the values of parameters - used by the function. + `updfcn(t, x, u, params) -> array` - outfcn : callable - Function returning the output at the given state + where `x` is a 1-D array with shape (nstates,), `u` is a 1-D array + with shape (ninputs,), `t` is a float representing the currrent + time, and `params` is a dict containing the values of parameters + used by the function. - `outfcn(t, x, u, params) -> array` + outfcn : callable + Function returning the output at the given state - where the arguments are the same as for `upfcn`. + `outfcn(t, x, u, params) -> array` - inputs : int, list of str or None, optional - Description of the system inputs. This can be given as an integer - count or as a list of strings that name the individual signals. - If an integer count is specified, the names of the signal will be - of the form `s[i]` (where `s` is one of `u`, `y`, or `x`). If - this parameter is not given or given as `None`, the relevant - quantity will be determined when possible based on other - information provided to functions using the system. + where the arguments are the same as for `upfcn`. - outputs : int, list of str or None, optional - Description of the system outputs. Same format as `inputs`. + inputs : int, list of str or None, optional + Description of the system inputs. This can be given as an integer + count or as a list of strings that name the individual signals. + If an integer count is specified, the names of the signal will be + of the form `s[i]` (where `s` is one of `u`, `y`, or `x`). If + this parameter is not given or given as `None`, the relevant + quantity will be determined when possible based on other + information provided to functions using the system. - states : int, list of str, or None, optional - Description of the system states. Same format as `inputs`. + outputs : int, list of str or None, optional + Description of the system outputs. Same format as `inputs`. - params : dict, optional - Parameter values for the systems. Passed to the evaluation - functions for the system as default values, overriding internal - defaults. + states : int, list of str, or None, optional + Description of the system states. Same format as `inputs`. - dt : timebase, optional - The timebase for the system, used to specify whether the system is - operating in continuous or discrete time. It can have the - following values: + params : dict, optional + Parameter values for the systems. Passed to the evaluation + functions for the system as default values, overriding internal + defaults. - * dt = 0: continuous time system (default) - * dt > 0: discrete time system with sampling period 'dt' - * dt = True: discrete time with unspecified sampling period - * dt = None: no timebase specified + dt : timebase, optional + The timebase for the system, used to specify whether the system is + operating in continuous or discrete time. It can have the + following values: - name : string, optional - System name (used for specifying signals). If unspecified, a - generic name is generated with a unique integer id. + * dt = 0: continuous time system (default) + * dt > 0: discrete time system with sampling period 'dt' + * dt = True: discrete time with unspecified sampling period + * dt = None: no timebase specified - Returns - ------- - iosys : NonlinearIOSystem - Nonlinear system represented as an input/output system. + name : string, optional + System name (used for specifying signals). If unspecified, a + generic name is generated with a unique integer id. - """ + """ + def __init__(self, updfcn, outfcn=None, inputs=None, outputs=None, + states=None, params={}, name=None, **kwargs): + """Create a nonlinear I/O system given update and output functions.""" # Look for 'input' and 'output' parameter name variants inputs = _parse_signal_parameter(inputs, 'input', kwargs) outputs = _parse_signal_parameter(outputs, 'output', kwargs) @@ -949,21 +934,14 @@ class InterconnectedSystem(InputOutputSystem): whose inputs and outputs are connected via a connection map. The overall system inputs and outputs are subsets of the subsystem inputs and outputs. + See :func:`~control.interconnect` for a list of parameters. + """ def __init__(self, syslist, connections=[], inplist=[], outlist=[], inputs=None, outputs=None, states=None, params={}, dt=None, name=None, **kwargs): - """Create an I/O system from a list of systems + connection info. - - The InterconnectedSystem class is used to represent an input/output - system that consists of an interconnection between a set of subystems. - The outputs of each subsystem can be summed together to provide - inputs to other subsystems. The overall system inputs and outputs can - be any subset of subsystem inputs and outputs. - - See :func:`~control.interconnect` for a list of parameters. + """Create an I/O system from a list of systems + connection info.""" - """ # Look for 'input' and 'output' parameter name variants inputs = _parse_signal_parameter(inputs, 'input', kwargs) outputs = _parse_signal_parameter(outputs, 'output', kwargs, end=True) @@ -1430,6 +1408,9 @@ class LinearICSystem(InterconnectedSystem, LinearIOSystem): :class:`StateSpace` class structure, allowing it to be passed to functions that expect a :class:`StateSpace` system. + This class is usually generated using :func:`~control.interconnect` and + not called directly + """ def __init__(self, io_sys, ss_sys=None): @@ -2190,7 +2171,7 @@ def interconnect(syslist, connections=None, inplist=[], outlist=[], Notes ----- If a system is duplicated in the list of systems to be connected, - a warning is generated a copy of the system is created with the + a warning is generated and a copy of the system is created with the name of the new system determined by adding the prefix and suffix strings in config.defaults['iosys.linearized_system_name_prefix'] and config.defaults['iosys.linearized_system_name_suffix'], with the diff --git a/control/optimal.py b/control/optimal.py index 63509ef4f..bbc8d0c9a 100644 --- a/control/optimal.py +++ b/control/optimal.py @@ -22,7 +22,7 @@ class OptimalControlProblem(): - """Description of a finite horizon, optimal control problem + """Description of a finite horizon, optimal control problem. The `OptimalControlProblem` class holds all of the information required to specify and optimal control problem: the system dynamics, cost function, @@ -31,12 +31,64 @@ class OptimalControlProblem(): `optimize.minimize` module, with the hope that this makes it easier to remember how to describe a problem. + Parameters + ---------- + sys : InputOutputSystem + I/O system for which the optimal input will be computed. + timepts : 1D array_like + List of times at which the optimal input should be computed. + integral_cost : callable + Function that returns the integral cost given the current state + and input. Called as integral_cost(x, u). + trajectory_constraints : list of tuples, optional + List of constraints that should hold at each point in the time + vector. Each element of the list should consist of a tuple with + first element given by :meth:`~scipy.optimize.LinearConstraint` or + :meth:`~scipy.optimize.NonlinearConstraint` and the remaining + elements of the tuple are the arguments that would be passed to + those functions. The constraints will be applied at each time + point along the trajectory. + terminal_cost : callable, optional + Function that returns the terminal cost given the current state + and input. Called as terminal_cost(x, u). + initial_guess : 1D or 2D array_like + Initial inputs to use as a guess for the optimal input. The + inputs should either be a 2D vector of shape (ninputs, horizon) + or a 1D input of shape (ninputs,) that will be broadcast by + extension of the time axis. + log : bool, optional + If `True`, turn on logging messages (using Python logging module). + kwargs : dict, optional + Additional parameters (passed to :func:`scipy.optimal.minimize`). + + Returns + ------- + ocp : OptimalControlProblem + Optimal control problem object, to be used in computing optimal + controllers. + + Additional parameters + --------------------- + solve_ivp_method : str, optional + Set the method used by :func:`scipy.integrate.solve_ivp`. + solve_ivp_kwargs : str, optional + Pass additional keywords to :func:`scipy.integrate.solve_ivp`. + minimize_method : str, optional + Set the method used by :func:`scipy.optimize.minimize`. + minimize_options : str, optional + Set the options keyword used by :func:`scipy.optimize.minimize`. + minimize_kwargs : str, optional + Pass additional keywords to :func:`scipy.optimize.minimize`. + Notes ----- - This class sets up an optimization over the inputs at each point in - time, using the integral and terminal costs as well as the - trajectory and terminal constraints. The `compute_trajectory` - method sets up an optimization problem that can be solved using + To describe an optimal control problem we need an input/output system, a + time horizon, a cost function, and (optionally) a set of constraints on + the state and/or input, either along the trajectory and at the terminal + time. This class sets up an optimization over the inputs at each point in + time, using the integral and terminal costs as well as the trajectory and + terminal constraints. The `compute_trajectory` method sets up an + optimization problem that can be solved using :func:`scipy.optimize.minimize`. The `_cost_function` method takes the information computes the cost of the @@ -62,63 +114,7 @@ def __init__( self, sys, timepts, integral_cost, trajectory_constraints=[], terminal_cost=None, terminal_constraints=[], initial_guess=None, basis=None, log=False, **kwargs): - """Set up an optimal control problem - - To describe an optimal control problem we need an input/output system, - a time horizon, a cost function, and (optionally) a set of constraints - on the state and/or input, either along the trajectory and at the - terminal time. - - Parameters - ---------- - sys : InputOutputSystem - I/O system for which the optimal input will be computed. - timepts : 1D array_like - List of times at which the optimal input should be computed. - integral_cost : callable - Function that returns the integral cost given the current state - and input. Called as integral_cost(x, u). - trajectory_constraints : list of tuples, optional - List of constraints that should hold at each point in the time - vector. Each element of the list should consist of a tuple with - first element given by :meth:`~scipy.optimize.LinearConstraint` or - :meth:`~scipy.optimize.NonlinearConstraint` and the remaining - elements of the tuple are the arguments that would be passed to - those functions. The constraints will be applied at each time - point along the trajectory. - terminal_cost : callable, optional - Function that returns the terminal cost given the current state - and input. Called as terminal_cost(x, u). - initial_guess : 1D or 2D array_like - Initial inputs to use as a guess for the optimal input. The - inputs should either be a 2D vector of shape (ninputs, horizon) - or a 1D input of shape (ninputs,) that will be broadcast by - extension of the time axis. - log : bool, optional - If `True`, turn on logging messages (using Python logging module). - kwargs : dict, optional - Additional parameters (passed to :func:`scipy.optimal.minimize`). - - Returns - ------- - ocp : OptimalControlProblem - Optimal control problem object, to be used in computing optimal - controllers. - - Additional parameters - --------------------- - solve_ivp_method : str, optional - Set the method used by :func:`scipy.integrate.solve_ivp`. - solve_ivp_kwargs : str, optional - Pass additional keywords to :func:`scipy.integrate.solve_ivp`. - minimize_method : str, optional - Set the method used by :func:`scipy.optimize.minimize`. - minimize_options : str, optional - Set the options keyword used by :func:`scipy.optimize.minimize`. - minimize_kwargs : str, optional - Pass additional keywords to :func:`scipy.optimize.minimize`. - - """ + """Set up an optimal control problem.""" # Save the basic information for use later self.system = sys self.timepts = timepts @@ -772,9 +768,9 @@ def compute_mpc(self, x, squeeze=None): # Optimal control result class OptimalControlResult(sp.optimize.OptimizeResult): - """Represents the optimal control result + """Result from solving an optimal control problem. - This class is a subclass of :class:`sp.optimize.OptimizeResult` with + This class is a subclass of :class:`scipy.optimize.OptimizeResult` with additional attributes associated with solving optimal control problems. Attributes diff --git a/control/xferfcn.py b/control/xferfcn.py index 399def909..cb3bb4d41 100644 --- a/control/xferfcn.py +++ b/control/xferfcn.py @@ -108,13 +108,13 @@ class TransferFunction(LTI): Notes ----- - The main data members are 'num' and 'den', which are 2-D lists of arrays - containing MIMO numerator and denominator coefficients. For example, + The attribues 'num' and 'den' are 2-D lists of arrays containing MIMO + numerator and denominator coefficients. For example, >>> num[2][5] = numpy.array([1., 4., 8.]) - means that the numerator of the transfer function from the 6th input to the - 3rd output is set to s^2 + 4s + 8. + means that the numerator of the transfer function from the 6th input to + the 3rd output is set to s^2 + 4s + 8. A discrete time transfer function is created by specifying a nonzero 'timebase' dt when the system is constructed: diff --git a/doc/classes.rst b/doc/classes.rst index a1c0c3c39..b80b7dd54 100644 --- a/doc/classes.rst +++ b/doc/classes.rst @@ -17,7 +17,6 @@ these directly. TransferFunction StateSpace FrequencyResponseData - InputOutputSystem Input/output system subclasses ============================== @@ -25,9 +24,10 @@ Input/output systems are accessed primarily via a set of subclasses that allow for linear, nonlinear, and interconnected elements: .. autosummary:: - :toctree: generated/ :template: custom-class-template.rst + :nosignatures: + InputOutputSystem InterconnectedSystem LinearICSystem LinearIOSystem @@ -37,10 +37,13 @@ Additional classes ================== .. autosummary:: :template: custom-class-template.rst + :nosignatures: + DescribingFunctionNonlinearity flatsys.BasisFamily flatsys.FlatSystem flatsys.LinearFlatSystem flatsys.PolyFamily flatsys.SystemTrajectory optimal.OptimalControlProblem + optimal.OptimalControlResult diff --git a/doc/flatsys.rst b/doc/flatsys.rst index 4db754717..7599dd2af 100644 --- a/doc/flatsys.rst +++ b/doc/flatsys.rst @@ -256,22 +256,18 @@ the endpoints. Module classes and functions ============================ -Flat systems classes --------------------- .. autosummary:: :toctree: generated/ :template: custom-class-template.rst - BasisFamily - BezierFamily - FlatSystem - LinearFlatSystem - PolyFamily - SystemTrajectory + ~control.flatsys.BasisFamily + ~control.flatsys.BezierFamily + ~control.flatsys.FlatSystem + ~control.flatsys.LinearFlatSystem + ~control.flatsys.PolyFamily + ~control.flatsys.SystemTrajectory -Flat systems functions ----------------------- .. autosummary:: :toctree: generated/ - point_to_point + ~control.flatsys.point_to_point diff --git a/doc/iosys.rst b/doc/iosys.rst index 1b160bad1..41e37cfec 100644 --- a/doc/iosys.rst +++ b/doc/iosys.rst @@ -263,9 +263,9 @@ unconnected (so be careful!). Module classes and functions ============================ -Input/output system classes ---------------------------- .. autosummary:: + :toctree: generated/ + :template: custom-class-template.rst ~control.InputOutputSystem ~control.InterconnectedSystem @@ -273,9 +273,8 @@ Input/output system classes ~control.LinearIOSystem ~control.NonlinearIOSystem -Input/output system functions ------------------------------ .. autosummary:: + :toctree: generated/ ~control.find_eqpt ~control.linearize diff --git a/doc/optimal.rst b/doc/optimal.rst index 133163cdd..e173e430b 100644 --- a/doc/optimal.rst +++ b/doc/optimal.rst @@ -280,6 +280,7 @@ Module classes and functions :template: custom-class-template.rst ~control.optimal.OptimalControlProblem + ~control.optimal.OptimalControlResult .. autosummary:: :toctree: generated/ From 866a07c6de72de77680e34358e7959dc67b41ffa Mon Sep 17 00:00:00 2001 From: Richard Murray Date: Tue, 15 Jun 2021 08:07:45 -0700 Subject: [PATCH 7/7] TRV: fix typos in FRD docstring --- control/frdata.py | 15 +++++++-------- control/iosys.py | 8 ++++---- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/control/frdata.py b/control/frdata.py index 9eee5aa86..5e2f3f2e1 100644 --- a/control/frdata.py +++ b/control/frdata.py @@ -92,18 +92,17 @@ class FrequencyResponseData(LTI): Notes ----- - The main data members are 'omega' and 'fresp', where 'omega' is a the 1D - arran yf frequency points and and 'fresp' is a 3D array, with the first - dimension corresponding to the output index of the FRD, the second - dimension corresponding to the input index, and the 3rd dimension + The main data members are 'omega' and 'fresp', where 'omega' is a 1D array + of frequency points and and 'fresp' is a 3D array of frequency responses, + with the first dimension corresponding to the output index of the FRD, the + second dimension corresponding to the input index, and the 3rd dimension corresponding to the frequency points in omega. For example, >>> frdata[2,5,:] = numpy.array([1., 0.8-0.2j, 0.2-0.8j]) - means that the frequency response from the 6th input to the 3rd - output at the frequencies defined in omega is set to the array - above, i.e. the rows represent the outputs and the columns - represent the inputs. + means that the frequency response from the 6th input to the 3rd output at + the frequencies defined in omega is set to the array above, i.e. the rows + represent the outputs and the columns represent the inputs. A frequency response data object is callable and returns the value of the transfer function evaluated at a point in the complex plane (must be on diff --git a/control/iosys.py b/control/iosys.py index c8469bce0..08249a651 100644 --- a/control/iosys.py +++ b/control/iosys.py @@ -772,10 +772,10 @@ def _out(self, t, x, u): class NonlinearIOSystem(InputOutputSystem): """Nonlinear I/O system. - Creates an :class:`~control.InputOutputSystem` for a nonlinear system - by specifying a state update function and an output function. The new - system can be a continuous or discrete time system (Note: - discrete-time systems not yet supported by most function.) + Creates an :class:`~control.InputOutputSystem` for a nonlinear system by + specifying a state update function and an output function. The new system + can be a continuous or discrete time system (Note: discrete-time systems + are not yet supported by most functions.) Parameters ---------- 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