forked from 1iyiwei/topopt
-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathplotting.py
192 lines (162 loc) · 6.81 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
"""
Plotting the simulated TopOpt geometry with boundery conditions and loads.
Bram Lagerweij
Aerospace Structures and Materials Department TU Delft
2018
"""
import matplotlib.pyplot as plt
import matplotlib.animation as anim
import numpy as np
class Plot(object):
"""
This class contains functions that allows the visualisation of the TopOpt
algorithem. It can print the density distribution, the boundary conditions
and the forces.
Parameters
----------
load : object, child of the Loads class
The loadcase(s) considerd for this optimisation problem.
title : str, optional
Title of the plot if required.
Attributes
---------
nelx : int
Number of elements in x direction.
nely : int
Number of elements in y direction.
fig : matplotlib.pyplot figure
An empty figure of size nelx/10 and nely/10*1.2 inch.
ax : matplotlib.pyplot axis
The axis system that belongs to fig.
images : 1-D list with imshow objects
This list contains all density distributions that need to be plotted.
"""
def __init__(self, load, title=None):
# turning off the interactive plotting of matplotlib.pyplot
plt.ioff()
self.nelx = load.nelx
self.nely = load.nely
self.fig = plt.figure()
xsize = 100*load.nelx/1920
ysize = 100*load.nely/1080
schale = max(xsize, ysize)
self.fig.set_size_inches(load.nelx/schale, load.nely/schale)
self.ax = self.fig.add_axes([0.05, 0.05, 0.9, 0.8], frameon=False, aspect=1)
self.ax.set_xticks([])
self.ax.set_yticks([])
if title is not None:
self.fig.suptitle(title)
self.images = []
def add(self, x, animated=False):
"""
Adding a plot of the density distribution to the figure.
Parameters
----------
x : 2-D array size(nely, nelx)
The density distribution.
animated : bool, optional
An animated figure is genereted when history = True.
"""
if animated is False:
self.images = []
x = x.astype(np.float32)
plt_im = plt.imshow(1-x, vmin=0, vmax=1, cmap=plt.cm.gray, animated=animated)
self.images.append([plt_im])
def boundary(self, load):
"""
Plotting the boundary conditions.
Parameters
----------
load : object, child of the Loads class
The loadcase(s) considerd for this optimisation problem.
"""
wedgepropsH = dict(arrowstyle="wedge, tail_width=1.", color='g')
wedgepropsV = dict(arrowstyle="wedge, tail_width=1.", color='b')
for i in load.fixdofs():
if i % load.dim == 0:
node = int((i)/load.dim)
nodex = int(node/(self.nely + 1))-0.5
nodey = node % (self.nely + 1)-0.5
self.ax.annotate('', xy=(nodex, nodey), xytext=(-15, 0),
textcoords='offset points', arrowprops=wedgepropsH)
if i % load.dim == 1:
node = int((i)/load.dim)
nodex = int(node/(self.nely + 1))-0.5
nodey = node % (self.nely + 1)-0.5
self.ax.annotate('', xy=(nodex, nodey), xytext=(0, -15),
textcoords='offset points', arrowprops=wedgepropsV)
def loading(self, load):
"""
Plotting the loading conditions.
Parameters
----------
load : object, child of the Loads class
The loadcase(s) considerd for this optimisation problem.
"""
arrowprops = dict(arrowstyle="simple",fc="r", ec="r", mutation_scale=20)
forceloc = np.nonzero(load.force())[0]
for i in forceloc:
force = load.force()[i]
if i % load.dim == 0:
node = int((i)/load.dim)
nodex = int(node/(self.nely + 1))-0.5
nodey = node % (self.nely + 1)-0.5
self.ax.annotate('', xy=(nodex, nodey), xytext=(-60*force, 0),
textcoords='offset points', arrowprops=arrowprops)
if i % load.dim == 1:
node = int((i)/load.dim)
nodex = int(node/(self.nely + 1))-0.5
nodey = node % (self.nely + 1)-0.5
self.ax.annotate('', xy=(nodex, nodey), xytext=(0, -60*force),
textcoords='offset points', arrowprops=arrowprops)
def save(self, filename, fps=10):
"""
Saving an plot in svg or mp4 format, depending on the length of the
images list. The FasterFFMpegWriter is used when videos are generated.
These videos are encoded with a hardware accelerated h264 codec with
the .mp4 file format. Other codecs and encoders can be set within the
function itself.
Parameters
----------
filename : str
Name of the file, excluding the file exstension.
fps : int, optional
Amount of frames per second if the plots are animations.
"""
if len(self.images) == 1:
self.fig.savefig(filename+'.svg')
else:
writer = anim.FFMpegWriter(fps=30, codec='h264')
animation = anim.ArtistAnimation(self.fig, self.images, interval=1, blit=True, repeat=False)
animation.save(filename+'.mp4', writer=writer)
def show(self):
"""
Showing the plot in a window.
"""
self.fig.show()
class FasterFFMpegWriter(anim.FFMpegWriter):
'''FFMpeg-pipe writer bypassing figure.savefig. To improofs speed with
respect to the matplotlib.animation.FFMpegWriter'''
def __init__(self, **kwargs):
'''Initialize the Writer object and sets the default frame_format.'''
super().__init__(**kwargs)
self.frame_format = 'argb'
def grab_frame(self, **savefig_kwargs):
'''
Grab the image information from the figure and save as a movie frame.
Doesn't use savefig to be faster: savefig_kwargs will be ignored.
'''
try:
# re-adjust the figure size and dpi in case it has been changed by
# the user. We must ensure that every frame is the same size or
# the movie will not save correctly.
self.fig.set_size_inches(self._w, self._h)
self.fig.set_dpi(self.dpi)
# Draw and save the frame as an argb string to the pipe sink
self.fig.canvas.draw()
self._frame_sink().write(self.fig.canvas.tostring_argb())
except (RuntimeError, IOError) as e:
out, err = self._proc.communicate()
raise IOError('Error saving animation to file (cause: {0}) '
'Stdout: {1} StdError: {2}. It may help to re-run '
'with --verbose-debug.'.format(e, out, err))