Skip to content

Commit 65e051f

Browse files
committed
create function to copy system names, move default name parameters to namedio, update sys.sample to enable signal names to be passed.
1 parent 1fd68c7 commit 65e051f

5 files changed

Lines changed: 132 additions & 53 deletions

File tree

control/config.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ def reset_defaults():
9797
from .rlocus import _rlocus_defaults
9898
defaults.update(_rlocus_defaults)
9999

100+
from .namedio import _namedio_defaults
101+
defaults.update(_namedio_defaults)
102+
100103
from .xferfcn import _xferfcn_defaults
101104
defaults.update(_xferfcn_defaults)
102105

control/iosys.py

Lines changed: 18 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,7 @@
4747
'interconnect', 'summing_junction']
4848

4949
# Define module default parameter values
50-
_iosys_defaults = {
51-
'iosys.state_name_delim': '_',
52-
'iosys.duplicate_system_name_prefix': '',
53-
'iosys.duplicate_system_name_suffix': '$copy',
54-
'iosys.linearized_system_name_prefix': '',
55-
'iosys.linearized_system_name_suffix': '$linearized'
56-
}
50+
_iosys_defaults = {}
5751

5852

5953
class InputOutputSystem(NamedIOSystem):
@@ -515,7 +509,7 @@ def feedback(self, other=1, sign=-1, params=None):
515509
return newsys
516510

517511
def linearize(self, x0, u0, t=0, params=None, eps=1e-6,
518-
name=None, copy=False, **kwargs):
512+
name=None, copy_names=False, **kwargs):
519513
"""Linearize an input/output system at a given state and input.
520514
521515
Return the linearization of an input/output system at a given state
@@ -574,20 +568,14 @@ def linearize(self, x0, u0, t=0, params=None, eps=1e-6,
574568
StateSpace(A, B, C, D, self.dt, remove_useless_states=False),
575569
name=name, **kwargs)
576570

577-
# Set the names the system, inputs, outputs, and states
578-
if copy:
571+
# Set the system name, inputs, outputs, and states
572+
if copy_names:
579573
if name is None:
580-
linsys.name = \
581-
config.defaults['iosys.linearized_system_name_prefix'] + \
574+
name = \
575+
config.defaults['namedio.linearized_system_name_prefix'] +\
582576
self.name + \
583-
config.defaults['iosys.linearized_system_name_suffix']
584-
linsys.ninputs, linsys.input_index = self.ninputs, \
585-
self.input_index.copy()
586-
linsys.noutputs, linsys.output_index = \
587-
self.noutputs, self.output_index.copy()
588-
linsys.nstates, linsys.state_index = \
589-
self.nstates, self.state_index.copy()
590-
577+
config.defaults['namedio.linearized_system_name_suffix']
578+
linsys._copy_names(self, name=name)
591579
return linsys
592580

593581

@@ -966,7 +954,7 @@ def __init__(self, syslist, connections=None, inplist=None, outlist=None,
966954

967955
if states is None:
968956
states = []
969-
state_name_delim = config.defaults['iosys.state_name_delim']
957+
state_name_delim = config.defaults['namedio.state_name_delim']
970958
for sys, sysname in sysobj_name_dct.items():
971959
states += [sysname + state_name_delim +
972960
statename for statename in sys.state_index.keys()]
@@ -2192,18 +2180,18 @@ def linearize(sys, xeq, ueq=None, t=0, params=None, **kw):
21922180
params : dict, optional
21932181
Parameter values for the systems. Passed to the evaluation functions
21942182
for the system as default values, overriding internal defaults.
2195-
copy : bool, Optional
2196-
If `copy` is True, copy the names of the input signals, output signals,
2197-
and states to the linearized system. If `name` is not specified,
2198-
the system name is set to the input system name with the string
2199-
'_linearized' appended.
2183+
copy_names : bool, Optional
2184+
If `copy_names` is True, copy the names of the input signals, output
2185+
signals, and states to the linearized system. If `name` is not
2186+
specified, the system name is set to the input system name with the
2187+
string '_linearized' appended.
22002188
name : string, optional
22012189
Set the name of the linearized system. If not specified and
22022190
if `copy` is `False`, a generic name <sys[id]> is generated
22032191
with a unique integer id. If `copy` is `True`, the new system
22042192
name is determined by adding the prefix and suffix strings in
2205-
config.defaults['iosys.linearized_system_name_prefix'] and
2206-
config.defaults['iosys.linearized_system_name_suffix'], with the
2193+
config.defaults['namedio.linearized_system_name_prefix'] and
2194+
config.defaults['namedio.linearized_system_name_suffix'], with the
22072195
default being to add the suffix '$linearized'.
22082196
22092197
Returns
@@ -2728,8 +2716,8 @@ def interconnect(syslist, connections=None, inplist=None, outlist=None,
27282716
If a system is duplicated in the list of systems to be connected,
27292717
a warning is generated and a copy of the system is created with the
27302718
name of the new system determined by adding the prefix and suffix
2731-
strings in config.defaults['iosys.linearized_system_name_prefix']
2732-
and config.defaults['iosys.linearized_system_name_suffix'], with the
2719+
strings in config.defaults['namedio.linearized_system_name_prefix']
2720+
and config.defaults['namedio.linearized_system_name_suffix'], with the
27332721
default being to add the suffix '$copy'$ to the system name.
27342722
27352723
It is possible to replace lists in most of arguments with tuples instead,

control/namedio.py

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,18 @@
1212

1313
__all__ = ['issiso', 'timebase', 'common_timebase', 'timebaseEqual',
1414
'isdtime', 'isctime']
15-
15+
# Define module default parameter values
16+
_namedio_defaults = {
17+
'namedio.state_name_delim': '_',
18+
'namedio.duplicate_system_name_prefix': '',
19+
'namedio.duplicate_system_name_suffix': '$copy',
20+
'namedio.linearized_system_name_prefix': '',
21+
'namedio.linearized_system_name_suffix': '$linearized',
22+
'namedio.sampled_system_name_prefix': '',
23+
'namedio.sampled_system_name_suffix': '$sampled'
24+
}
25+
26+
1627
class NamedIOSystem(object):
1728
def __init__(
1829
self, name=None, inputs=None, outputs=None, states=None, **kwargs):
@@ -88,14 +99,26 @@ def __str__(self):
8899
def _find_signal(self, name, sigdict):
89100
return sigdict.get(name, None)
90101

102+
def _copy_names(self, sys, name=None):
103+
"""copy the signal and system name of sys. Name is given as a keyword
104+
in case a specific name (e.g. append 'linearized') is desired. """
105+
if name is None:
106+
self.name = sys.name
107+
self.ninputs, self.input_index = \
108+
sys.ninputs, sys.input_index.copy()
109+
self.noutputs, self.output_index = \
110+
sys.noutputs, sys.output_index.copy()
111+
self.nstates, self.state_index = \
112+
sys.nstates, sys.state_index.copy()
113+
91114
def copy(self, name=None, use_prefix_suffix=True):
92115
"""Make a copy of an input/output system
93116
94117
A copy of the system is made, with a new name. The `name` keyword
95118
can be used to specify a specific name for the system. If no name
96119
is given and `use_prefix_suffix` is True, the name is constructed
97-
by prepending config.defaults['iosys.duplicate_system_name_prefix']
98-
and appending config.defaults['iosys.duplicate_system_name_suffix'].
120+
by prepending config.defaults['namedio.duplicate_system_name_prefix']
121+
and appending config.defaults['namedio.duplicate_system_name_suffix'].
99122
Otherwise, a generic system name of the form `sys[<id>]` is used,
100123
where `<id>` is based on an internal counter.
101124
@@ -106,8 +129,8 @@ def copy(self, name=None, use_prefix_suffix=True):
106129
# Update the system name
107130
if name is None and use_prefix_suffix:
108131
# Get the default prefix and suffix to use
109-
dup_prefix = config.defaults['iosys.duplicate_system_name_prefix']
110-
dup_suffix = config.defaults['iosys.duplicate_system_name_suffix']
132+
dup_prefix = config.defaults['namedio.duplicate_system_name_prefix']
133+
dup_suffix = config.defaults['namedio.duplicate_system_name_suffix']
111134
newsys.name = self._name_or_default(
112135
dup_prefix + self.name + dup_suffix)
113136
else:

control/statesp.py

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@
6363
from .exception import ControlSlycot
6464
from .frdata import FrequencyResponseData
6565
from .lti import LTI, _process_frequency_response
66-
from .namedio import common_timebase, isdtime
67-
from .namedio import _process_namedio_keywords
66+
from .namedio import common_timebase, isdtime, _process_namedio_keywords, \
67+
_process_dt_keyword
6868
from . import config
6969
from copy import deepcopy
7070

@@ -357,9 +357,9 @@ def __init__(self, *args, init_namedio=True, **kwargs):
357357
states=states, dt=dt)
358358
elif kwargs:
359359
raise TypeError("unrecognized keyword(s): ", str(kwargs))
360-
360+
361361
# Reset shapes (may not be needed once np.matrix support is removed)
362-
if 0 == self.nstates:
362+
if self._isstatic():
363363
# static gain
364364
# matrix's default "empty" shape is 1x0
365365
A.shape = (0, 0)
@@ -1298,7 +1298,8 @@ def __getitem__(self, indices):
12981298
return StateSpace(self.A, self.B[:, j], self.C[i, :],
12991299
self.D[i, j], self.dt)
13001300

1301-
def sample(self, Ts, method='zoh', alpha=None, prewarp_frequency=None):
1301+
def sample(self, Ts, method='zoh', alpha=None, prewarp_frequency=None,
1302+
name=None, copy_names=True, **kwargs):
13021303
"""Convert a continuous time system to discrete time
13031304
13041305
Creates a discrete-time system from a continuous-time system by
@@ -1317,22 +1318,44 @@ def sample(self, Ts, method='zoh', alpha=None, prewarp_frequency=None):
13171318
alpha=0)
13181319
* backward_diff: Backwards differencing ("gbt" with alpha=1.0)
13191320
* zoh: zero-order hold (default)
1320-
13211321
alpha : float within [0, 1]
13221322
The generalized bilinear transformation weighting parameter, which
13231323
should only be specified with method="gbt", and is ignored
13241324
otherwise
1325-
13261325
prewarp_frequency : float within [0, infinity)
13271326
The frequency [rad/s] at which to match with the input continuous-
13281327
time system's magnitude and phase (the gain=1 crossover frequency,
13291328
for example). Should only be specified with method='bilinear' or
13301329
'gbt' with alpha=0.5 and ignored otherwise.
1330+
copy_names : bool, Optional
1331+
If `copy_names` is True, copy the names of the input signals, output
1332+
signals, and states to the sampled system. If `name` is not
1333+
specified, the system name is set to the input system name with the
1334+
string '_sampled' appended.
1335+
name : string, optional
1336+
Set the name of the sampled system. If not specified and
1337+
if `copy` is `False`, a generic name <sys[id]> is generated
1338+
with a unique integer id. If `copy` is `True`, the new system
1339+
name is determined by adding the prefix and suffix strings in
1340+
config.defaults['namedio.sampled_system_name_prefix'] and
1341+
config.defaults['namedio.sampled_system_name_suffix'], with the
1342+
default being to add the suffix '$sampled'.
13311343
13321344
Returns
13331345
-------
13341346
sysd : StateSpace
1335-
Discrete time system, with sampling rate Ts
1347+
Discrete-time system, with sampling rate Ts
1348+
1349+
Additional Parameters
1350+
---------------------
1351+
inputs : int, list of str or None, optional
1352+
Description of the system inputs. If not specified, the origional
1353+
system inputs are used. See :class:`InputOutputSystem` for more
1354+
information.
1355+
outputs : int, list of str or None, optional
1356+
Description of the system outputs. Same format as `inputs`.
1357+
states : int, list of str, or None, optional
1358+
Description of the system states. Same format as `inputs`.
13361359
13371360
Notes
13381361
-----
@@ -1354,10 +1377,18 @@ def sample(self, Ts, method='zoh', alpha=None, prewarp_frequency=None):
13541377
Twarp = Ts
13551378
sys = (self.A, self.B, self.C, self.D)
13561379
Ad, Bd, C, D, _ = cont2discrete(sys, Twarp, method, alpha)
1357-
# get and pass along same signal names
1358-
_, inputs, outputs, states, _ = _process_namedio_keywords(defaults=self)
1359-
return StateSpace(Ad, Bd, C, D, Ts,
1360-
inputs=inputs, outputs=outputs, states=states)
1380+
sysd = StateSpace(Ad, Bd, C, D, Ts)
1381+
# copy over the system name, inputs, outputs, and states
1382+
if copy_names:
1383+
if name is None:
1384+
name = \
1385+
config.defaults['namedio.sampled_system_name_prefix'] +\
1386+
self.name + \
1387+
config.defaults['namedio.sampled_system_name_suffix']
1388+
sysd._copy_names(self, name=name)
1389+
# pass desired signal names if names were provided
1390+
sysd = StateSpace(sysd, **kwargs)
1391+
return sysd
13611392

13621393
def dcgain(self, warn_infinite=False):
13631394
"""Return the zero-frequency gain

control/xferfcn.py

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,7 +1090,8 @@ def _common_den(self, imag_tol=None, allow_nonproper=False):
10901090

10911091
return num, den, denorder
10921092

1093-
def sample(self, Ts, method='zoh', alpha=None, prewarp_frequency=None):
1093+
def sample(self, Ts, method='zoh', alpha=None, prewarp_frequency=None,
1094+
name=None, copy_names=True, **kwargs):
10941095
"""Convert a continuous-time system to discrete time
10951096
10961097
Creates a discrete-time system from a continuous-time system by
@@ -1118,11 +1119,35 @@ def sample(self, Ts, method='zoh', alpha=None, prewarp_frequency=None):
11181119
time system's magnitude and phase (the gain=1 crossover frequency,
11191120
for example). Should only be specified with method='bilinear' or
11201121
'gbt' with alpha=0.5 and ignored otherwise.
1122+
copy_names : bool, Optional
1123+
If `copy_names` is True, copy the names of the input signals, output
1124+
signals, and states to the sampled system. If `name` is not
1125+
specified, the system name is set to the input system name with the
1126+
string '_sampled' appended.
1127+
name : string, optional
1128+
Set the name of the sampled system. If not specified and
1129+
if `copy` is `False`, a generic name <sys[id]> is generated
1130+
with a unique integer id. If `copy` is `True`, the new system
1131+
name is determined by adding the prefix and suffix strings in
1132+
config.defaults['namedio.sampled_system_name_prefix'] and
1133+
config.defaults['namedio.sampled_system_name_suffix'], with the
1134+
default being to add the suffix '$sampled'.
11211135
11221136
Returns
11231137
-------
11241138
sysd : TransferFunction system
1125-
Discrete time system, with sample period Ts
1139+
Discrete-time system, with sample period Ts
1140+
1141+
Additional Parameters
1142+
---------------------
1143+
inputs : int, list of str or None, optional
1144+
Description of the system inputs. If not specified, the origional
1145+
system inputs are used. See :class:`NamedIOSystem` for more
1146+
information.
1147+
outputs : int, list of str or None, optional
1148+
Description of the system outputs. Same format as `inputs`.
1149+
states : int, list of str, or None, optional
1150+
Description of the system states. Same format as `inputs`.
11261151
11271152
Notes
11281153
-----
@@ -1149,11 +1174,20 @@ def sample(self, Ts, method='zoh', alpha=None, prewarp_frequency=None):
11491174
else:
11501175
Twarp = Ts
11511176
numd, dend, _ = cont2discrete(sys, Twarp, method, alpha)
1152-
# get and pass along same signal names
1153-
_, inputs, outputs, _, _ = _process_namedio_keywords(defaults=self)
1154-
return TransferFunction(numd[0, :], dend, Ts,
1155-
inputs=inputs, outputs=outputs)
11561177

1178+
sysd = TransferFunction(numd[0, :], dend, Ts)
1179+
# copy over the system name, inputs, outputs, and states
1180+
if copy_names:
1181+
if name is None:
1182+
name = \
1183+
config.defaults['namedio.sampled_system_name_prefix'] +\
1184+
self.name + \
1185+
config.defaults['namedio.sampled_system_name_suffix']
1186+
sysd._copy_names(self, name=name)
1187+
# pass desired signal names if names were provided
1188+
sysd = TransferFunction(sysd, **kwargs)
1189+
return sysd
1190+
11571191
def dcgain(self, warn_infinite=False):
11581192
"""Return the zero-frequency (or DC) gain
11591193

0 commit comments

Comments
 (0)