def calc_EM_modes(self): """ Run a Fortran FEM calculation to find the optical modes. Returns a ``Simmo`` object that has these key values: Eig_values: a 1d array of Eigenvalues (propagation constants) in [1/m] sol1: the associated Eigenvectors, ie. the fields, stored as [field comp, node nu on element, Eig value, el nu] EM_mode_power: the power in the optical modes. Note this power is negative for modes travelling in the negative z-direction, eg the Stokes wave in backward SBS. """ self.d_in_m = self.structure.unitcell_x*1e-9 n_list = [] n_list_tmp = np.array([self.structure.material_bkg.n, self.structure.material_a.n, self.structure.material_b.n, self.structure.material_c.n, self.structure.material_d.n, self.structure.material_e.n, self.structure.material_f.n, self.structure.material_g.n, self.structure.material_h.n, self.structure.material_i.n, self.structure.material_j.n, self.structure.material_k.n, self.structure.material_l.n, self.structure.material_m.n, self.structure.material_n.n, self.structure.material_o.n, self.structure.material_p.n, self.structure.material_q.n, self.structure.material_r.n]) self.el_conv_table_n = {} i = 1; j = 1 for n in n_list_tmp: if n != 0: n_list.append(n) self.el_conv_table_n[i] = j j += 1 i += 1 self.n_list = np.array(n_list) n_list = None if self.structure.loss is False: self.n_list = self.n_list.real if self.num_modes < 20: self.num_modes = 20 print("Warning: ARPACK needs >= 20 modes so set num_modes=20.") # Parameters that control how FEM routine runs self.E_H_field = 1 # Selected formulation (1=E-Field, 2=H-Field) i_cond = 2 # Boundary conditions (0=Dirichlet,1=Neumann,2=unitcell_x) itermax = 30 # Maximum number of iterations for convergence EM_FEM_debug = 0 # Fortran routines will display & save add. info # Calculate where to center the Eigenmode solver around. # (Shift and invert FEM method) shift = self.n_eff**2 * self.k_0**2 if EM_FEM_debug == 1: if not os.path.exists("Normed"): os.mkdir("Normed") if not os.path.exists("Matrices"): os.mkdir("Matrices") if not os.path.exists("Output"): os.mkdir("Output") with open("../backend/fortran/msh/"+self.structure.mesh_file) as f: self.n_msh_pts, self.n_msh_el = [int(i) for i in f.readline().split()] # Size of Fortran's complex superarray (scales with mesh) int_max, cmplx_max, real_max = NumBAT.array_size(self.n_msh_el, self.num_modes) try: resm = NumBAT.calc_em_modes( self.wl_m, self.num_modes, EM_FEM_debug, self.structure.mesh_file, self.n_msh_pts, self.n_msh_el, self.structure.nb_typ_el, self.n_list, self.k_pll, self.d_in_m, shift, self.E_H_field, i_cond, itermax, self.structure.plotting_fields, self.structure.plot_real, self.structure.plot_imag, self.structure.plot_abs, cmplx_max, real_max, int_max) self.Eig_values, self.sol1, self.mode_pol, self.table_nod, \ self.type_el, self.type_nod, self.x_arr, self.ls_material = resm except KeyboardInterrupt: print("\n\n FEM routine calc_EM_modes",\ "interrupted by keyboard.\n\n") # if not self.structure.plot_field_conc: # self.mode_pol = None # if self.structure.plotting_fields != 1: # self.sol1 = None # self.n_list = None # self.E_H_field = None # self.table_nod = None # self.type_el = None # self.x_arr = None # self.n_msh_pts = None # self.n_msh_el = None if self.structure.plt_mesh: plotting.plot_msh(self.x_arr, prefix_str=self.structure.mesh_file, suffix_str='_EM') ### Calc unnormalised power in each EM mode Kokou equiv. of Eq. 8. try: nnodes = 6 if self.structure.inc_shape in self.structure.linear_element_shapes: ## Integration using analytically evaluated basis function integrals. Fast. self.EM_mode_power = NumBAT.em_mode_energy_int_v2_ez( self.k_0, self.num_modes, self.n_msh_el, self.n_msh_pts, nnodes, self.table_nod, self.x_arr, self.Eig_values, self.sol1) else: if self.structure.inc_shape not in self.structure.curvilinear_element_shapes: print("Warning: em_mode_energy_int - not sure if mesh contains curvi-linear elements", "\n using slow quadrature integration by default.\n\n") # Integration by quadrature. Slowest. self.EM_mode_power = NumBAT.em_mode_energy_int_ez( self.k_0, self.num_modes, self.n_msh_el, self.n_msh_pts, nnodes, self.table_nod, self.x_arr, self.Eig_values, self.sol1) # Bring Kokou's def into line with CW formulation. self.EM_mode_power = 2.0*self.EM_mode_power except KeyboardInterrupt: print("\n\n FEM routine EM_mode_energy_int interrupted by keyboard.\n\n") ### Calc energy (not power) in each EM mode - PRA Eq. 6. if self.calc_EM_mode_energy is True: try: nnodes = 6 # import time # start = time.time() if self.structure.inc_shape in self.structure.linear_element_shapes: # # Semi-analytic integration. Fastest! # else: # if self.structure.inc_shape not in self.structure.curvilinear_element_shapes: # print("Warning: em_mode_e_energy_int - not sure if mesh contains curvi-linear elements", # "\n using slow quadrature integration by default.\n\n") # # Integration by quadrature. Slowest. self.EM_mode_power_energy = NumBAT.em_mode_e_energy_int( self.num_modes, self.n_msh_el, self.n_msh_pts, nnodes, self.table_nod, self.type_el, self.structure.nb_typ_el, self.n_list, self.x_arr, self.sol1) except KeyboardInterrupt: print("\n\n FEM routine em_mode_e_energy_int interrupted by keyboard.\n\n") # This group velocity calc is not accurate in the presence of dispersion! # self.group_velocity_EM = self.EM_mode_power/self.EM_mode_power_energy # If considering a the backwards propagating Stokes field. if self.Stokes == True: self.Eig_values = -1*self.Eig_values self.sol1 = np.conj(self.sol1)
def calc_AC_modes(self): """ Run a Fortran FEM calculation to find the acoustic modes. Returns a ``Simmo`` object that has these key values: Eig_values: a 1d array of Eigenvalues (frequencies) in [1/s] sol1: the associated Eigenvectors, ie. the fields, stored as [field comp, node nu on element, Eig value, el nu] AC_mode_energy_elastic: the elastic power in the acoutic modes. """ self.d_in_m = self.structure.inc_a_x*1e-9 el_conv_table = {} i = 1; j = 1 for matter in self.structure.acoustic_props_tmp: if matter.s != None: el_conv_table[i] = j j += 1 i += 1 final_dict = {} for entry in el_conv_table: # print entry, self.EM_sim.el_conv_table_n[entry], el_conv_table[entry] final_dict[self.EM_sim.el_conv_table_n[entry]] = el_conv_table[entry] # print final_dict self.typ_el_AC = final_dict if self.num_modes < 20: self.num_modes = 20 print("Warning: ARPACK needs >= 20 modes so set num_modes=20.") # Parameters that control how FEM routine runs i_cond = 1 # Boundary conditions (0=Dirichlet,1=Neumann,2=unitcell_x) itermax = 30 # Maximum number of iterations for convergence AC_FEM_debug = 0 # Fortran routines will display & save add. info ARPACK_tol = 1e-10 # ARPACK accuracy (0.0 for machine precision) # Calculate where to center the Eigenmode solver around. # (Shift and invert FEM method) if self.shift_Hz is None: # For AC problem shift is a frequency; [shift] = s^-1. v_list = [] for el in range(self.structure.nb_typ_el_AC): # Using acoustic velocity of longitudinal mode pg 215 Auld vol 1. v_list.append(np.sqrt(self.structure.c_tensor[0,0][el]/self.structure.rho[el])) # # Using acoustic velocity of shear mode pg 215 Auld vol 1. # v_list.append(np.sqrt(self.structure.c_tensor[3,3][el]/self.structure.rho[el])) AC_velocity = np.real(v_list).min() shift = np.real(AC_velocity*self.k_AC/(2.*np.pi)) # print "shift", shift shift = 0.9*shift # print "shift", shift else: shift = self.shift_Hz # Take existing msh from EM FEM and manipulate mesh to exclude vacuum areas. if self.EM_sim: suplied_geo_flag = 1 n_msh_el = self.EM_sim.n_msh_el n_msh_pts = self.EM_sim.n_msh_pts type_el = self.EM_sim.type_el type_nod = self.EM_sim.type_nod table_nod = self.EM_sim.table_nod x_arr = self.EM_sim.x_arr n_el_kept = 0 n_msh_pts_AC = 0 type_el_AC = [] table_nod_AC_tmp = np.zeros(np.shape(table_nod)) el_convert_tbl = {} el_convert_tbl_inv = {} node_convert_tbl = {} if self.structure.plt_mesh: plotting.plot_msh(x_arr, prefix_str=self.structure.mesh_file, suffix_str='_AC-orig') for el in range(n_msh_el): # print type_el[el] if type_el[el] in self.typ_el_AC: # print "in", type_el[el] type_el_AC.append(self.typ_el_AC[type_el[el]]) el_convert_tbl[n_el_kept] = el el_convert_tbl_inv[el] = n_el_kept for i in range(6): # Leaves node numbering untouched table_nod_AC_tmp[i][n_el_kept] = table_nod[i][el] n_el_kept += 1 n_msh_el_AC = n_el_kept # Find unique nodes node_lst_tmp = [] for el in range(n_msh_el_AC): for i in range(6): node_lst_tmp.append(table_nod_AC_tmp[i][el]) unique_nodes = list(set(node_lst_tmp)) n_msh_pts_AC = len(unique_nodes) unique_nodes = [int(j) for j in unique_nodes] # Mapping unique nodes to start from zero for i in range(n_msh_pts_AC): node_convert_tbl[unique_nodes[i]] = i # Creating finalised table_nod. table_nod_AC = [] for i in range(6): el_tbl = [] for el in range(n_msh_el_AC): # Note table_nod needs to be adjust back to fortran indexing el_tbl.append(node_convert_tbl[table_nod_AC_tmp[i][el]]+1) table_nod_AC.append(el_tbl) # Find the coordinates of chosen nodes. x_arr_AC = np.zeros((2,n_msh_pts_AC)) for node in unique_nodes: # Note x_arr needs to be adjust back to fortran indexing x_arr_AC[0,node_convert_tbl[node]] = (x_arr[0,node-1]) x_arr_AC[1,node_convert_tbl[node]] = (x_arr[1,node-1]) self.el_convert_tbl = el_convert_tbl self.el_convert_tbl_inv = el_convert_tbl_inv self.node_convert_tbl = node_convert_tbl ### AC FEM uses Neumann B.C.s so type_nod is totally irrelevant! # # Find nodes on boundaries of materials # node_array = -1*np.ones(n_msh_pts) # interface_nodes = [] # for el in range(n_msh_el): # for i in range(6): # node = table_nod[i][el] # # Check if first time seen this node # if node_array[node - 1] == -1: # adjust to python indexing # node_array[node - 1] = type_el[el] # else: # if node_array[node - 1] != type_el[el]: # interface_nodes.append(node) # interface_nodes = list(set(interface_nodes)) type_nod_AC = np.zeros(n_msh_pts_AC) # import matplotlib # matplotlib.use('pdf') # import matplotlib.pyplot as plt # plt.clf() # plt.figure(figsize=(13,13)) # ax = plt.subplot(1,1,1) # for node in unique_nodes: # if node in interface_nodes: # type_nod_AC[node_convert_tbl[node]] = i_cond # plt.plot(x_arr_AC[0,node_convert_tbl[node]], x_arr_AC[1,node_convert_tbl[node]], 'ok') # ax.set_aspect('equal') # plt.savefig('boundary.pdf', bbox_inches='tight') self.n_msh_pts = n_msh_pts_AC self.n_msh_el = n_msh_el_AC # Default, indicates to use geometry subroutine in FEM routine. else: suplied_geo_flag = 0 with open("../backend/fortran/msh/"+self.structure.mesh_file) as f: self.n_msh_pts, self.n_msh_el = [int(i) for i in f.readline().split()] table_nod_AC = np.zeros((6, self.n_msh_el)) type_el_AC = np.zeros(self.n_msh_el) x_arr_AC = np.zeros((2,self.n_msh_pts)) type_nod_AC = np.zeros(self.n_msh_pts) if AC_FEM_debug == 1: print('shift', shift) if not os.path.exists("Normed"): os.mkdir("Normed") if not os.path.exists("Output"): os.mkdir("Output") if not os.path.exists("Matrices"): os.mkdir("Matrices") # Size of Fortran's complex superarray (scales with mesh) int_max, cmplx_max, real_max = NumBAT.array_size(self.n_msh_el, self.num_modes) try: resm = NumBAT.calc_ac_modes( self.k_AC, self.num_modes, AC_FEM_debug, self.structure.mesh_file, self.n_msh_pts, self.n_msh_el, self.structure.nb_typ_el_AC, self.structure.c_tensor, self.structure.rho, self.d_in_m, shift, i_cond, itermax, ARPACK_tol, self.structure.plotting_fields, cmplx_max, real_max, int_max, suplied_geo_flag, type_nod_AC, self.structure.symmetry_flag, table_nod_AC, type_el_AC, x_arr_AC) table_nod_out, type_el_out, x_arr_out, \ self.Eig_values, self.sol1, self.mode_pol = resm # FEM Eigenvalue is frequency, rather than angular frequency Omega self.Omega_AC = self.Eig_values*2*np.pi except KeyboardInterrupt: print("\n\n FEM routine calc_AC_modes",\ "interrupted by keyboard.\n\n") # Retrieve the material properties of each mesh point. self.ls_material = NumBAT.array_material_ac(self.n_msh_pts, self.n_msh_el, self.structure.nb_typ_el_AC, type_el_AC, self.structure.rho, self.structure.c_tensor, self.structure.p_tensor, self.structure.eta_tensor) if self.structure.plt_mesh: plotting.plot_msh(x_arr_AC, prefix_str=self.structure.mesh_file, suffix_str='_AC-in') plotting.plot_msh(x_arr_out, prefix_str=self.structure.mesh_file, suffix_str='_AC-out') # if self.EM_sim is None: # table_nod_out = None # type_el_out = None # x_arr_out = None # self.table_nod = table_nod_AC # self.type_el = type_el_AC # self.x_arr = x_arr_AC # else: self.table_nod = table_nod_out self.type_el = type_el_out self.x_arr = x_arr_out ### Calc unnormalised power in each AC mode - PRA Eq. 18. if self.calc_AC_mode_power is True: try: nnodes = 6 if self.structure.inc_shape in self.structure.linear_element_shapes: # Semi-analytic integration following KD 9/9/16 notes. Fastest! self.AC_mode_power = NumBAT.ac_mode_power_int_v4( self.num_modes, self.n_msh_el, self.n_msh_pts, nnodes, self.table_nod, self.type_el, self.x_arr, self.structure.nb_typ_el_AC, self.structure.c_tensor, self.k_AC, self.Omega_AC, self.sol1) else: if self.structure.inc_shape not in self.structure.curvilinear_element_shapes: print("Warning: ac_mode_power_int - not sure if mesh contains curvi-linear elements", "\n using slow quadrature integration by default.\n\n") # Integration by quadrature. Slowest. self.AC_mode_power = NumBAT.ac_mode_power_int( self.num_modes, self.n_msh_el, self.n_msh_pts, nnodes, self.table_nod, self.type_el, self.x_arr, self.structure.nb_typ_el_AC, self.structure.c_tensor_z, self.k_AC, self.Omega_AC, self.sol1, AC_FEM_debug) except KeyboardInterrupt: print("\n\n FEM routine AC_mode_energy_int interrupted by keyboard.\n\n") ### Calc unnormalised elastic energy in each AC mode - PRA Eq. 16. try: nnodes = 6 if self.structure.inc_shape in self.structure.linear_element_shapes: # Semi-analytic integration. Fastest! self.AC_mode_energy_elastic = NumBAT.ac_mode_elastic_energy_int_v4( self.num_modes, self.n_msh_el, self.n_msh_pts, nnodes, self.table_nod, self.type_el, self.x_arr, self.structure.nb_typ_el_AC, self.structure.rho, self.Omega_AC, self.sol1) else: if self.structure.inc_shape not in self.structure.curvilinear_element_shapes: print("Warning: ac_mode_elastic_energy_int - not sure if mesh contains curvi-linear elements", "\n using slow quadrature integration by default.\n\n") # Integration by quadrature. Slowest. self.AC_mode_energy_elastic = NumBAT.ac_mode_elastic_energy_int( self.num_modes, self.n_msh_el, self.n_msh_pts, nnodes, self.table_nod, self.type_el, self.x_arr, self.structure.nb_typ_el_AC, self.structure.rho, self.Omega_AC, self.sol1, AC_FEM_debug) except KeyboardInterrupt: print("\n\n FEM routine AC_mode_elastic_energy_int interrupted by keyboard.\n\n")
def gain_and_qs(sim_EM_pump, sim_EM_Stokes, sim_AC, k_AC, EM_ival_pump=0, EM_ival_Stokes=0, AC_ival=0, fixed_Q=None, typ_select_out=None): r""" Calculate interaction integrals and SBS gain. Implements Eqs. 33, 41, 45, 91 of Wolff et al. PRA 92, 013836 (2015) doi/10.1103/PhysRevA.92.013836 These are for Q_photoelastic, Q_moving_boundary, the Acoustic loss "alpha", and the SBS gain respectively. Note there is a sign error in published Eq. 41. Also, in implementing Eq. 45 we use integration by parts, with a boundary integral term set to zero on physical grounds, and filled in some missing subscripts. We prefer to express Eq. 91 with the Lorentzian explicitly visible, which makes it clear how to transform to frequency space. The final integrals are .. math:: Q^{\rm PE} = -\varepsilon_0 \int_A {\rm d}^2r \sum_{ijkl} \varepsilon^2_r e^{(s)\star}_i e^{(p)}_j p_{ijkl} \partial_k u_l^{*},\\ Q^{\rm MB} = \int_C {\rm d \mathbf{r} (\mathbf{u}^{*} \cdot \hat n}) \big[ (\varepsilon_a - \varepsilon_b) \varepsilon_0 ({\rm \hat n \times \mathbf{e}}) \cdot ({\rm \hat n \times \mathbf{e}}) - (\varepsilon_a^{-1} - \varepsilon_b^{-1}) \varepsilon_0^{-1} ({\rm \hat n \cdot \mathbf{d}}) \cdot ({\rm \hat n \cdot \mathbf{d}}) \big],\\ \alpha = \frac{\Omega^2}{\mathcal{E}_{ac}} \int {\rm d}^2r \sum_{ijkl} \partial_i u_j^{*} \eta_{ijkl} \partial_k u_l,\\ \Gamma = \frac{2 \omega \Omega {\rm Re} (Q_1 Q_1^*)}{P_p P_s \mathcal{E}_{ac}} \frac{1}{\alpha} \frac{\alpha^2}{\alpha^2 + \kappa^2}. Args: sim_EM_pump (``Simmo`` object): Contains all info on pump EM modes sim_EM_Stokes (``Simmo`` object): Contains all info on Stokes EM modes sim_AC (``Simmo`` object): Contains all info on AC modes k_AC (float): Propagation constant of acoustic modes. Keyword Args: EM_ival_pump (int/string): Specify mode number of EM mode 1 (pump mode) to calculate interactions for. Numbering is python index so runs from 0 to num_EM_modes-1, with 0 being fundamental mode (largest prop constant). Can also set to 'All' to include all modes. EM_ival_Stokes (int/string): Specify mode number of EM mode 2 (stokes mode) to calculate interactions for. Numbering is python index so runs from 0 to num_EM_modes-1, with 0 being fundamental mode (largest prop constant). Can also set to 'All' to include all modes. AC_ival (int/string): Specify mode number of AC mode to calculate interactions for. Numbering is python index so runs from 0 to num_AC_modes-1, with 0 being fundamental mode (largest prop constant). Can also set to 'All' to include all modes. fixed_Q (int): Specify a fixed Q-factor for the AC modes, rather than calculating the acoustic loss (alpha). Returns: SBS_gain : The SBS gain including both photoelastic and moving boundary contributions. Note this will be negative for backwards SBS because gain is expressed as gain in power as move along z-axis in positive direction, but the Stokes waves experience gain as they propagate in the negative z-direction. Dimensions = [num_modes_EM_Stokes,num_modes_EM_pump,num_modes_AC]. SBS_gain_PE : The SBS gain for only the photoelastic effect. The comment about negative gain (see SBS_gain above) holds here also. Dimensions = [num_modes_EM_Stokes,num_modes_EM_pump,num_modes_AC]. SBS_gain_MB : The SBS gain for only the moving boundary effect. The comment about negative gain (see SBS_gain above) holds here also. Dimensions = [num_modes_EM_Stokes,num_modes_EM_pump,num_modes_AC]. alpha : The acoustic loss for each mode. Dimensions = [num_modes_AC]. """ # Notes about internals of fortran integration # Calc overlap of basis functions (and PE tensor and epsilon) # Then use this multiple times for calc of each mode field values # phi is values of Lagrange polynomials (1-6) at that node. # grad is value of gradient of Lagrange polynomials (1-6) at that node. # i variables refer to E field # j variables refer to H field # ww weight function # coeff numerical integration if EM_ival_pump == 'All': EM_ival_pump_fortran = -1 else: EM_ival_pump_fortran = EM_ival_pump + 1 # convert back to Fortran indexing if EM_ival_Stokes == 'All': EM_ival_Stokes_fortran = -1 else: EM_ival_Stokes_fortran = EM_ival_Stokes + 1 # convert back to Fortran indexing if AC_ival == 'All': AC_ival_fortran = -1 else: AC_ival_fortran = AC_ival + 1 # convert back to Fortran indexing Fortran_debug = 0 ncomps = 3 nnodes = 6 num_modes_EM_pump = sim_EM_pump.num_modes num_modes_EM_Stokes = sim_EM_Stokes.num_modes num_modes_AC = sim_AC.num_modes n_msh_el_AC = sim_AC.n_msh_el trimmed_EM_pump_field = np.zeros( (ncomps, nnodes, num_modes_EM_pump, n_msh_el_AC), dtype=complex) trimmed_EM_Stokes_field = np.zeros( (ncomps, nnodes, num_modes_EM_Stokes, n_msh_el_AC), dtype=complex) for el in range(n_msh_el_AC): new_el = sim_AC.el_convert_tbl[el] for n in range(nnodes): for x in range(ncomps): for ival in range(num_modes_EM_pump): trimmed_EM_pump_field[x, n, ival, el] = sim_EM_pump.sol1[x, n, ival, new_el] for ival in range(num_modes_EM_Stokes): trimmed_EM_Stokes_field[x, n, ival, el] = sim_EM_Stokes.sol1[x, n, ival, new_el] # sim_EM_pump.sol1 = trimmed_EM_pump_field # sim_EM_pump.n_msh_el = sim_AC.n_msh_el # sim_EM_pump.n_msh_pts = sim_AC.n_msh_pts # sim_EM_pump.type_el = sim_AC.type_el # sim_EM_pump.table_nod = sim_AC.table_nod # sim_EM_pump.x_arr = sim_AC.x_arr # plotting.plt_mode_fields(sim_EM_pump, EM_AC='EM', prefix_str='int_test-', suffix_str='trim') relevant_eps_effs = [] for el_typ in range(sim_EM_pump.structure.nb_typ_el): if el_typ + 1 in sim_AC.typ_el_AC: relevant_eps_effs.append(sim_EM_pump.n_list[el_typ]**2) print("\n-----------------------------------------------") if fixed_Q is None: # Calc alpha (loss) Eq. 45 print("Acoustic loss calc") start = time.time() try: if sim_EM_pump.structure.inc_shape in sim_EM_pump.structure.linear_element_shapes: alpha = NumBAT.ac_alpha_int_v2( sim_AC.num_modes, sim_AC.n_msh_el, sim_AC.n_msh_pts, nnodes, sim_AC.table_nod, sim_AC.type_el, sim_AC.x_arr, sim_AC.structure.nb_typ_el_AC, sim_AC.structure.eta_tensor, k_AC, sim_AC.Omega_AC, sim_AC.sol1, # sim_AC.AC_mode_power) # appropriate for alpha in [1/m] sim_AC.AC_mode_energy_elastic ) # appropriate for alpha in [1/s] else: if sim_EM_pump.structure.inc_shape not in sim_EM_pump.structure.curvilinear_element_shapes: print( "Warning: ac_alpha_int - not sure if mesh contains curvi-linear elements", "\n using slow quadrature integration by default.\n\n") alpha = NumBAT.ac_alpha_int( sim_AC.num_modes, sim_AC.n_msh_el, sim_AC.n_msh_pts, nnodes, sim_AC.table_nod, sim_AC.type_el, sim_AC.x_arr, sim_AC.structure.nb_typ_el_AC, sim_AC.structure.eta_tensor, k_AC, sim_AC.Omega_AC, sim_AC.sol1, # sim_AC.AC_mode_power, Fortran_debug) # appropriate for alpha in [1/m] sim_AC.AC_mode_energy_elastic, Fortran_debug) # appropriate for alpha in [1/s] except KeyboardInterrupt: print("\n\n Routine ac_alpha_int interrupted by keyboard.\n\n") alpha = np.real(alpha) # Q_factors = 0.5*(k_AC/alpha)*np.ones(num_modes_AC) # appropriate for alpha in [1/m] Q_factors = 0.5 * (sim_AC.Omega_AC / alpha) * np.ones( num_modes_AC) # appropriate for alpha in [1/s] end = time.time() print(" time (sec.)", (end - start)) else: # factor of a 1/2 because alpha is for power! # alpha [1/m] = Omega_AC/(2*vg*fixed_Q) = k_AC/fixed_Q # alpha [1/s] = vg * alpha [1/m] # alpha [1/s] = Omega_AC/(2*fixed_Q) # alpha = 0.5*(k_AC/fixed_Q)*np.ones(num_modes_AC) # appropriate for alpha in [1/m] alpha = 0.5 * (sim_AC.Omega_AC / fixed_Q) * np.ones( num_modes_AC) # appropriate for alpha in [1/s] Q_factors = fixed_Q * np.ones(num_modes_AC) linewidth_Hz = alpha / np.pi # SBS linewidth of each resonance in [Hz] # Calc Q_photoelastic Eq. 33 print("Photoelastic calc") start = time.time() try: if sim_EM_pump.structure.inc_shape in sim_EM_pump.structure.linear_element_shapes: Q_PE = NumBAT.photoelastic_int_v2( sim_EM_pump.num_modes, sim_EM_Stokes.num_modes, sim_AC.num_modes, EM_ival_pump_fortran, EM_ival_Stokes_fortran, AC_ival_fortran, sim_AC.n_msh_el, sim_AC.n_msh_pts, nnodes, sim_AC.table_nod, sim_AC.type_el, sim_AC.x_arr, sim_AC.structure.nb_typ_el_AC, sim_AC.structure.p_tensor, k_AC, trimmed_EM_pump_field, trimmed_EM_Stokes_field, sim_AC.sol1, relevant_eps_effs, Fortran_debug) else: if sim_EM_pump.structure.inc_shape not in sim_EM_pump.structure.curvilinear_element_shapes: print( "Warning: photoelastic_int - not sure if mesh contains curvi-linear elements", "\n using slow quadrature integration by default.\n\n") Q_PE = NumBAT.photoelastic_int( sim_EM_pump.num_modes, sim_EM_Stokes.num_modes, sim_AC.num_modes, EM_ival_pump_fortran, EM_ival_Stokes_fortran, AC_ival_fortran, sim_AC.n_msh_el, sim_AC.n_msh_pts, nnodes, sim_AC.table_nod, sim_AC.type_el, sim_AC.x_arr, sim_AC.structure.nb_typ_el_AC, sim_AC.structure.p_tensor, k_AC, trimmed_EM_pump_field, trimmed_EM_Stokes_field, sim_AC.sol1, relevant_eps_effs, Fortran_debug) except KeyboardInterrupt: print("\n\n Routine photoelastic_int interrupted by keyboard.\n\n") end = time.time() print(" time (sec.)", (end - start)) # Calc Q_moving_boundary Eq. 41 typ_select_in = 1 # first element in relevant_eps_effs list, in fortan indexing if len(relevant_eps_effs) == 2: typ_select_out = 2 elif typ_select_out is None: typ_select_out = -1 print("Moving boundary calc") start = time.time() try: Q_MB = NumBAT.moving_boundary( sim_EM_pump.num_modes, sim_EM_Stokes.num_modes, sim_AC.num_modes, EM_ival_pump_fortran, EM_ival_Stokes_fortran, AC_ival_fortran, sim_AC.n_msh_el, sim_AC.n_msh_pts, nnodes, sim_AC.table_nod, sim_AC.type_el, sim_AC.x_arr, sim_AC.structure.nb_typ_el_AC, typ_select_in, typ_select_out, trimmed_EM_pump_field, trimmed_EM_Stokes_field, sim_AC.sol1, relevant_eps_effs, Fortran_debug) except KeyboardInterrupt: print("\n\n Routine moving_boundary interrupted by keyboard.\n\n") end = time.time() print(" time (sec.)", (end - start)) print("-----------------------------------------------") Q = Q_PE + Q_MB # Note: sim_EM_pump.omega_EM is the optical angular freq in units of Hz # Note: sim_AC.Omega_AC is the acoustic angular freq in units of Hz gain = 2 * sim_EM_pump.omega_EM * sim_AC.Omega_AC * np.real(Q * np.conj(Q)) gain_PE = 2 * sim_EM_pump.omega_EM * sim_AC.Omega_AC * np.real( Q_PE * np.conj(Q_PE)) gain_MB = 2 * sim_EM_pump.omega_EM * sim_AC.Omega_AC * np.real( Q_MB * np.conj(Q_MB)) normal_fact = np.zeros( (num_modes_EM_Stokes, num_modes_EM_pump, num_modes_AC), dtype=complex) for i in range(num_modes_EM_Stokes): P1 = sim_EM_Stokes.EM_mode_power[i] for j in range(num_modes_EM_pump): P2 = sim_EM_pump.EM_mode_power[j] for k in range(num_modes_AC): # P3 = sim_AC.AC_mode_power[k] P3 = sim_AC.AC_mode_energy_elastic[k] normal_fact[i, j, k] = P1 * P2 * P3 * alpha[k] SBS_gain = np.real(gain / normal_fact) SBS_gain_PE = np.real(gain_PE / normal_fact) SBS_gain_MB = np.real(gain_MB / normal_fact) return SBS_gain, SBS_gain_PE, SBS_gain_MB, linewidth_Hz, Q_factors, alpha
def plt_mode_fields(sim_wguide, ivals=None, n_points=500, quiver_steps=50, xlim_min=None, xlim_max=None, ylim_min=None, ylim_max=None, EM_AC='EM_E', num_ticks=None, contours=False, contour_lst=None, stress_fields=False, pdf_png='png', prefix_str='', suffix_str=''): """ Plot E or H fields of EM mode, or the AC modes displacement fields. Args: sim_wguide : A ``Struct`` instance that has had calc_modes calculated Keyword Args: ivals (list): mode numbers of modes you wish to plot n_points (int): The number of points across unitcell to interpolate the field onto xlim_min (float): Limit plotted xrange to xlim_min:(1-xlim_max) of unitcell xlim_max (float): Limit plotted xrange to xlim_min:(1-xlim_max) of unitcell ylim_min (float): Limit plotted yrange to ylim_min:(1-ylim_max) of unitcell ylim_max (float): Limit plotted yrange to ylim_min:(1-ylim_max) of unitcell EM_AC (str): Either 'EM' or 'AC' modes num_ticks (int): Number of tick marks contours (bool): Controls contours being overlaid on fields contour_lst (list): Specify contour values stress_fields (bool): Calculate acoustic stress fields pdf_png (str): File type to save, either 'png' or 'pdf' prefix_str (str): Add a string to start of file name suffix_str (str): Add a string to end of file name. """ if EM_AC is not 'EM_E' and EM_AC is not 'EM_H' and EM_AC is not 'AC': raise ValueError("EM_AC must be either 'AC', 'EM_E' or 'EM_H'.") # Calculate the magnetic field from the electric field if EM_AC == 'EM_H': nnodes = 6 sim_wguide.sol1_H = NumBAT.h_mode_field_ez( sim_wguide.k_0, sim_wguide.num_modes, sim_wguide.n_msh_el, sim_wguide.n_msh_pts, nnodes, sim_wguide.table_nod, sim_wguide.x_arr, sim_wguide.Eig_values, sim_wguide.sol1) plt.clf() # field mapping x_tmp = [] y_tmp = [] for i in np.arange(sim_wguide.n_msh_pts): x_tmp.append(sim_wguide.x_arr[0, i]) y_tmp.append(sim_wguide.x_arr[1, i]) x_min = np.min(x_tmp) x_max = np.max(x_tmp) y_min = np.min(y_tmp) y_max = np.max(y_tmp) area = abs((x_max - x_min) * (y_max - y_min)) n_pts_x = int(n_points * abs(x_max - x_min) / np.sqrt(area)) n_pts_y = int(n_points * abs(y_max - y_min) / np.sqrt(area)) v_x = np.zeros(n_pts_x * n_pts_y) v_y = np.zeros(n_pts_x * n_pts_y) if contours: v_XX, v_YY = np.meshgrid(range(n_pts_x), range(n_pts_y)) i = 0 for x in np.linspace(x_min, x_max, n_pts_x): for y in np.linspace(y_min, y_max, n_pts_y): v_x[i] = x v_y[i] = y i += 1 v_x = np.array(v_x) v_y = np.array(v_y) # unrolling data for the interpolators table_nod = sim_wguide.table_nod.T x_arr = sim_wguide.x_arr.T if ivals: ival_range = ivals else: ival_range = range(len(sim_wguide.Eig_values)) for ival in ival_range: # dense triangulation with multiple points v_x6p = np.zeros(6 * sim_wguide.n_msh_el) v_y6p = np.zeros(6 * sim_wguide.n_msh_el) v_Ex6p = np.zeros(6 * sim_wguide.n_msh_el, dtype=np.complex128) v_Ey6p = np.zeros(6 * sim_wguide.n_msh_el, dtype=np.complex128) v_Ez6p = np.zeros(6 * sim_wguide.n_msh_el, dtype=np.complex128) v_triang6p = [] i = 0 for i_el in np.arange(sim_wguide.n_msh_el): # triangles idx = np.arange(6 * i_el, 6 * (i_el + 1)) triangles = [[idx[0], idx[3], idx[5]], [idx[1], idx[4], idx[3]], [idx[2], idx[5], idx[4]], [idx[3], idx[4], idx[5]]] v_triang6p.extend(triangles) for i_node in np.arange(6): # index for the coordinates i_ex = table_nod[i_el, i_node] - 1 # values v_x6p[i] = x_arr[i_ex, 0] v_y6p[i] = x_arr[i_ex, 1] if EM_AC == 'EM_E' or EM_AC == 'AC': v_Ex6p[i] = sim_wguide.sol1[0, i_node, ival, i_el] v_Ey6p[i] = sim_wguide.sol1[1, i_node, ival, i_el] v_Ez6p[i] = sim_wguide.sol1[2, i_node, ival, i_el] if EM_AC == 'EM_H': v_Ex6p[i] = sim_wguide.sol1_H[0, i_node, ival, i_el] v_Ey6p[i] = sim_wguide.sol1_H[1, i_node, ival, i_el] v_Ez6p[i] = sim_wguide.sol1_H[2, i_node, ival, i_el] i += 1 v_E6p = np.sqrt( np.abs(v_Ex6p)**2 + np.abs(v_Ey6p)**2 + np.abs(v_Ez6p)**2) ### Interpolate onto triangular grid - honest to FEM elements # dense triangulation with unique points v_triang1p = [] for i_el in np.arange(sim_wguide.n_msh_el): # triangles triangles = [[ table_nod[i_el, 0] - 1, table_nod[i_el, 3] - 1, table_nod[i_el, 5] - 1 ], [ table_nod[i_el, 1] - 1, table_nod[i_el, 4] - 1, table_nod[i_el, 3] - 1 ], [ table_nod[i_el, 2] - 1, table_nod[i_el, 5] - 1, table_nod[i_el, 4] - 1 ], [ table_nod[i_el, 3] - 1, table_nod[i_el, 4] - 1, table_nod[i_el, 5] - 1 ]] v_triang1p.extend(triangles) # triangulations triang6p = matplotlib.tri.Triangulation(v_x6p, v_y6p, v_triang6p) triang1p = matplotlib.tri.Triangulation(x_arr[:, 0], x_arr[:, 1], v_triang1p) # building interpolators: triang1p for the finder, triang6p for the values finder = matplotlib.tri.TrapezoidMapTriFinder(triang1p) ReEx = matplotlib.tri.LinearTriInterpolator(triang6p, v_Ex6p.real, trifinder=finder) ImEx = matplotlib.tri.LinearTriInterpolator(triang6p, v_Ex6p.imag, trifinder=finder) ReEy = matplotlib.tri.LinearTriInterpolator(triang6p, v_Ey6p.real, trifinder=finder) ImEy = matplotlib.tri.LinearTriInterpolator(triang6p, v_Ey6p.imag, trifinder=finder) ReEz = matplotlib.tri.LinearTriInterpolator(triang6p, v_Ez6p.real, trifinder=finder) ImEz = matplotlib.tri.LinearTriInterpolator(triang6p, v_Ez6p.imag, trifinder=finder) AbsE = matplotlib.tri.LinearTriInterpolator(triang6p, v_E6p, trifinder=finder) # interpolated fields m_ReEx = ReEx(v_x, v_y).reshape(n_pts_x, n_pts_y) m_ReEy = ReEy(v_x, v_y).reshape(n_pts_x, n_pts_y) m_ReEz = ReEz(v_x, v_y).reshape(n_pts_x, n_pts_y) m_ImEx = ImEx(v_x, v_y).reshape(n_pts_x, n_pts_y) m_ImEy = ImEy(v_x, v_y).reshape(n_pts_x, n_pts_y) m_ImEz = ImEz(v_x, v_y).reshape(n_pts_x, n_pts_y) m_AbsE = AbsE(v_x, v_y).reshape(n_pts_x, n_pts_y) # Flip y order as imshow has origin at top left v_plots = [ m_ReEx[:, ::-1], m_ReEy[:, ::-1], m_ReEz[:, ::-1], m_ImEx[:, ::-1], m_ImEy[:, ::-1], m_ImEz[:, ::-1], m_AbsE[:, ::-1] ] if EM_AC == 'EM_E': v_labels = [ r"Re($E_x$)", r"Re($E_y$)", r"Re($E_z$)", r"Im($E_x$)", r"Im($E_y$)", r"Im($E_z$)", r"$|E|$" ] elif EM_AC == 'EM_H': v_labels = [ r"Re($H_x$)", r"Re($H_y$)", r"Re($H_z$)", r"Im($H_x$)", r"Im($H_y$)", r"Im($H_z$)", r"$|H|$" ] else: v_labels = [ r"Re($u_x$)", r"Re($u_y$)", r"Re($u_z$)", r"Im($u_x$)", r"Im($u_y$)", r"Im($u_z$)", r"$|u|$" ] # field plots plot_threshold = 1e-4 # set negligible components to explicitly zero plt.clf() fig = plt.figure(figsize=(15, 15)) for i_p, plot in enumerate(v_plots): ax = plt.subplot(3, 3, i_p + 1) if np.max(np.abs(plot[~np.isnan(plot)])) < plot_threshold: # im = plt.imshow(plot.T,cmap='viridis'); im = plt.imshow(np.zeros(np.shape(plot.T))) else: im = plt.imshow(plot.T) # ax.set_aspect('equal') # no ticks plt.xticks([]) plt.yticks([]) # limits axes = plt.gca() xmin, xmax = axes.get_xlim() ymin, ymax = axes.get_ylim() width_x = xmax - xmin width_y = ymax - ymin if xlim_min != None: ax.set_xlim(xmin + xlim_min * width_x, xmax - xlim_max * width_x) if ylim_min != None: ax.set_ylim(ymin + ylim_min * width_y, ymax - ylim_max * width_y) # titles plt.title(v_labels[i_p], fontsize=title_font - 4) # colorbar divider = make_axes_locatable(ax) cax = divider.append_axes("right", size="5%", pad=0.1) cbar = plt.colorbar(im, cax=cax) if num_ticks: cbarticks = np.linspace(np.min(plot), np.max(plot), num=num_ticks) elif ylim_min != None: if xlim_min / ylim_min > 3: cbarticks = np.linspace(np.min(plot), np.max(plot), num=3) if xlim_min / ylim_min > 1.5: cbarticks = np.linspace(np.min(plot), np.max(plot), num=5) else: cbarticks = np.linspace(np.min(plot), np.max(plot), num=7) else: cbarticks = np.linspace(np.min(plot), np.max(plot), num=7) cbar.set_ticks(cbarticks) cbarlabels = ['%.2f' % t for t in cbarticks] cbar.set_ticklabels(cbarlabels) if contours: if contour_lst: cbarticks = contour_lst if np.max(np.abs(plot[~np.isnan(plot)])) > plot_threshold: CS2 = ax.contour(v_XX, v_YY, plot.T, levels=cbarticks, colors=colors[::-1], linewidths=(1.5, )) cbar.add_lines(CS2) cbar.ax.tick_params(labelsize=title_font - 10) if EM_AC == 'AC': v_x_q = v_x.reshape(n_pts_x, n_pts_y) v_y_q = v_y.reshape(n_pts_x, n_pts_y) v_x_q = v_x_q[0::quiver_steps, 0::quiver_steps] v_y_q = v_y_q[0::quiver_steps, 0::quiver_steps] m_ReEx_q = m_ReEx[0::quiver_steps, 0::quiver_steps] m_ReEy_q = m_ReEy[0::quiver_steps, 0::quiver_steps] m_ImEx_q = m_ImEx[0::quiver_steps, 0::quiver_steps] m_ImEy_q = m_ImEy[0::quiver_steps, 0::quiver_steps] ax = plt.subplot(3, 3, i_p + 2) plt.quiver( v_x_q, v_y_q, (m_ReEx_q + m_ImEx_q), (m_ReEy_q + m_ImEy_q), # data np.sqrt( np.real((m_ReEx_q + 1j * m_ImEx_q) * (m_ReEx_q - 1j * m_ImEx_q) + (m_ReEy_q + 1j * m_ImEy_q) * (m_ReEy_q - 1j * m_ImEy_q)) ), #colour the arrows based on this array linewidths=(0.2, ), edgecolors=('k'), pivot='mid', headlength=5) # length of the arrows ax.set_aspect('equal') plt.xticks([]) plt.yticks([]) axes = plt.gca() xmin, xmax = axes.get_xlim() ymin, ymax = axes.get_ylim() if xlim_min != None: ax.set_xlim(xlim_min * xmax, (1 - xlim_max) * xmax) if ylim_min != None: ax.set_ylim((1 - ylim_min) * ymin, ylim_max * ymin) plt.title('Transverse', fontsize=title_font - 4) if EM_AC == 'EM_E' or EM_AC == 'EM_H': n_eff = sim_wguide.Eig_values[ival] * sim_wguide.wl_m / (2 * np.pi) if np.imag(sim_wguide.Eig_values[ival]) < 0: k_str = r'$k_z = %(re_k)f %(im_k)f i$'% \ {'re_k' : np.real(sim_wguide.Eig_values[ival]), 'im_k' : np.imag(sim_wguide.Eig_values[ival])} n_str = r'$n_{eff} = %(re_k)f %(im_k)f i$'% \ {'re_k' : np.real(n_eff), 'im_k' : np.imag(n_eff)} else: k_str = r'$k_z = %(re_k)f + %(im_k)f i$'% \ {'re_k' : np.real(sim_wguide.Eig_values[ival]), 'im_k' : np.imag(sim_wguide.Eig_values[ival])} n_str = r'$n_{eff} = %(re_k)f + %(im_k)f i$'% \ {'re_k' : np.real(n_eff), 'im_k' : np.imag(n_eff)} # plt.text(10, 0.3, n_str, fontsize=title_font) else: n_str = '' if np.imag(sim_wguide.Eig_values[ival]) < 0: k_str = r'$\Omega/2\pi = %(re_k)f %(im_k)f i$ GHz'% \ {'re_k' : np.real(sim_wguide.Eig_values[ival]*1e-9), 'im_k' : np.imag(sim_wguide.Eig_values[ival]*1e-9)} else: k_str = r'$\Omega/2\pi = %(re_k)f + %(im_k)f i$ GHz'% \ {'re_k' : np.real(sim_wguide.Eig_values[ival]*1e-9), 'im_k' : np.imag(sim_wguide.Eig_values[ival]*1e-9)} # plt.text(10, 0.5, k_str, fontsize=title_font) plt.suptitle('Mode #' + str(ival) + ' ' + k_str + ' ' + n_str + "\n", fontsize=title_font) # plt.tight_layout(pad=2.5, w_pad=0.5, h_pad=1.0) fig.set_tight_layout(True) if not os.path.exists("%sfields" % prefix_str): os.mkdir("%sfields" % prefix_str) if pdf_png == 'png': plt.savefig('%(pre)sfields/%(s)s_field_%(i)i%(add)s.png' % { 'pre': prefix_str, 's': EM_AC, 'i': ival, 'add': suffix_str }) #, bbox_inches='tight') - this caused error in Q calc... ? elif pdf_png == 'pdf': plt.savefig('%(pre)sfields/%(s)s_field_%(i)i%(add)s.pdf' % { 'pre': prefix_str, 's': EM_AC, 'i': ival, 'add': suffix_str }, bbox_inches='tight') else: raise ValueError("pdf_png must be either 'png' or 'pdf'.") plt.close() if EM_AC == 'AC' and stress_fields is True: ### Interpolate onto rectangular Cartesian grid xy = list(zip(v_x6p, v_y6p)) grid_x, grid_y = np.mgrid[x_min:x_max:n_pts_x * 1j, y_min:y_max:n_pts_y * 1j] m_ReEx = interpolate.griddata(xy, v_Ex6p.real, (grid_x, grid_y), method='linear') m_ReEy = interpolate.griddata(xy, v_Ey6p.real, (grid_x, grid_y), method='linear') m_ReEz = interpolate.griddata(xy, v_Ez6p.real, (grid_x, grid_y), method='linear') m_ImEx = interpolate.griddata(xy, v_Ex6p.imag, (grid_x, grid_y), method='linear') m_ImEy = interpolate.griddata(xy, v_Ey6p.imag, (grid_x, grid_y), method='linear') m_ImEz = interpolate.griddata(xy, v_Ez6p.imag, (grid_x, grid_y), method='linear') m_AbsE = interpolate.griddata(xy, v_E6p.real, (grid_x, grid_y), method='linear') dx = grid_x[-1, 0] - grid_x[-2, 0] dy = grid_y[0, -1] - grid_y[0, -2] m_Ex = m_ReEx + 1j * m_ImEx m_Ey = m_ReEy + 1j * m_ImEy m_Ez = m_ReEz + 1j * m_ImEz m_Ex = m_Ex.reshape(n_pts_x, n_pts_y) m_Ey = m_Ey.reshape(n_pts_x, n_pts_y) m_Ez = m_Ez.reshape(n_pts_x, n_pts_y) m_AbsE = m_AbsE.reshape(n_pts_x, n_pts_y) m_ReEx = np.real(m_Ex) m_ReEy = np.real(m_Ey) m_ReEz = np.real(m_Ez) m_ImEx = np.imag(m_Ex) m_ImEy = np.imag(m_Ey) m_ImEz = np.imag(m_Ez) del_x_Ex = np.gradient(m_Ex, dx, axis=0) del_y_Ex = np.gradient(m_Ex, dy, axis=1) del_x_Ey = np.gradient(m_Ey, dx, axis=0) del_y_Ey = np.gradient(m_Ey, dy, axis=1) del_x_Ez = np.gradient(m_Ez, dx, axis=0) del_y_Ez = np.gradient(m_Ez, dy, axis=1) del_z_Ex = 1j * sim_wguide.k_AC * m_Ex del_z_Ey = 1j * sim_wguide.k_AC * m_Ey del_z_Ez = 1j * sim_wguide.k_AC * m_Ez # Flip y order as imshow has origin at top left del_mat = np.array([ del_x_Ex[:, ::-1].real, del_x_Ey[:, ::-1].real, del_x_Ez[:, ::-1].real, del_x_Ex[:, ::-1].imag, del_x_Ey[:, ::-1].imag, del_x_Ez[:, ::-1].imag, del_y_Ex[:, ::-1].real, del_y_Ey[:, ::-1].real, del_y_Ez[:, ::-1].real, del_y_Ex[:, ::-1].imag, del_y_Ey[:, ::-1].imag, del_y_Ez[:, ::-1].imag, del_z_Ex[:, ::-1].real, del_z_Ey[:, ::-1].real, del_z_Ez[:, ::-1].real, del_z_Ex[:, ::-1].imag, del_z_Ey[:, ::-1].imag, del_z_Ez[:, ::-1].imag ]) v_labels = [ "Re($S_{xx}$)", "Re($S_{xy}$)", "Re($S_{xz}$)", "Im($S_{xx}$)", "Im($S_{xy}$)", "Im($S_{xz}$)", "Re($S_{yx}$)", "Re($S_{yy}$)", "Re($S_{yz}$)", "Im($S_{yx}$)", "Im($S_{yy}$)", "Im($S_{yz}$)", "Re($S_{zx}$)", "Re($S_{zy}$)", "Re($S_{zz}$)", "Im($S_{zx}$)", "Im($S_{zy}$)", "Im($S_{zz}$)" ] # field plots plt.clf() fig = plt.figure(figsize=(15, 30)) for i_p, plot in enumerate(del_mat): ax = plt.subplot(6, 3, i_p + 1) im = plt.imshow(plot.T) # no ticks plt.xticks([]) plt.yticks([]) # limits if xlim_min != None: ax.set_xlim(xlim_min * n_points, (1 - xlim_max) * n_points) if ylim_min != None: ax.set_ylim((1 - ylim_min) * n_points, ylim_max * n_points) # titles plt.title(v_labels[i_p], fontsize=title_font - 4) # colorbar divider = make_axes_locatable(ax) cax = divider.append_axes("right", size="5%", pad=0.1) cbar = plt.colorbar(im, cax=cax, format='%.2e') if num_ticks: cbarticks = np.linspace(np.min(plot), np.max(plot), num=num_ticks) elif ylim_min != None: if xlim_min / ylim_min > 3: cbarlabels = np.linspace(np.min(plot), np.max(plot), num=3) if xlim_min / ylim_min > 1.5: cbarlabels = np.linspace(np.min(plot), np.max(plot), num=5) else: cbarlabels = np.linspace(np.min(plot), np.max(plot), num=7) else: cbarlabels = np.linspace(np.min(plot), np.max(plot), num=7) cbar.set_ticks(cbarlabels) cbarlabels = ['%.2f' % t for t in cbarlabels] cbar.set_ticklabels(cbarlabels) if contours: if contour_lst: cbarticks = contour_lst print(contour_lst) if np.max(np.abs(plot[~np.isnan(plot)])) > plot_threshold: CS2 = ax.contour(v_XX, v_YY, plot.T, levels=cbarticks, colors=colors[::-1], linewidths=(1.5, )) cbar.add_lines(CS2) cbar.ax.tick_params(labelsize=title_font - 10) fig.set_tight_layout(True) n_str = '' if np.imag(sim_wguide.Eig_values[ival]) < 0: k_str = r'$\Omega/2\pi = %(re_k)f %(im_k)f i$ GHz'% \ {'re_k' : np.real(sim_wguide.Eig_values[ival]*1e-9), 'im_k' : np.imag(sim_wguide.Eig_values[ival]*1e-9)} else: k_str = r'$\Omega/2\pi = %(re_k)f + %(im_k)f i$ GHz'% \ {'re_k' : np.real(sim_wguide.Eig_values[ival]*1e-9), 'im_k' : np.imag(sim_wguide.Eig_values[ival]*1e-9)} plt.suptitle('Mode #' + str(ival) + ' ' + k_str + ' ' + n_str, fontsize=title_font) if pdf_png == 'png': plt.savefig('%(pre)sfields/%(s)s_S_field_%(i)i%(add)s.png' % { 'pre': prefix_str, 's': EM_AC, 'i': ival, 'add': suffix_str }) elif pdf_png == 'pdf': plt.savefig('%(pre)sfields/%(s)s_S_field_%(i)i%(add)s.pdf' % { 'pre': prefix_str, 's': EM_AC, 'i': ival, 'add': suffix_str }, bbox_inches='tight') plt.close()
def plt_mode_fields(sim_wguide, ivals=None, n_points=501, quiver_steps=50, xlim_min=None, xlim_max=None, ylim_min=None, ylim_max=None, EM_AC='EM_E', num_ticks=None, colorbar=True, contours=False, contour_lst=None, stress_fields=False, pdf_png='png', prefix_str='', suffix_str='', ticks=False, comps=None, decorator=None): """ Plot E or H fields of EM mode, or the AC modes displacement fields. Args: sim_wguide : A ``Struct`` instance that has had calc_modes calculated Keyword Args: ivals (list): mode numbers of modes you wish to plot n_points (int): The number of points across unitcell to interpolate the field onto xlim_min (float): Limit plotted xrange to xlim_min:(1-xlim_max) of unitcell xlim_max (float): Limit plotted xrange to xlim_min:(1-xlim_max) of unitcell ylim_min (float): Limit plotted yrange to ylim_min:(1-ylim_max) of unitcell ylim_max (float): Limit plotted yrange to ylim_min:(1-ylim_max) of unitcell EM_AC (str): Either 'EM' or 'AC' modes num_ticks (int): Number of tick marks contours (bool): Controls contours being overlaid on fields contour_lst (list): Specify contour values stress_fields (bool): Calculate acoustic stress fields pdf_png (str): File type to save, either 'png' or 'pdf' prefix_str (str): Add a string to start of file name suffix_str (str): Add a string to end of file name. """ if EM_AC is not 'EM_E' and EM_AC is not 'EM_H' and EM_AC is not 'AC': raise ValueError("EM_AC must be either 'AC', 'EM_E' or 'EM_H'.") # Calculate the magnetic field from the electric field if EM_AC == 'EM_H': nnodes = 6 sim_wguide.sol1_H = NumBAT.h_mode_field_ez( sim_wguide.k_0, sim_wguide.num_modes, sim_wguide.n_msh_el, sim_wguide.n_msh_pts, nnodes, sim_wguide.table_nod, sim_wguide.x_arr, sim_wguide.Eig_values, sim_wguide.sol1) plt.clf() # field mapping x_tmp = [] y_tmp = [] for i in np.arange(sim_wguide.n_msh_pts): x_tmp.append(sim_wguide.x_arr[0, i]) y_tmp.append(sim_wguide.x_arr[1, i]) x_min = np.min(x_tmp) x_max = np.max(x_tmp) y_min = np.min(y_tmp) y_max = np.max(y_tmp) area = abs((x_max - x_min) * (y_max - y_min)) n_pts_x = int(n_points * abs(x_max - x_min) / np.sqrt(area)) n_pts_y = int(n_points * abs(y_max - y_min) / np.sqrt(area)) v_x = np.zeros(n_pts_x * n_pts_y) v_y = np.zeros(n_pts_x * n_pts_y) v_XX = None v_YY = None if contours: v_XX, v_YY = np.meshgrid(range(n_pts_x), range(n_pts_y)) i = 0 for x in np.linspace(x_min, x_max, n_pts_x): for y in np.linspace(y_min, y_max, n_pts_y): v_x[i] = x v_y[i] = y i += 1 v_x = np.array(v_x) v_y = np.array(v_y) # unrolling data for the interpolators table_nod = sim_wguide.table_nod.T x_arr = sim_wguide.x_arr.T if ivals: ival_range = ivals else: ival_range = range(len(sim_wguide.Eig_values)) for ival in ival_range: # dense triangulation with multiple points v_x6p = np.zeros(6 * sim_wguide.n_msh_el) v_y6p = np.zeros(6 * sim_wguide.n_msh_el) v_Ex6p = np.zeros(6 * sim_wguide.n_msh_el, dtype=np.complex128) v_Ey6p = np.zeros(6 * sim_wguide.n_msh_el, dtype=np.complex128) v_Ez6p = np.zeros(6 * sim_wguide.n_msh_el, dtype=np.complex128) v_triang6p = [] i = 0 for i_el in np.arange(sim_wguide.n_msh_el): # triangles idx = np.arange(6 * i_el, 6 * (i_el + 1)) triangles = [[idx[0], idx[3], idx[5]], [idx[1], idx[4], idx[3]], [idx[2], idx[5], idx[4]], [idx[3], idx[4], idx[5]]] v_triang6p.extend(triangles) for i_node in np.arange(6): # index for the coordinates i_ex = table_nod[i_el, i_node] - 1 # values v_x6p[i] = x_arr[i_ex, 0] v_y6p[i] = x_arr[i_ex, 1] if EM_AC == 'EM_E' or EM_AC == 'AC': v_Ex6p[i] = sim_wguide.sol1[0, i_node, ival, i_el] v_Ey6p[i] = sim_wguide.sol1[1, i_node, ival, i_el] v_Ez6p[i] = sim_wguide.sol1[2, i_node, ival, i_el] if EM_AC == 'EM_H': v_Ex6p[i] = sim_wguide.sol1_H[0, i_node, ival, i_el] v_Ey6p[i] = sim_wguide.sol1_H[1, i_node, ival, i_el] v_Ez6p[i] = sim_wguide.sol1_H[2, i_node, ival, i_el] i += 1 v_E6p = np.sqrt( np.abs(v_Ex6p)**2 + np.abs(v_Ey6p)**2 + np.abs(v_Ez6p)**2) ### Interpolate onto triangular grid - honest to FEM elements # dense triangulation with unique points v_triang1p = [] for i_el in np.arange(sim_wguide.n_msh_el): # triangles triangles = [[ table_nod[i_el, 0] - 1, table_nod[i_el, 3] - 1, table_nod[i_el, 5] - 1 ], [ table_nod[i_el, 1] - 1, table_nod[i_el, 4] - 1, table_nod[i_el, 3] - 1 ], [ table_nod[i_el, 2] - 1, table_nod[i_el, 5] - 1, table_nod[i_el, 4] - 1 ], [ table_nod[i_el, 3] - 1, table_nod[i_el, 4] - 1, table_nod[i_el, 5] - 1 ]] v_triang1p.extend(triangles) # triangulations triang6p = matplotlib.tri.Triangulation(v_x6p, v_y6p, v_triang6p) triang1p = matplotlib.tri.Triangulation(x_arr[:, 0], x_arr[:, 1], v_triang1p) #fields are called Ex, Ey, etc regardless of whether we are plotting E, H, or u/S # building interpolators: triang1p for the finder, triang6p for the values #TODO: could be more efficient only interpolating the fields which are ultimately to be used? finder = matplotlib.tri.TrapezoidMapTriFinder(triang1p) ReEx = matplotlib.tri.LinearTriInterpolator(triang6p, v_Ex6p.real, trifinder=finder) ImEx = matplotlib.tri.LinearTriInterpolator(triang6p, v_Ex6p.imag, trifinder=finder) ReEy = matplotlib.tri.LinearTriInterpolator(triang6p, v_Ey6p.real, trifinder=finder) ImEy = matplotlib.tri.LinearTriInterpolator(triang6p, v_Ey6p.imag, trifinder=finder) ReEz = matplotlib.tri.LinearTriInterpolator(triang6p, v_Ez6p.real, trifinder=finder) ImEz = matplotlib.tri.LinearTriInterpolator(triang6p, v_Ez6p.imag, trifinder=finder) AbsE = matplotlib.tri.LinearTriInterpolator(triang6p, v_E6p, trifinder=finder) # interpolated fields m_ReEx = ReEx(v_x, v_y).reshape(n_pts_x, n_pts_y) m_ReEy = ReEy(v_x, v_y).reshape(n_pts_x, n_pts_y) m_ReEz = ReEz(v_x, v_y).reshape(n_pts_x, n_pts_y) m_ImEx = ImEx(v_x, v_y).reshape(n_pts_x, n_pts_y) m_ImEy = ImEy(v_x, v_y).reshape(n_pts_x, n_pts_y) m_ImEz = ImEz(v_x, v_y).reshape(n_pts_x, n_pts_y) m_AbsE = AbsE(v_x, v_y).reshape(n_pts_x, n_pts_y) v_x_q = v_x.reshape(n_pts_x, n_pts_y) v_y_q = v_y.reshape(n_pts_x, n_pts_y) v_x_q = v_x_q[0::quiver_steps, 0::quiver_steps] v_y_q = v_y_q[0::quiver_steps, 0::quiver_steps] m_ReEx_q = m_ReEx[0::quiver_steps, 0::quiver_steps] m_ReEy_q = m_ReEy[0::quiver_steps, 0::quiver_steps] m_ImEx_q = m_ImEx[0::quiver_steps, 0::quiver_steps] m_ImEy_q = m_ImEy[0::quiver_steps, 0::quiver_steps] ### No longer needed as imshow is using origin=lower # Flip y order as imshow has origin at top left #v_plots = [m_ReEx[:,::-1],m_ReEy[:,::-1],m_ReEz[:,::-1],m_ImEx[:,::-1],m_ImEy[:,::-1],m_ImEz[:,::-1],m_AbsE[:,::-1]] v_plots = [m_ReEx, m_ReEy, m_ReEz, m_ImEx, m_ImEy, m_ImEz, m_AbsE] vq_plots = [m_ReEx_q, m_ReEy_q, m_ImEx_q, m_ImEy_q] if EM_AC == 'EM_E': v_labels = [ r"Re($E_x$)", r"Re($E_y$)", r"Re($E_z$)", r"Im($E_x$)", r"Im($E_y$)", r"Im($E_z$)", r"$|E|$" ] elif EM_AC == 'EM_H': v_labels = [ r"Re($H_x$)", r"Re($H_y$)", r"Re($H_z$)", r"Im($H_x$)", r"Im($H_y$)", r"Im($H_z$)", r"$|H|$" ] else: v_labels = [ r"Re($u_x$)", r"Re($u_y$)", r"Re($u_z$)", r"Im($u_x$)", r"Im($u_y$)", r"Im($u_z$)", r"$|u|$" ] if decorator == None: decorator = FieldDecorator() plot_params = { 'xlim_min': xlim_min, 'xlim_max': xlim_max, 'ylim_min': ylim_min, 'ylim_max': ylim_max, 'ticks': ticks, 'num_ticks': num_ticks, 'colorbar': colorbar, 'contours': contours, 'contour_lst': contour_lst, 'EM_AC': EM_AC, 'prefix_str': prefix_str, 'suffix_str': suffix_str, 'pdf_png': pdf_png, 'stress_fields': stress_fields, 'decorator': decorator } if not os.path.exists("%sfields" % prefix_str): os.mkdir("%sfields" % prefix_str) decorator._set_for_multi() plot_all_components(v_x, v_y, v_x_q, v_y_q, v_XX, v_YY, v_plots, vq_plots, v_labels, plot_params, sim_wguide, ival) decorator._set_for_single() if comps != None: for comp in comps: if comp == 'Ex' and EM_AC == 'EM_E': plot_component(v_x, v_y, v_XX, v_YY, v_plots[0], v_labels[0], plot_params, sim_wguide, ival, comp) elif comp == 'Ey' and EM_AC == 'EM_E': plot_component(v_x, v_y, v_XX, v_YY, v_plots[1], v_labels[1], plot_params, sim_wguide, ival, comp) elif comp == 'Ez' and EM_AC == 'EM_E': plot_component(v_x, v_y, v_XX, v_YY, v_plots[5], v_labels[5], plot_params, sim_wguide, ival, comp) elif comp == 'Eabs' and EM_AC == 'EM_E': plot_component(v_x, v_y, v_XX, v_YY, v_plots[6], v_labels[6], plot_params, sim_wguide, ival, comp) elif comp == 'Et' and EM_AC == 'EM_E': plot_component(v_x_q, v_y_q, v_XX, v_YY, vq_plots, '$(E_x,E_y)$', plot_params, sim_wguide, ival, comp) elif comp == 'Hx' and EM_AC == 'EM_H': plot_component(v_x, v_y, v_XX, v_YY, v_plots[0], v_labels[0], plot_params, sim_wguide, ival, comp) elif comp == 'Hy' and EM_AC == 'EM_H': plot_component(v_x, v_y, v_XX, v_YY, v_plots[1], v_labels[1], plot_params, sim_wguide, ival, comp) elif comp == 'Hz' and EM_AC == 'EM_H': plot_component(v_x, v_y, v_XX, v_YY, v_plots[5], v_labels[5], plot_params, sim_wguide, ival, comp) elif comp == 'Habs' and EM_AC == 'EM_H': plot_component(v_x, v_y, v_XX, v_YY, v_plots[6], v_labels[6], plot_params, sim_wguide, ival, comp) elif comp == 'Ht' and EM_AC == 'EM_H': plot_component(v_x_q, v_y_q, v_XX, v_YY, vq_plots, '$(H_x,H_y)$', plot_params, sim_wguide, ival, comp) elif comp == 'ux' and EM_AC == 'AC': plot_component(v_x, v_y, v_XX, v_YY, v_plots[0], v_labels[0], plot_params, sim_wguide, ival, comp) elif comp == 'uy' and EM_AC == 'AC': plot_component(v_x, v_y, v_XX, v_YY, v_plots[1], v_labels[1], plot_params, sim_wguide, ival, comp) elif comp == 'uz' and EM_AC == 'AC': plot_component(v_x, v_y, v_XX, v_YY, v_plots[5], v_labels[5], plot_params, sim_wguide, ival, comp) elif comp == 'uabs' and EM_AC == 'AC': plot_component(v_x, v_y, v_XX, v_YY, v_plots[6], v_labels[6], plot_params, sim_wguide, ival, comp) elif comp == 'ut' and EM_AC == 'AC': plot_component(v_x_q, v_y_q, v_XX, v_YY, vq_plots, '$(u_x, u_y)$', plot_params, sim_wguide, ival, comp)
def calc_acoustic_losses( self, fixed_Q=None ): # TODO: make sure this is not done more than once on the same Simmo alpha = None if fixed_Q is None: self.Q_method = QAcMethod.Intrinsic # Calc alpha (loss) Eq. 45 print("Acoustic loss calc") nnodes = 6 # TODO: is this right? try: if self.EM_sim.structure.inc_shape in self.EM_sim.structure.linear_element_shapes: alpha = NumBAT.ac_alpha_int_v2( self.num_modes, self.n_msh_el, self.n_msh_pts, nnodes, self.table_nod, self.type_el, self.x_arr, self.structure.nb_typ_el_AC, self.structure.eta_tensor, self.k_AC, self.Omega_AC, self.sol1, # sim_AC.AC_mode_power) # appropriate for alpha in [1/m] self.AC_mode_energy) # appropriate for alpha in [1/s] else: if self.EM_sim.structure.inc_shape not in self.EM_sim.structure.curvilinear_element_shapes: print( "Warning: ac_alpha_int - not sure if mesh contains curvi-linear elements", "\n using slow quadrature integration by default.\n\n" ) Fortran_debug = 0 overlap = np.zeros( self.num_modes, dtype=complex ) # not sure why this is needed by ac_alpha_int alpha = NumBAT.ac_alpha_int( self.num_modes, self.n_msh_el, self.n_msh_pts, nnodes, self.table_nod, self.type_el, self.x_arr, self.structure.nb_typ_el_AC, self.structure.eta_tensor, self.k_AC, self.Omega_AC, self.sol1, # sim_AC.AC_mode_power, Fortran_debug) # appropriate for alpha in [1/m] self.AC_mode_energy, Fortran_debug) # appropriate for alpha in [1/s] except KeyboardInterrupt: print("\n\n Routine ac_alpha_int interrupted by keyboard.\n\n") self.ac_alpha_t = np.real(alpha) # Q_factors = 0.5*(k_AC/alpha)*np.ones(num_modes_AC) # appropriate for alpha in [1/m] self.ac_Qmech = 0.5 * ( np.real(self.Omega_AC) / self.ac_alpha_t) * np.ones( self.num_modes) # appropriate for alpha in [1/s] else: self.Q_method = QAcMethod.Fixed # factor of a 1/2 because alpha is for power! # alpha [1/m] = Omega_AC/(2*vg*fixed_Q) = k_AC/fixed_Q # alpha [1/s] = vg * alpha [1/m] # alpha [1/s] = Omega_AC/(2*fixed_Q) # alpha = 0.5*(k_AC/fixed_Q)*np.ones(num_modes_AC) # appropriate for alpha in [1/m] self.ac_Qmech = fixed_Q * np.ones(self.num_modes) self.ac_alpha_t = 0.5 * ( np.real(self.Omega_AC) / fixed_Q) * np.ones( self.num_modes) # appropriate for alpha in [1/s] self.ac_linewidth = self.ac_alpha_t / np.pi # SBS linewidth of each resonance in [Hz]