def plot_root(self, ax=None, color='red', size=8): 'plot calculated root' # convenient variables m, n, c, root = self.m, self.n, self.c, self.root # if no axis is given, make a new plot if ax is None: fig = plt.figure() ax = fig.add_subplot(111) # text label for root point label = ['$\chi_{' + str(n) + '}$'] # plot roots f = lambda x: self.root_equation(m, c, x) root_line, = ax.plot(root, f(root), marker='o', markerfacecolor=color, markersize=size) # make root draggable self.drag = DragRoot(root_line, f, label=label) # draw the root self.drag.draw()
class TMmode: '''Contain a single TM wave guide mode, and methods to calculate important quantities''' def __init__(self,m,n,c): # make sure values are valid if m<0: raise NotGreaterThenZero, 'm must be non-negative' if n<1: raise NotGreaterThenOrEqualToOne, 'n must be >= 1' if c<=1: raise NotGreaterThenOne, 'c must be greater then 1' # save values to class variables self.mode = 'TM' self.m = m # Bessel function order self.n = n # Root number of Bessel function self.c = c # Ratio of outer to inner radius self.root = 0 # root of equation self.kz = 0 # z component of the wavenumber k self.set_root() # sets initial root to something reasonable self.drag = None # information to drag root in plot self.E_field = None # The quiver / arrow plot of the Electric field self.H_field = None # " " Magnetic field def __str__(self): return "<%s mode> m = %s, n = %s, c = %s" % (self.mode, self.m, self.n, self.c) def root_equation(self,m,c,x): 'Radial root equation of Phi for TM mode' return yn(m,x)*jn(m,c*x)-jn(m,x)*yn(m,c*x) def z(self,x): 'Z(x) equation that keeps showing up in waveguide modes' m, n, chi = self.m, self.n, self.root return yn(m,chi)*jn(m,x)-jn(m,chi)*yn(m,x) def z_dash(self,x): ''' Z'(x) equation for waveguide mode ''' m, n, chi = self.m, self.n, self.root # jvp(m,x,r) is the rth derivative of the bessel function of order m evaluated at x return yn(m,chi)*jvp(m,x,1)-jn(m,chi)*yvp(m,x,1) def update_kz(self): ''' wave number (2 pi lambda)^-1 in z direction ''' # In plots we will take inner radius (b) to be 1 # kz^2 = k^2-(chi/b)^2 = k^2-chi^2 chi = self.root self.kz = sqrt(K**2 - chi**2) def E_rho(self, rho, phi): ''' radial component of electric field evaluated at (rho,phi) - polar coordinates ''' m, chi, kz = self.m, self.root, self.kz return -1*kz*chi*self.z_dash(chi*rho)*cos(m*phi) def E_phi(self, rho, phi): ''' polar component of electric field evaluated at (rho,phi) - polar coordinates ''' m, chi, kz = self.m, self.root, self.kz return kz*m/rho*self.z(chi*rho)*sin(m*phi) def E_z(self, rho, phi): ''' Z component of electric field ''' m, chi, kz = self.m, self.root, self.kz return chi**2*self.z(chi*rho)*cos(m*phi) def H_rho(self, rho, phi): ''' radial component of magnetic field ''' m, chi = self.m, self.root return -1*OMEGA*EPSILON*m/rho*self.z(chi*rho)*sin(m*phi) def H_phi(self, rho, phi): ''' polar component of magnetic field ''' m, chi = self.m, self.root return -1*OMEGA*EPSILON*chi*self.z_dash(chi*rho)*cos(m*phi) def H_z(self, rho, phi): ''' z component of magnetic field ''' # need to return a numpy array of zeros, not just a single int 0 incase # rho and phi are meshgrid arrays return zeros(rho.shape) def guess_root(self,m,n,c): 'Guess the root chi_mn for TM mode' return pi*n/(c-1.) def set_root(self, guess=None): 'Set the initial guess value for the root' if guess is not None: self.root = guess # If no guess is provided use the Marcuvitz formula to calculate approximate root else: self.root = self.guess_root(self.m, self.n, self.c) # update the kz now that we have a new root self.update_kz() def find_root(self): 'find root using Newton method' f = lambda x: self.root_equation(self.m,self.c,x) try: self.root = newton(f, self.root, maxiter=100) except RuntimeError: pass # update the kz self.update_kz() def marcuvitz(self): 'returns a string label and value for the root form tabulated in Marcuvitz' label = '(c-1)*chi' root = (self.c-1.)*self.root return label, root def plot_root(self, ax=None, color='red', size=8): 'plot calculated root' # convenient variables m, n, c, root = self.m, self.n, self.c, self.root # if no axis is given, make a new plot if ax is None: fig = plt.figure() ax = fig.add_subplot(111) # text label for root point label = ['$\chi_{' + str(n) + '}$'] # plot roots f = lambda x: self.root_equation(m, c, x) root_line, = ax.plot(root, f(root), marker='o', markerfacecolor=color, markersize=size) # make root draggable self.drag = DragRoot(root_line, f, label=label) # draw the root self.drag.draw() def recalculate_root(self): ''' from the root plot retrieve the estimated root and recalculate it using Newton-Raphson method of finding zeros ''' if self.drag is None: return new_guess = self.drag.get_xdata() self.set_root(guess=new_guess) self.find_root() # set new xdata in the plot new_x = self.root new_y = self.root_equation(self.m, self.c, new_x) self.drag.set_xdata(new_x) self.drag.set_ydata(new_y) # draw on this figure now self.drag.draw() def plot_root_equation(self, ax=None, Npoints=400): ''' Plot the radial root equation ''' # if no axis is given, make a new plot if ax is None: fig = plt.figure() ax = fig.add_subplot(111) # give a horizontal line indicating zero ax.axhline(0,0, linewidth=1, linestyle='dashed', color='black') # set up root function plot to have zoom function f = lambda x: self.root_equation(self.m, self.c, x) self.rootplot = RootZoomPlot(f, axis=ax, Npoints=Npoints, x_min=0, x_max=2*self.root) # set the title for the new plot ax.set_title('Radial root equation for %s$_{%d,%d}$ mode (c = %.2f)' %(self.mode, self.m, self.n, self.c)) # plot the root function (plot last so as to keep pretty y range) self.rootplot.plot() def get_field_plot_title(self): ''' the title given to the vector field plot ''' return '%s %i,%i mode'%(self.mode, self.m, self.n) def plot_field(self, ax, E_color='blue', H_color='orange', axis_bgcolor='white', fig_facecolor='gray', n_rho=15, n_phi=60): ''' plots H field into ax (matplotlib.Axes class) n_rho = number of different rho(radial) points to use n_phi = number of different phi(polar angle) points to use''' # if no axis is given, make a new plot if ax is None: fig = plt.figure() else: ''' if a figure is already given need to clear it and make sure it's polar projection ''' fig = ax.figure fig.clear() fig.set_facecolor(fig_facecolor) ax = fig.add_subplot(111, projection='polar', axis_bgcolor=axis_bgcolor) ax.set_title(self.get_field_plot_title()) b = 1 # inner radius a = b*self.c # outer radius rho = linspace(b,a,n_rho) phi = linspace(0,2*pi,n_phi) # meshgrid form of rho and phi RHO, PHI = meshgrid(rho, phi) # plot the centre circle of the annulus circle_N = 100 circle_phi = linspace(0,2*pi,circle_N) circle_inner_radius = circle_N*[b] circle_outer_radius = circle_N*[a] circle_centre = circle_N*[0] ax.fill_between(circle_phi, circle_centre, circle_inner_radius, facecolor=fig_facecolor, alpha=1.0, linewidth=0) ax.plot(circle_phi, circle_inner_radius, circle_phi, circle_outer_radius, linewidth=2, color='black') # Vector field in rho, phi basis H_rho = self.H_rho H_phi = self.H_phi E_rho = self.E_rho E_phi = self.E_phi # vector field in Cartesian x,y basis, calculated from rho, phi basis E_x = E_rho(RHO,PHI)*cos(PHI)-E_phi(RHO,PHI)*sin(PHI) E_y = E_rho(RHO,PHI)*sin(PHI)+E_phi(RHO,PHI)*cos(PHI) H_x = H_rho(RHO,PHI)*cos(PHI)-H_phi(RHO,PHI)*sin(PHI) H_y = H_rho(RHO,PHI)*sin(PHI)+H_phi(RHO,PHI)*cos(PHI) # make the field plots self.E_field = ax.quiver(PHI,RHO,E_x,E_y, color=E_color) self.H_field = ax.quiver(PHI,RHO,H_x,H_y, color=H_color) # get rid of the radial and polar ticks ax.set_thetagrids([]), ax.set_rticks([])