Skip to content

Commit 44de990

Browse files
committed
update config module to use dictionary + module.variable format
1 parent b5aaf4a commit 44de990

6 files changed

Lines changed: 136 additions & 101 deletions

File tree

control/config.py

Lines changed: 40 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,58 +5,66 @@
55
# variables that control the behavior of the control package.
66
# Eventually it will be possible to read and write configuration
77
# files. For now, you can just choose between MATLAB and FBS default
8-
# values.
8+
# values + tweak a few other things.
99

1010
import warnings
1111

12-
# Bode plot defaults
13-
bode_dB = False # Bode plot magnitude units
14-
bode_deg = True # Bode Plot phase units
15-
bode_Hz = False # Bode plot frequency units
16-
bode_number_of_samples = None # Bode plot number of samples
17-
bode_feature_periphery_decade = 1.0 # Bode plot feature periphery in decades
12+
__all__ = ['reset_defaults', 'use_matlab_defaults', 'use_fbs_defaults',
13+
'use_numpy_matrix']
14+
15+
# Dictionary of default values
16+
defaults = {
17+
'bode.dB':False, 'bode.deg':True, 'bode.Hz':False, 'bode.grid':True,
18+
'freqplot.feature_periphery_decades':1, 'freqplot.number_of_samples':None,
19+
'statesp.use_numpy_matrix':True,
20+
}
1821

19-
# State space module variables
20-
_use_numpy_matrix = True # Decide whether to use numpy.marix
2122

2223
def reset_defaults():
23-
"""Reset package configuration values to their default values."""
24-
global bode_dB; bode_dB = False
25-
global bode_deg; bode_deg = True
26-
global bode_Hz; bode_Hz = False
27-
global bode_number_of_samples; bode_number_of_samples = None
28-
global bode_feature_periphery_decade; bode_feature_periphery_decade = 1.0
29-
global _use_numpy_matrix; _use_numpy_matrix = True
24+
"""Reset configuration values to their default values."""
25+
defaults['bode.dB'] = False
26+
defaults['bode.deg'] = True
27+
defaults['bode.Hz'] = False
28+
defaults['bode.grid'] = True
29+
defaults['freqplot.number_of_samples'] = None
30+
defaults['freqplot.feature_periphery_decades'] = 1.0
31+
defaults['statesp.use_numpy_matrix'] = True
3032

3133

3234
# Set defaults to match MATLAB
3335
def use_matlab_defaults():
34-
"""
35-
Use MATLAB compatible configuration settings
36+
"""Use MATLAB compatible configuration settings.
3637
3738
The following conventions are used:
38-
* Bode plots plot gain in dB, phase in degrees, frequency in Hertz
39+
* Bode plots plot gain in dB, phase in degrees, frequency in
40+
Hertz, with grids
41+
3942
"""
4043
# Bode plot defaults
41-
global bode_dB; bode_dB = True
42-
global bode_deg; bode_deg = True
43-
global bode_Hz; bode_Hz = True
44-
global _use_numpy_matrix; _use_numpy_matrix = True
44+
from .freqplot import bode_plot
45+
defaults['bode.dB'] = True
46+
defaults['bode.deg'] = True
47+
defaults['bode.Hz'] = True
48+
defaults['bode.grid'] = True
49+
defaults['statesp.use_numpy_matrix'] = True
4550

4651

4752
# Set defaults to match FBS (Astrom and Murray)
4853
def use_fbs_defaults():
49-
"""
50-
Use `Feedback Systems <http://fbsbook.org>`_ (FBS) compatible settings
54+
"""Use `Feedback Systems <http://fbsbook.org>`_ (FBS) compatible settings.
5155
5256
The following conventions are used:
57+
5358
* Bode plots plot gain in powers of ten, phase in degrees,
54-
frequency in Hertz
59+
frequency in Hertz, no grid
60+
5561
"""
5662
# Bode plot defaults
57-
global bode_dB; bode_dB = False
58-
global bode_deg; bode_deg = True
59-
global bode_Hz; bode_Hz = False
63+
from .freqplot import bode_plot
64+
defaults['bode.dB'] = False
65+
defaults['bode.deg'] = True
66+
defaults['bode.Hz'] = False
67+
defaults['bode.grid'] = False
6068

6169

6270
# Decide whether to use numpy.matrix for state space operations
@@ -68,8 +76,8 @@ def use_numpy_matrix(flag=True, warn=True):
6876
flag : bool
6977
If flag is `True` (default), use the Numpy (soon to be deprecated)
7078
`matrix` class to represent matrices in the `~control.StateSpace`
71-
class and functions. If flat is `False`, then matrices are represnted
72-
by a 2D `ndarray` object.
79+
class and functions. If flat is `False`, then matrices are
80+
represented by a 2D `ndarray` object.
7381
7482
warn : bool
7583
If flag is `True` (default), issue a warning when turning on the use
@@ -80,4 +88,4 @@ class and functions. If flat is `False`, then matrices are represnted
8088
if flag and warn:
8189
warnings.warn("Return type numpy.matrix is soon to be deprecated.",
8290
stacklevel=2)
83-
global _use_numpy_matrix; _use_numpy_matrix = flag
91+
defaults['statesp.use_numpy_matrix'] = flag

control/freqplot.py

Lines changed: 41 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
from .ctrlutil import unwrap
4949
from .bdalg import feedback
5050
from .margins import stability_margins
51+
from .config import defaults
5152

5253
__all__ = ['bode_plot', 'nyquist_plot', 'gangof4_plot',
5354
'bode', 'nyquist', 'gangof4']
@@ -62,10 +63,10 @@
6263
# Bode plot
6364

6465

65-
def bode_plot(syslist, omega=None, dB=None, Hz=None, deg=None,
66-
Plot=True, omega_limits=None, omega_num=None, margins=None, *args, **kwargs):
67-
"""
68-
Bode plot for a system
66+
def bode_plot(syslist, omega=None,
67+
Plot=True, omega_limits=None, omega_num=None,
68+
margins=None, *args, **kwargs):
69+
"""Bode plot for a system
6970
7071
Plots a Bode plot for the system over a (optional) frequency range.
7172
@@ -76,18 +77,21 @@ def bode_plot(syslist, omega=None, dB=None, Hz=None, deg=None,
7677
omega : list
7778
List of frequencies in rad/sec to be used for frequency response
7879
dB : boolean
79-
If True, plot result in dB
80+
If True, plot result in dB. Defaults to config.defaults['bode.dB'].
8081
Hz : boolean
81-
If True, plot frequency in Hz (omega must be provided in rad/sec)
82+
If True, plot frequency in Hz (omega must be provided in rad/sec).
83+
Defaults to config.defaults['bode.Hz']
8284
deg : boolean
83-
If True, plot phase in degrees (else radians)
85+
If True, plot phase in degrees (else radians). Defaults to
86+
config.defaults['bode.deg']
8487
Plot : boolean
8588
If True, plot magnitude and phase
8689
omega_limits: tuple, list, ... of two values
8790
Limits of the to generate frequency vector.
8891
If Hz=True the limits are in Hz otherwise in rad/s.
8992
omega_num: int
90-
number of samples
93+
Number of samples to plot. Defaults to
94+
config.defaults['freqplot.number_of_samples'].
9195
margins : boolean
9296
If True, plot gain and phase margin
9397
\*args, \**kwargs:
@@ -102,6 +106,12 @@ def bode_plot(syslist, omega=None, dB=None, Hz=None, deg=None,
102106
omega : array (list if len(syslist) > 1)
103107
frequency in rad/sec
104108
109+
Other Parameters
110+
----------------
111+
grid : bool
112+
If True, plot grid lines on gain and phase plots. Default is set by
113+
config.defaults['bode.grid'].
114+
105115
Notes
106116
-----
107117
1. Alternatively, you may use the lower-level method (mag, phase, freq)
@@ -117,15 +127,13 @@ def bode_plot(syslist, omega=None, dB=None, Hz=None, deg=None,
117127
--------
118128
>>> sys = ss("1. -2; 3. -4", "5.; 7", "6. 8", "9.")
119129
>>> mag, phase, omega = bode(sys)
130+
120131
"""
121-
# Set default values for options
122-
from . import config
123-
if dB is None:
124-
dB = config.bode_dB
125-
if deg is None:
126-
deg = config.bode_deg
127-
if Hz is None:
128-
Hz = config.bode_Hz
132+
# Set default values for options (and pop from list to allow use in plots)
133+
dB = kwargs.pop('dB', defaults.get('bode.dB', False))
134+
deg = kwargs.pop('deg', defaults.get('bode.deg', True))
135+
Hz = kwargs.pop('Hz', defaults.get('bode.Hz', False))
136+
grid = kwargs.pop('grid', defaults.get('bode.grid', True))
129137

130138
# If argument was a singleton, turn it into a list
131139
if not getattr(syslist, '__iter__', False):
@@ -235,7 +243,7 @@ def bode_plot(syslist, omega=None, dB=None, Hz=None, deg=None,
235243
color=pltline[0].get_color())
236244

237245
# Add a grid to the plot + labeling
238-
ax_mag.grid(False if margins else True, which='both')
246+
ax_mag.grid(grid and not margins, which='both')
239247
ax_mag.set_ylabel("Magnitude (dB)" if dB else "Magnitude")
240248

241249
# Phase plot
@@ -377,7 +385,7 @@ def gen_zero_centered_series(val_min, val_max, period):
377385
ylim[1],
378386
math.pi / 12.),
379387
minor=True)
380-
ax_phase.grid(False if margins else True, which='both')
388+
ax_phase.grid(grid and not margins, which='both')
381389
# ax_mag.grid(which='minor', alpha=0.3)
382390
# ax_mag.grid(which='major', alpha=0.9)
383391
# ax_phase.grid(which='minor', alpha=0.3)
@@ -592,7 +600,7 @@ def gangof4_plot(P, C, omega=None):
592600

593601
# Compute reasonable defaults for axes
594602
def default_frequency_range(syslist, Hz=None, number_of_samples=None,
595-
feature_periphery_decade=None):
603+
feature_periphery_decades=None):
596604
"""Compute a reasonable default frequency range for frequency
597605
domain plots.
598606
@@ -603,24 +611,18 @@ def default_frequency_range(syslist, Hz=None, number_of_samples=None,
603611
----------
604612
syslist : list of LTI
605613
List of linear input/output systems (single system is OK)
606-
607614
Hz : bool
608615
If True, the limits (first and last value) of the frequencies
609616
are set to full decades in Hz so it fits plotting with logarithmic
610617
scale in Hz otherwise in rad/s. Omega is always returned in rad/sec.
611-
612618
number_of_samples : int, optional
613-
Number of samples to generate. Defaults to ``numpy.logspace`` default
614-
value.
615-
616-
feature_periphery_decade : float, optional
619+
Number of samples to generate. The default value is read from
620+
``config.defaults['freqplot.number_of_samples']. If None, then the
621+
default from `numpy.logspace` is used.
622+
feature_periphery_decades : float, optional
617623
Defines how many decades shall be included in the frequency range on
618-
both sides of features (poles, zeros).
619-
620-
Example: If there is a feature, e.g. a pole, at 1 Hz and
621-
feature_periphery_decade=1., then the range of frequencies shall span
622-
0.1 .. 10 Hz. The default value is read from
623-
``config.bode_feature_periphery_decade``.
624+
both sides of features (poles, zeros). The default value is read from
625+
``config.defaults['freqplot.feature_periphery_decades']``.
624626
625627
Returns
626628
-------
@@ -643,9 +645,10 @@ def default_frequency_range(syslist, Hz=None, number_of_samples=None,
643645
# Set default values for options
644646
from . import config
645647
if number_of_samples is None:
646-
number_of_samples = config.bode_number_of_samples
647-
if feature_periphery_decade is None:
648-
feature_periphery_decade = config.bode_feature_periphery_decade
648+
number_of_samples = config.defaults['freqplot.number_of_samples']
649+
if feature_periphery_decades is None:
650+
feature_periphery_decades = \
651+
config.defaults['freqplot.feature_periphery_decades']
649652

650653
# Find the list of all poles and zeros in the systems
651654
features = np.array(())
@@ -693,14 +696,14 @@ def default_frequency_range(syslist, Hz=None, number_of_samples=None,
693696
if Hz:
694697
features /= 2. * math.pi
695698
features = np.log10(features)
696-
lsp_min = np.floor(np.min(features) - feature_periphery_decade)
697-
lsp_max = np.ceil(np.max(features) + feature_periphery_decade)
699+
lsp_min = np.floor(np.min(features) - feature_periphery_decades)
700+
lsp_max = np.ceil(np.max(features) + feature_periphery_decades)
698701
lsp_min += np.log10(2. * math.pi)
699702
lsp_max += np.log10(2. * math.pi)
700703
else:
701704
features = np.log10(features)
702-
lsp_min = np.floor(np.min(features) - feature_periphery_decade)
703-
lsp_max = np.ceil(np.max(features) + feature_periphery_decade)
705+
lsp_min = np.floor(np.min(features) - feature_periphery_decades)
706+
lsp_max = np.ceil(np.max(features) + feature_periphery_decades)
704707
if freq_interesting:
705708
lsp_min = min(lsp_min, np.log10(min(freq_interesting)))
706709
lsp_max = max(lsp_max, np.log10(max(freq_interesting)))

control/matlab/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@
8787
from .timeresp import *
8888
from .wrappers import *
8989

90+
# Set up defaults corresponding to MATLAB conventions
91+
from ..config import *
92+
use_matlab_defaults()
93+
9094
r"""
9195
The following tables give an overview of the module ``control.matlab``.
9296
They also show the implementation progress and the planned features of the

control/statesp.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ def _ssmatrix(data, axis=1):
8686
"""
8787
# Convert the data into an array or matrix, as configured
8888
# If data is passed as a string, use (deprecated?) matrix constructor
89-
if config._use_numpy_matrix or isinstance(data, str):
89+
if config.defaults['statesp.use_numpy_matrix'] or isinstance(data, str):
9090
arr = np.matrix(data, dtype=float)
9191
else:
9292
arr = np.array(data, dtype=float)
@@ -128,15 +128,21 @@ class StateSpace(LTI):
128128
where u is the input, y is the output, and x is the state.
129129
130130
The main data members are the A, B, C, and D matrices. The class also
131-
keeps track of the number of states (i.e., the size of A).
132-
133-
Discrete-time state space system are implemented by using the 'dt' instance
134-
variable and setting it to the sampling period. If 'dt' is not None,
135-
then it must match whenever two state space systems are combined.
131+
keeps track of the number of states (i.e., the size of A). The data
132+
format used to store state space matrices is set using the value of
133+
`config.defaults['use_numpy_matrix']`. If True (default), the state space
134+
elements are stored as `numpy.matrix` objects; otherwise they are
135+
`numpy.ndarray` objects. The :func:`~control.use_numpy_matrix` function
136+
can be used to set the storage type.
137+
138+
Discrete-time state space system are implemented by using the 'dt'
139+
instance variable and setting it to the sampling period. If 'dt' is not
140+
None, then it must match whenever two state space systems are combined.
136141
Setting dt = 0 specifies a continuous system, while leaving dt = None
137142
means the system timebase is not specified. If 'dt' is set to True, the
138-
system will be treated as a discrete time system with unspecified
139-
sampling time.
143+
system will be treated as a discrete time system with unspecified sampling
144+
time.
145+
140146
"""
141147

142148
# Allow ndarray * StateSpace to give StateSpace._rmul_() priority

0 commit comments

Comments
 (0)