/
plotting.py
253 lines (215 loc) · 7.74 KB
/
plotting.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
# -*- coding: iso-8859-1 -*-
# plotting.py
# Module to plot the simulation data (through matplotlib)
# Copyright 2009-2013 Giuseppe Venturini
# This file is part of the ahkab simulator.
#
# Ahkab is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 2 of the License.
#
# Ahkab is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License v2
# along with ahkab. If not, see <http://www.gnu.org/licenses/>.
"""
This module offers the functions needed to plot the results
of a simulation.
It is only functional if `matplotlib <http://matplotlib.org/>`_
is installed.
Module reference
''''''''''''''''
"""
from __future__ import (unicode_literals, absolute_import,
division, print_function)
import re
import numpy as np
from . import printing
from . import options
from . import py3compat
try:
import pylab
except ImportError:
# no matplotlib on a platform that supports it
if not py3compat.PYPY:
printing.print_warning('Matplotlib not found.')
def _split_netlist_label(labels_string):
labels_string = labels_string.strip().upper()
ret_labels = []
p = re.compile(r'V\s*\(\s*(\w*)\s*,\s*(\w*)\s*\)', re.IGNORECASE)
labels_list = p.findall(labels_string)
for i in range(len(labels_list)):
l2 = "V" + labels_list[i][0]
l1 = "V" + labels_list[i][1]
ret_labels.append((l2, l1))
p = re.compile(r'V\s*\(\s*(\w*)\s*\)', re.IGNORECASE)
labels_list = p.findall(labels_string)
for i in range(len(labels_list)):
l2 = "V" + labels_list[i]
l1 = None
ret_labels.append((l2, l1))
p = re.compile(r'I\s*\(\s*(\w*)\s*\)', re.IGNORECASE)
labels_list = p.findall(labels_string)
for i in range(len(labels_list)):
l2 = "I(" + labels_list[i] + ")"
l1 = None
ret_labels.append((l2, l1))
if len(ret_labels) == 0:
raise Exception("Unrecognized plot labels: " + labels_string)
return ret_labels
def _setup_plot(fig, title, xvu, yvu, log=False, xlog=False, ylog=False):
"""Setup the figure for plotting.
**Parameters:**
fig : figure
A matplotlib figure
title : string
The plot title:
xvu : tuple
A tuple defined as ``xvu = (xvar, xunit)``, where ``xvar`` is the
x-axis variable label (str) and ``xunit`` is its unit (str too).
yvu : list of tuples
defined as yvu += [
Each tuple defined as ``(yvar, yunit)``, where ``yvarN`` is the
y-axis variable label (str) and ``yunit`` is its unit (str too).
log : bool, optional
Whether to set all scales to ``log``.
xlog : bool, optional
Whether to set the x-axis scale to ``log``.
ylog : bool, optional
Whether to set the y-axis scale to ``log``.
"""
# xvar, xunit = xvu
pylab.title(title.upper())
ax = pylab.gca()
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')
ax.xaxis.grid(False)
ax.yaxis.grid(False)
if log or xlog:
ax.set_xscale('log')
pylab.xlabel('%s [%s]' % xvu)
yunits = []
yinitials = []
for yv, yu in yvu:
yv = yv[:].replace('|', "")
if yu not in yunits:
yunits.append(yu)
yinitials.append(yv[0])
ylabel = ""
for yi, yu in zip(yinitials, yunits):
ylabel += "%s [%s] , " % (yi, yu)
ylabel = ylabel[:-3]
pylab.ylabel(ylabel)
if log or ylog:
ax.set_yscale('log')
# fig.tight_layout()
def save_figure(filename, fig=None):
"""Apply the figure options for saving and then save the supplied
figure to ``filename``.
The format of the output figure is set by ``options.plotting_outtype``.
**Parameters:**
filename : string
The output filename.
fig : figure object, optional
The figure to be saved.
**Returns:**
``None``.
"""
if fig is None:
fig = pylab.gcf()
fig.set_size_inches(*options.plotting_save_figsize)
pylab.savefig(filename, dpi=100, bbox_inches='tight',
format=options.plotting_outtype, pad=0.1)
fig.set_size_inches(*options.plotting_display_figsize)
def _data_abs_arg_pass(res, label):
# extract abs / phase if needed or pass the data
if label[0] == label[-1] == '|':
data = np.absolute(res[label[1:-1]])
units = res.units[label[1:-1]]
elif label[0:4] == 'arg(' and label[-1] == ')':
data = np.angle(res[label[4:-1]], deg=options.ac_phase_in_deg)
units = res.units[label[4:-1]]
else:
data = res[label]
units = res.units[label]
return data, units
def plot_results(title, y2y1_list, results, outfilename=None):
"""Plot the results.
**Parameters:**
title : string
The plot title
y2y1_list : list
A list of tuples. Each tuple has to be in the format ``(y2, y1)``.
Each member of the tuple has to be a valid identifier. You can
check the possible voltage and current identifiers in the
result set calling ``res.keys()``, where ``res`` is a solution
object.
result : solution object or derivate
The results to be plotted.
outfilename : string, optional
The filename of the output file. If left unset, the plot will
not be written to disk. The format is set through
``options.plotting_outtype``.
**Returns:**
``None``.
"""
if results is None:
printing.print_warning("No results available for plotting. Skipping.")
return
fig = pylab.figure(figsize=options.plotting_display_figsize)
analysis = results.sol_type.upper()
gdata = []
x, xlabel = results.get_x(), results.get_xlabel()
xunit = results.units[xlabel]
yvu = []
for y2label, y1label in y2y1_list:
if y1label is not None and y1label != '':
try:
data1, _ = _data_abs_arg_pass(results, y1label)
except ValueError as e:
printing.print_warning(str(e) + " " + y1label)
continue
line_label = y2label + "-" + y1label
else:
line_label = y2label
data1 = 0
try:
data2, units = _data_abs_arg_pass(results, y2label)
except ValueError as e:
printing.print_warning(str(e) + " " + y2label)
continue
yvu += [(line_label, units)]
gdata.append((data2 - data1, line_label))
if xlabel == 'w':
xlog = True
else:
xlog = False
_setup_plot(fig, title, (xlabel, xunit), yvu, xlog=xlog)
ms = 7./(1. + max(np.log(len(x)/100.), 0))
ms = ms if ms > 2 else 0.
pylab.hold(True)
ymax, ymin = None, None
for y, label in gdata:
[line] = pylab.plot(
x, y, options.plotting_style, label=label +
" (" + analysis + ")", ms=ms,
mfc='w', lw=options.plotting_lw, mew=options.plotting_lw)
line.set_mec(line.get_color()) # nice empty circles
ymax = y.max() if ymax is None or y.max() > ymax else ymax
ymin = y.min() if ymin is None or y.min() < ymin else ymin
pylab.xlim((x.min(), x.max()))
pylab.ylim((ymin - (ymax - ymin) * .01, ymax + (ymax - ymin) * .01))
pylab.grid(True)
pylab.hold(False)
pylab.legend()
if outfilename is not None and options.plotting_outtype is not None:
save_figure(outfilename, fig)
return
def show_plots():
"""See the fruit of your work!"""
pylab.show()