Beispiel #1
0
    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)
Beispiel #2
0
    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")
Beispiel #3
0
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
Beispiel #4
0
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()
Beispiel #5
0
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)
Beispiel #6
0
    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]