import numpy as np from matplotlib import pyplot as plt from matplotlib import animation import time class Fdtd1DAnimation(animation.TimedAnimation): '''Animation of the 1D FDTD fields. Based on https://matplotlib.org/examples/animation/subplots.html Arguments --------- x : 1d-array Spatial coordinates t : 1d-array Time x_interface : float Position of the interface (default: None) step : float Time step between frames (default: 2e-15/25) fps : int Frames per second (default: 25) Ez: 2d-array Ez field to animate (each row corresponds to one time step) Hy: 2d-array Hy field to animate (each row corresponds to one time step) ''' def __init__(self, x, t, Ez, Hy, x_interface=None, step=2e-15/25, fps=25): # constants c = 2.99792458e8 # speed of light [m/s] mu0 = 4*np.pi*1e-7 # vacuum permeability [Vs/(Am)] eps0 = 1/(mu0*c**2) # vacuum permittivity [As/(Vm)] Z0 = np.sqrt(mu0/eps0) # vacuum impedance [Ohm] self.Ez = Ez self.Z0Hy = Z0*Hy self.x = x self.ct = c*t # index step between consecutive frames self.frame_step = int(round(step/(t[1] - t[0]))) # set up initial plot colors = plt.rcParams['axes.prop_cycle'].by_key()['color'] vmax = max(np.max(np.abs(Ez)),np.max(np.abs(Hy))*Z0)*1e6 fig, ax = plt.subplots(2,1, sharex=True, gridspec_kw={'hspace': 0.4}) self.line_E, = ax[0].plot(x*1e6, self.E_at_step(0), color=colors[0], label='$\\Re\\{E_z\\}$') self.line_H, = ax[1].plot(x*1e6, self.H_at_step(0), color=colors[1], label='$Z_0\\Re\\{H_y\\}$') if x_interface is not None: for a in ax: a.axvline(x_interface*1e6, ls='--', color='k') for a in ax: a.set_xlim(x[[0,-1]]*1e6) a.set_ylim(np.array([-1.1, 1.1])*vmax) ax[0].set_ylabel('$\\Re\\{E_z\\}$ [µV/m]') ax[1].set_ylabel('$Z_0\\Re\\{H_y\\}$ [µV/m]') self.text_E = ax[0].set_title('') self.text_H = ax[1].set_title('') ax[1].set_xlabel('$x$ [µm]') super().__init__(fig, interval=1000/fps, blit=False) def E_at_step(self, n): return self.Ez[n,:].real*1e6 def H_at_step(self, n): return self.Z0Hy[n,:].real*1e6 def new_frame_seq(self): return iter(range(0, self.ct.size, self.frame_step)) def _init_draw(self): self.line_E.set_ydata(self.x*np.nan) self.line_H.set_ydata(self.x*np.nan) self.text_E.set_text('') self.text_E.set_text('') def _draw_frame(self, framedata): i = framedata self.line_E.set_ydata(self.E_at_step(i)) self.line_H.set_ydata(self.H_at_step(i)) self.text_E.set_text( 'Electric field, $ct = {0:1.2f}$µm'.format(self.ct[i]*1e6)) self.text_H.set_text( 'Magnetic field, $ct = {0:1.2f}$µm'.format(self.ct[i]*1e6)) self._drawn_artists = [self.line_E, self.line_H, self.text_E, self.text_H] class Fdtd3DAnimation(animation.TimedAnimation): '''Animation of a 3D FDTD field. Based on https://matplotlib.org/examples/animation/subplots.html Arguments --------- x, y : 1d-array Coordinate axes. t : 1d-array Time field: 3d-array Slices of the field to animate (the time axis is assumed to be be the first axis of the array) titlestr : str Plot title. cb_label : str Colrbar label. rel_color_range: float Range of the colormap relative to the full scale of the field magnitude. fps : int Frames per second (default: 25) ''' def __init__(self, x, y, t, field, titlestr, cb_label, rel_color_range, fps=25): # constants c = 2.99792458e8 # speed of light [m/s] self.ct = c*t self.fig = plt.figure() self.F = field color_range = rel_color_range*np.max(np.abs(field)) phw = 0.5*(x[1] - x[0]) # pixel half-width extent = ((x[0] - phw)*1e6, (x[-1] + phw)*1e6, (y[-1] + phw)*1e6, (y[0] - phw)*1e6) self.mapable = plt.imshow(self.F[0,:,:].real.T, vmin=-color_range, vmax=color_range, extent=extent) cb = plt.colorbar(self.mapable) plt.gca().invert_yaxis() self.titlestr = titlestr self.text = plt.title('') plt.xlabel('x position [µm]') plt.ylabel('y position [µm]') cb.set_label(cb_label) super().__init__(self.fig, interval=1000/fps, blit=False) def new_frame_seq(self): return iter(range(self.ct.size)) def _init_draw(self): self.mapable.set_array(np.nan*self.F[0, :, :].real.T) self.text.set_text('') def _draw_frame(self, framedata): i = framedata self.mapable.set_array(self.F[i, :, :].real.T) self.text.set_text(self.titlestr + ', $ct$ = {0:1.2f}µm'.format(self.ct[i]*1e6)) self._drawn_artists = [self.mapable, self.text]