def calc_R_T_net(self, save_working=True): """ Calculate the scattering matrices for the stack as a whole. INPUTS: - `save_working` : If `True`, then store net reflection and transmission matrices at each part of the stack, in `self.R_net_list` and `self.T_net_list`, ordered with the reflection and tranmsission of the first/topmost finitely thick layer first. OUTPUTS: - `R_net` : Net reflection matrix - `T_net` : Net transmission matrix. """ self._check_periods_are_consistent() if save_working: self.R_net_list, self.T_net_list = [], [] # TODO: swap order of layers # Reflection and transmission at bottom of structure R_net, T_net = r_t_mat(self.layers[1], self.layers[0])[:2] lays = self.layers for lay, lay_t, h in zip(lays[1:-1], lays[2:], self.heights_norm()): if save_working: self.R_net_list.insert(0, R_net) self.T_net_list.insert(0, T_net) # lay (2) is the layer we're in right now # lay_t (1) is the layer above it # tf = T in forwards direction (down into lay) R12, T12, R21, T21 = r_t_mat(lay_t, lay) P = lay.prop_fwd(h) idm = np.eye(len(P)) # Matrix that maps vector of forward modes in medium 1 # at 1-2 interface, to fwd modes in medium 2 at 2-3 interface # P * (I - R21 * P * R2net * P)^-1 * T12 f12 = P * np.linalg.solve(idm - R21 * P * R_net * P, T12) T_net = T_net * f12 R_net = R12 + T21 * P * R_net * f12 self.R_net, self.T_net = R_net, T_net return self.R_net, self.T_net
def calc_R_T_net(self, save_working = True): """ Calculate the scattering matrices for the stack as a whole. INPUTS: - `save_working` : If `True`, then store net reflection and transmission matrices at each part of the stack, in `self.R_net_list` and `self.T_net_list`, ordered with the reflection and tranmsission of the first/topmost finitely thick layer first. OUTPUTS: - `R_net` : Net reflection matrix - `T_net` : Net transmission matrix. """ self._check_periods_are_consistent() if save_working: self.R_net_list, self.T_net_list = [], [] # TODO: swap order of layers # Reflection and transmission at bottom of structure R_net, T_net = r_t_mat(self.layers[1], self.layers[0])[:2] lays = self.layers for lay, lay_t, h in zip(lays[1:-1], lays[2:], self.heights_norm()): if save_working: self.R_net_list.insert(0, R_net) self.T_net_list.insert(0, T_net) # lay (2) is the layer we're in right now # lay_t (1) is the layer above it # tf = T in forwards direction (down into lay) R12, T12, R21, T21 = r_t_mat(lay_t, lay) P = lay.prop_fwd(h) idm = np.eye(len(P)) # Matrix that maps vector of forward modes in medium 1 # at 1-2 interface, to fwd modes in medium 2 at 2-3 interface # P * (I - R21 * P * R2net * P)^-1 * T12 f12 = P * np.linalg.solve(idm - R21 * P * R_net * P, T12) T_net = T_net * f12 R_net = R12 + T21 * P * R_net * f12 self.R_net, self.T_net = R_net, T_net return self.R_net, self.T_net
def calc_scat(self, pol='TE', incoming_amplitudes=None): """ Calculate the transmission and reflection matrices of the stack In relation to the FEM mesh the polarisation is orientated, - vertically for TE - horizontally for TM at normal incidence (polar angle theta = 0, azimuthal angle phi = 0). """ # Can check this against lines ~ 127 in J_overlap.f E components are TE, have diffraction # orders along beta, which is along y. # TODO: Switch to calc_R_T_net, which does not use infinitesimal air # layers. This will require rewriting the parts that calculate fluxes # through each layer. self._check_periods_are_consistent() nu_intfaces = 2 * (len(self.layers) - 1) neq_PW = self.layers[ 0].structure.num_pw_per_pol # assumes incident from homogeneous film PW_pols = 2 * neq_PW I_air = np.matrix(np.eye(PW_pols), dtype='D') """ Calculate net scattering matrices starting at the bottom 1 is infintesimal air layer 2 is medium in layer (symmetric as air on each side) (r)t12 and (r)tnet lists run from bottom to top! """ r12_list = [] r21_list = [] t12_list = [] t21_list = [] P_list = [] for st1 in self.layers: R12, T12, R21, T21 = r_t_mat(st1.air_ref(), st1) r12_list.append(R12) r21_list.append(R21) t12_list.append(T12) t21_list.append(T21) # Save the reflection matrices to the layers # (for easier introspection/testing) st1.R12, st1.T12, st1.R21, st1.T21 = R12, T12, R21, T21 # initiate (r)tnet as substrate top interface tnet_list = [] rnet_list = [] tnet = t12_list[0] rnet = r12_list[0] tnet_list.append(tnet) rnet_list.append(rnet) inv_t21_list = [] inv_t12_list = [] for i in range(1, len(self.layers) - 1): lay = self.layers[i] # through air layer at bottom of TF to_invert = (I_air - r12_list[i] * rnet) inverted_t21 = np.linalg.solve(to_invert, t21_list[i]) tnet = tnet * inverted_t21 rnet = r21_list[i] + t12_list[i] * rnet * inverted_t21 inv_t21_list.append(inverted_t21) tnet_list.append(tnet) rnet_list.append(rnet) # through TF layer P = lay.prop_fwd(self.heights_nm()[i - 1] / self.period) I_TF = np.matrix(np.eye(len(P)), dtype='D') to_invert = (I_TF - r21_list[i] * P * rnet * P) inverted_t12 = np.linalg.solve(to_invert, t12_list[i]) P_inverted_t12 = P * inverted_t12 tnet = tnet * P_inverted_t12 rnet = r12_list[i] + t21_list[i] * P * rnet * P_inverted_t12 to_invert_hat = (I_TF - r21_list[i] * P * r21_list[i] * P) inverted_t12_hat = np.linalg.solve(to_invert_hat, t12_list[i]) P_inverted_t12_hat = P * inverted_t12_hat P_list.append(P) inv_t12_list.append(inverted_t12) tnet_list.append(tnet) rnet_list.append(rnet) # into top semi-infinite medium to_invert = (I_air - r12_list[-1] * rnet) inverted_t21 = np.linalg.solve(to_invert, t21_list[-1]) tnet = tnet * inverted_t21 rnet = r21_list[-1] + t12_list[-1] * rnet * inverted_t21 inv_t21_list.append(inverted_t21) tnet_list.append(tnet) rnet_list.append(rnet) self.R_net, self.T_net = rnet, tnet """ Calculate field expansions for all layers (including air) starting at top Ordering is now top to bottom (inverse of above)! ie f1 is superstrate (top) Calculate net downward energy flux in each infintesimal air layer & super/substrates (see appendix C in Dossou et al. JOSA 2012) """ self.t_list = [] self.r_list = [] self.a_list = [] num_prop_air = self.layers[-1].air_ref().num_prop_pw_per_pol num_prop_in = self.layers[-1].num_prop_pw_per_pol num_prop_out = self.layers[0].num_prop_pw_per_pol out = self.layers[0].specular_order down_fluxes = [] up_flux = [] # Start by composing U matrix which is same for all air layers. # diagonal with 1 for propagating, i for evanescent TE and -i for evanescent TM plane wave orders U_mat = np.matrix(np.zeros((2 * PW_pols, 2 * PW_pols), complex)) for i in range(0, num_prop_air): U_mat[i, i] = 1.0 U_mat[neq_PW + i, neq_PW + i] = 1.0 U_mat[PW_pols + i, PW_pols + i] = -1.0 U_mat[PW_pols + neq_PW + i, PW_pols + neq_PW + i] = -1.0 for i in range(num_prop_air, neq_PW): U_mat[i, PW_pols + i] = -1.0j U_mat[neq_PW + i, PW_pols + neq_PW + i] = 1.0j U_mat[PW_pols + i, i] = 1.0j U_mat[PW_pols + neq_PW + i, neq_PW + i] = -1.0j if incoming_amplitudes is None: # Set the incident field to be a 0th order plane wave # in a given polarisation, from the semi-inf top layer d_minus = self.layers[-1].specular_incidence(pol) else: d_minus = incoming_amplitudes # total incoming flux flux_TE = np.linalg.norm(d_minus[0:num_prop_in])**2 flux_TM = np.linalg.norm(d_minus[neq_PW:neq_PW + num_prop_in])**2 down_fluxes.append(flux_TE + flux_TM) # up into semi-inf off top air gap d_plus = rnet_list[-1] * d_minus # total reflected flux flux_TE = np.linalg.norm(d_plus[0:num_prop_in])**2 flux_TM = np.linalg.norm(d_plus[neq_PW:neq_PW + num_prop_in])**2 up_flux.append(flux_TE + flux_TM) # incoming from semi-inf into top air gap f1_minus = inv_t21_list[-1] * d_minus for i in range(len(self.layers) - 2): f1_plus = rnet_list[-2 * i - 2] * f1_minus # net downward flux in infintesimal air layer f_mat = np.matrix(np.concatenate((f1_minus, f1_plus))) flux = f_mat.H * U_mat * f_mat down_fluxes.append(flux) f2_minus = inv_t12_list[-i - 1] * f1_minus f2_plus = rnet_list[-2 * i - 3] * P_list[-i - 1] * f2_minus f1_minus = inv_t21_list[-i - 2] * P_list[-i - 1] * f2_minus # bottom air to semi-inf substrate f1_plus = rnet_list[0] * f1_minus f2_minus = tnet_list[0] * f1_minus self.trans_vector = f2_minus flux_TE = np.linalg.norm(f2_minus[0:num_prop_out])**2 flux_TM = np.linalg.norm(f2_minus[neq_PW:neq_PW + num_prop_out])**2 down_fluxes.append(flux_TE + flux_TM) # calculate absorptance in each layer for i in range(1, len(down_fluxes) - 1): a_layer = abs(abs(down_fluxes[i]) - abs(down_fluxes[i + 1])) self.a_list.append(a_layer) a_layer = abs(down_fluxes[0] - down_fluxes[-1] - up_flux[0]) self.a_list.append(a_layer) # calculate reflectance in each layer for i in range(1, len(up_flux) - 1): r_layer = abs(abs(up_flux[i]) / abs(down_flux[i])) self.r_list.append(r_layer) r_layer = abs(up_flux[0] / down_fluxes[0]) self.r_list.append(r_layer) # calculate transmittance in each layer for i in range(0, len(down_fluxes) - 2): t_layer = abs(abs(down_fluxes[i + 2]) / abs(down_fluxes[i])) self.t_list.append(t_layer) t_layer = abs(down_fluxes[-1] / down_fluxes[0]) self.t_list.append(t_layer)
def calc_scat(self, pol='TE', incoming_amplitudes=None, calc_fluxes=True, save_scat_list=False): """ Calculate the transmission and reflection matrices of the stack. In relation to the FEM mesh the polarisation is orientated, - along the y axis for TE - along the x axis for TM at normal incidence (polar angle theta = 0, azimuthal angle phi = 0). Keyword Args: pol (str): Polarisation for which to calculate transmission \ & reflection. incoming_amplitudes (int): Which incoming PW order to give \ 1 unit of energy. If None the 0th order PW is selected. calc_fluxes (bool): Calculate energy fluxes. Only possible if \ top layer is a ThinFilm. save_scat_list (bool): If True, save tnet_list, rnet_list \ as property of stack for later access. """ # Can check this against lines ~ 127 in J_overlap.f E components are TE, have diffraction # orders along beta, which is along y. # TODO: Switch to calc_R_T_net, which does not use infinitesimal air # layers. This will require rewriting the parts that calculate fluxes # through each layer. self._check_periods_are_consistent() """ Calculate net scattering matrices starting at the bottom. 1 is infinitesimal air layer. 2 is medium in layer (symmetric as air on each side). (r)t12 and (r)tnet lists run from bottom to top! """ r12_list = [] r21_list = [] t12_list = [] t21_list = [] P_list = [] for st1 in self.layers: R12, T12, R21, T21 = r_t_mat(st1.air_ref(), st1) r12_list.append(R12) r21_list.append(R21) t12_list.append(T12) t21_list.append(T21) # Save the reflection matrices to the layers # (for easier introspection/testing) st1.R12, st1.T12, st1.R21, st1.T21 = R12, T12, R21, T21 PW_pols = np.shape(R12)[0] neq_PW = int(PW_pols/2.0) PW_pols = int(PW_pols) I_air = np.matrix(np.eye(PW_pols), dtype='D') # initiate (r)tnet as top interface of substrate tnet_list = [] rnet_list = [] tnet = t12_list[0] rnet = r12_list[0] tnet_list.append(tnet) rnet_list.append(rnet) inv_t21_list = [] inv_t12_list = [] for i in range(1, len(self.layers) - 1): lay = self.layers[i] # through air layer at bottom of layer if self.shears == None: to_invert = (I_air - r12_list[i]*rnet) inverted_t21 = np.linalg.solve(to_invert, t21_list[i]) tnet = tnet*inverted_t21 rnet = r21_list[i] + t12_list[i]*rnet*inverted_t21 else: coord_diff = np.asarray(self.shears[i-1]) - np.asarray(self.shears[i]) Q = st1.shear_transform(coord_diff) Q_inv = st1.shear_transform(-1*coord_diff) to_invert = (I_air - r12_list[i]*Q_inv*rnet*Q) inverted_t21 = np.linalg.solve(to_invert,t21_list[i]) tnet = tnet*Q*inverted_t21 rnet = r21_list[i] + t12_list[i]*Q_inv*rnet*Q*inverted_t21 inv_t21_list.append(inverted_t21) tnet_list.append(tnet) rnet_list.append(rnet) # through layer P = lay.prop_fwd(self.heights_nm()[i-1]/self.period) I_TF = np.matrix(np.eye(len(P)), dtype='D') to_invert = (I_TF - r21_list[i]*P*rnet*P) inverted_t12 = np.linalg.solve(to_invert, t12_list[i]) P_inverted_t12 = P*inverted_t12 tnet = tnet*P_inverted_t12 rnet = r12_list[i] + t21_list[i]*P*rnet*P_inverted_t12 P_list.append(P) inv_t12_list.append(inverted_t12) tnet_list.append(tnet) rnet_list.append(rnet) # into top semi-infinite medium if self.shears == None: to_invert = (I_air - r12_list[-1]*rnet) inverted_t21 = np.linalg.solve(to_invert,t21_list[-1]) tnet = tnet*inverted_t21 rnet = r21_list[-1] + t12_list[-1]*rnet*inverted_t21 else: coord_diff = np.asarray(self.shears[-1]) Q = st1.shear_transform(coord_diff) Q_inv = st1.shear_transform(-1*coord_diff) to_invert = (I_air - r12_list[-1]*Q_inv*rnet*Q) inverted_t21 = np.linalg.solve(to_invert,t21_list[-1]) tnet = tnet*Q*inverted_t21 rnet = r21_list[-1] + t12_list[-1]*Q_inv*rnet*Q*inverted_t21 inv_t21_list.append(inverted_t21) tnet_list.append(tnet) rnet_list.append(rnet) self.R_net, self.T_net = rnet, tnet if save_scat_list == True: self.tnet_list = tnet_list self.rnet_list = rnet_list if calc_fluxes == True: """ Calculate field expansions for all layers (including air) \ starting at top. Ordering is now top to bottom (inverse of above)! \ i.e. f1 is superstrate (top). Calculate net downward energy flux in each infinitesimal air layer \ & super/substrates (see appendix C in Dossou et al. JOSA 2012). """ self.t_list = [] self.r_list = [] self.a_list = [] num_prop_air = self.layers[-1].air_ref().num_prop_pw_per_pol num_prop_in = self.layers[-1].num_prop_pw_per_pol down_fluxes = [] up_flux = [] vec_coef_down = [] vec_coef_up = [] self.vec_coef_down = vec_coef_down self.vec_coef_up = vec_coef_up # Start by composing U matrix which is same for all air layers. # It is a diagonal matrix with 1 for propagating, i for evanescent TE # & -i for evanescent TM plane wave orders. U_mat = np.matrix(np.zeros((2*PW_pols, 2*PW_pols),complex)) for i in range(0, num_prop_air): U_mat[i, i] = 1.0 U_mat[neq_PW+i, neq_PW+i] = 1.0 U_mat[PW_pols+i, PW_pols+i] = -1.0 U_mat[PW_pols+neq_PW+i, PW_pols+neq_PW+i] = -1.0 for i in range(num_prop_air, neq_PW): U_mat[i, PW_pols+i] = -1.0j U_mat[neq_PW+i, PW_pols+neq_PW+i] = 1.0j U_mat[PW_pols+i, i] = 1.0j U_mat[PW_pols+neq_PW+i, neq_PW+i] = -1.0j if incoming_amplitudes is None: # Set the incident field to be a 0th order plane wave # in a given polarisation, from the semi-inf top layer d_minus = self.layers[-1].specular_incidence(pol) else: d_minus = incoming_amplitudes # total incoming flux flux_TE = np.linalg.norm(d_minus[0:num_prop_in])**2 flux_TM = np.linalg.norm(d_minus[neq_PW:neq_PW+num_prop_in])**2 down_fluxes.append(flux_TE + flux_TM) # up into semi-inf off top air gap d_plus = rnet_list[-1]*d_minus self.vec_coef_down.append(d_minus) self.vec_coef_up.append(d_plus) # total reflected flux flux_TE = np.linalg.norm(d_plus[0:num_prop_in])**2 flux_TM = np.linalg.norm(d_plus[neq_PW:neq_PW+num_prop_in])**2 up_flux.append(flux_TE + flux_TM) # incoming from semi-inf into top air gap f1_minus = inv_t21_list[-1]*d_minus for i in range(len(self.layers) - 2): f1_plus = rnet_list[-2*i-2]*f1_minus # net downward flux in infinitesimal air layer f_mat = np.matrix(np.concatenate((f1_minus, f1_plus))) flux = f_mat.H*U_mat*f_mat down_fluxes.append(flux) f2_minus = inv_t12_list[-i-1]*f1_minus f2_plus = rnet_list[-2*i-3]*P_list[-i-1]*f2_minus self.vec_coef_down.append(f2_minus) self.vec_coef_up.append(f2_plus) f1_minus = inv_t21_list[-i-2]*P_list[-i-1]*f2_minus # bottom air to semi-inf substrate f1_plus = rnet_list[0]*f1_minus f2_minus = tnet_list[0]*f1_minus self.vec_coef_down.append(f2_minus) # self.trans_vector = f2_minus # can only calculate the energy flux in homogeneous films if isinstance(self.layers[0], Anallo): num_prop_out = self.layers[0].num_prop_pw_per_pol if num_prop_out != 0: # out = self.layers[0].specular_order flux_TE = np.linalg.norm(f2_minus[0:num_prop_out])**2 flux_TM = np.linalg.norm(f2_minus[neq_PW:neq_PW+num_prop_out])**2 down_fluxes.append(flux_TE + flux_TM) else: print("Warning: there are no propagating modes in the semi-inf \n substrate therefore cannot calculate energy fluxes here.") num_prop_in = self.layers[-1].num_prop_pw_per_pol if num_prop_out != 0: # calculate absorptance in each layer for i in range(1 , len(down_fluxes)-1): a_layer = abs(abs(down_fluxes[i])-abs(down_fluxes[i+1])) self.a_list.append(a_layer) a_layer = abs(down_fluxes[0]-down_fluxes[-1]-up_flux[0]) self.a_list.append(a_layer) # calculate reflectance in each layer for i in range(1, len(up_flux)-1): r_layer = abs(up_flux[i])/abs(down_fluxes[i]) self.r_list.append(r_layer) r_layer = abs(up_flux[0]/down_fluxes[0]) self.r_list.append(r_layer) # calculate transmittance in each layer for i in range(0, len(down_fluxes)-2): t_layer = abs(abs(down_fluxes[i+2])/abs(down_fluxes[i])) self.t_list.append(t_layer) t_layer = abs(down_fluxes[-1]/down_fluxes[0]) self.t_list.append(t_layer) else: self.a_list = np.zeros(len(self.layers) - 2) self.r_list = np.zeros(len(self.layers) - 2) self.t_list = np.zeros(len(self.layers) - 2) print("Warning: there are no propagating modes in the semi-inf \n superstrate therefore CANNOT CALCULATE ENERGY FLUXES ANYWHERE.") else: # calculate absorptance in each layer for i in range(1, len(down_fluxes)-1): a_layer = abs(abs(down_fluxes[i])-abs(down_fluxes[i+1])) self.a_list.append(a_layer) a_layer = abs(down_fluxes[0]-down_fluxes[-1]-up_flux[0]) self.a_list.append(a_layer) # calculate reflectance in each layer for i in range(1, len(up_flux)-1): r_layer = abs(up_flux[i])/abs(down_fluxes[i]) self.r_list.append(r_layer) r_layer = abs(up_flux[0]/down_fluxes[0]) self.r_list.append(r_layer) # calculate transmittance in each layer for i in range(0, len(down_fluxes)-2): t_layer = abs(down_fluxes[i+2])/abs(down_fluxes[i]) self.t_list.append(t_layer) t_layer = abs(down_fluxes[-1]/down_fluxes[0]) self.t_list.append(t_layer)
def calc_scat(self, pol='TE', incoming_amplitudes=None, calc_fluxes=True, save_scat_list=False): """ Calculate the transmission and reflection matrices of the stack. In relation to the FEM mesh the polarisation is orientated, - along the y axis for TE - along the x axis for TM at normal incidence (polar angle theta = 0, azimuthal angle phi = 0). Keyword Args: pol (str): Polarisation for which to calculate transmission \ & reflection. incoming_amplitudes (int): Which incoming PW order to give \ 1 unit of energy. If None the 0th order PW is selected. calc_fluxes (bool): Calculate energy fluxes. Only possible if \ top layer is a ThinFilm. save_scat_list (bool): If True, save tnet_list, rnet_list \ as property of stack for later access. """ # Can check this against lines ~ 127 in J_overlap.f E components are TE, have diffraction # orders along beta, which is along y. # TODO: Switch to calc_R_T_net, which does not use infinitesimal air # layers. This will require rewriting the parts that calculate fluxes # through each layer. self._check_periods_are_consistent() """ Calculate net scattering matrices starting at the bottom. 1 is infinitesimal air layer. 2 is medium in layer (symmetric as air on each side). (r)t12 and (r)tnet lists run from bottom to top! """ r12_list = [] r21_list = [] t12_list = [] t21_list = [] P_list = [] for st1 in self.layers: R12, T12, R21, T21 = r_t_mat(st1.air_ref(), st1) r12_list.append(R12) r21_list.append(R21) t12_list.append(T12) t21_list.append(T21) # Save the reflection matrices to the layers # (for easier introspection/testing) st1.R12, st1.T12, st1.R21, st1.T21 = R12, T12, R21, T21 PW_pols = np.shape(R12)[0] neq_PW = int(PW_pols / 2.0) PW_pols = int(PW_pols) I_air = np.matrix(np.eye(PW_pols), dtype='D') # initiate (r)tnet as top interface of substrate tnet_list = [] rnet_list = [] tnet = t12_list[0] rnet = r12_list[0] tnet_list.append(tnet) rnet_list.append(rnet) inv_t21_list = [] inv_t12_list = [] for i in range(1, len(self.layers) - 1): lay = self.layers[i] # through air layer at bottom of layer if self.shears == None: to_invert = (I_air - r12_list[i] * rnet) inverted_t21 = np.linalg.solve(to_invert, t21_list[i]) tnet = tnet * inverted_t21 rnet = r21_list[i] + t12_list[i] * rnet * inverted_t21 else: coord_diff = np.asarray(self.shears[i - 1]) - np.asarray( self.shears[i]) Q = st1.shear_transform(coord_diff) Q_inv = st1.shear_transform(-1 * coord_diff) to_invert = (I_air - r12_list[i] * Q_inv * rnet * Q) inverted_t21 = np.linalg.solve(to_invert, t21_list[i]) tnet = tnet * Q * inverted_t21 rnet = r21_list[ i] + t12_list[i] * Q_inv * rnet * Q * inverted_t21 inv_t21_list.append(inverted_t21) tnet_list.append(tnet) rnet_list.append(rnet) # through layer P = lay.prop_fwd(self.heights_nm()[i - 1] / self.period) I_TF = np.matrix(np.eye(len(P)), dtype='D') to_invert = (I_TF - r21_list[i] * P * rnet * P) inverted_t12 = np.linalg.solve(to_invert, t12_list[i]) P_inverted_t12 = P * inverted_t12 tnet = tnet * P_inverted_t12 rnet = r12_list[i] + t21_list[i] * P * rnet * P_inverted_t12 P_list.append(P) inv_t12_list.append(inverted_t12) tnet_list.append(tnet) rnet_list.append(rnet) # into top semi-infinite medium if self.shears == None: to_invert = (I_air - r12_list[-1] * rnet) inverted_t21 = np.linalg.solve(to_invert, t21_list[-1]) tnet = tnet * inverted_t21 rnet = r21_list[-1] + t12_list[-1] * rnet * inverted_t21 else: coord_diff = np.asarray(self.shears[-1]) Q = st1.shear_transform(coord_diff) Q_inv = st1.shear_transform(-1 * coord_diff) to_invert = (I_air - r12_list[-1] * Q_inv * rnet * Q) inverted_t21 = np.linalg.solve(to_invert, t21_list[-1]) tnet = tnet * Q * inverted_t21 rnet = r21_list[-1] + t12_list[-1] * Q_inv * rnet * Q * inverted_t21 inv_t21_list.append(inverted_t21) tnet_list.append(tnet) rnet_list.append(rnet) self.R_net, self.T_net = rnet, tnet if save_scat_list == True: self.tnet_list = tnet_list self.rnet_list = rnet_list if calc_fluxes == True: """ Calculate field expansions for all layers (including air) \ starting at top. Ordering is now top to bottom (inverse of above)! \ i.e. f1 is superstrate (top). Calculate net downward energy flux in each infinitesimal air layer \ & super/substrates (see appendix C in Dossou et al. JOSA 2012). """ self.t_list = [] self.r_list = [] self.a_list = [] num_prop_air = self.layers[-1].air_ref().num_prop_pw_per_pol num_prop_in = self.layers[-1].num_prop_pw_per_pol down_fluxes = [] up_flux = [] vec_coef_down = [] vec_coef_up = [] self.vec_coef_down = vec_coef_down self.vec_coef_up = vec_coef_up # Start by composing U matrix which is same for all air layers. # It is a diagonal matrix with 1 for propagating, i for evanescent TE # & -i for evanescent TM plane wave orders. U_mat = np.matrix(np.zeros((2 * PW_pols, 2 * PW_pols), complex)) for i in range(0, num_prop_air): U_mat[i, i] = 1.0 U_mat[neq_PW + i, neq_PW + i] = 1.0 U_mat[PW_pols + i, PW_pols + i] = -1.0 U_mat[PW_pols + neq_PW + i, PW_pols + neq_PW + i] = -1.0 for i in range(num_prop_air, neq_PW): U_mat[i, PW_pols + i] = -1.0j U_mat[neq_PW + i, PW_pols + neq_PW + i] = 1.0j U_mat[PW_pols + i, i] = 1.0j U_mat[PW_pols + neq_PW + i, neq_PW + i] = -1.0j if incoming_amplitudes is None: # Set the incident field to be a 0th order plane wave # in a given polarisation, from the semi-inf top layer d_minus = self.layers[-1].specular_incidence(pol) else: d_minus = incoming_amplitudes # total incoming flux flux_TE = np.linalg.norm(d_minus[0:num_prop_in])**2 flux_TM = np.linalg.norm(d_minus[neq_PW:neq_PW + num_prop_in])**2 down_fluxes.append(flux_TE + flux_TM) # up into semi-inf off top air gap d_plus = rnet_list[-1] * d_minus self.vec_coef_down.append(d_minus) self.vec_coef_up.append(d_plus) # total reflected flux flux_TE = np.linalg.norm(d_plus[0:num_prop_in])**2 flux_TM = np.linalg.norm(d_plus[neq_PW:neq_PW + num_prop_in])**2 up_flux.append(flux_TE + flux_TM) # incoming from semi-inf into top air gap f1_minus = inv_t21_list[-1] * d_minus for i in range(len(self.layers) - 2): f1_plus = rnet_list[-2 * i - 2] * f1_minus # net downward flux in infinitesimal air layer f_mat = np.matrix(np.concatenate((f1_minus, f1_plus))) flux = f_mat.H * U_mat * f_mat down_fluxes.append(flux) f2_minus = inv_t12_list[-i - 1] * f1_minus f2_plus = rnet_list[-2 * i - 3] * P_list[-i - 1] * f2_minus self.vec_coef_down.append(f2_minus) self.vec_coef_up.append(f2_plus) f1_minus = inv_t21_list[-i - 2] * P_list[-i - 1] * f2_minus # bottom air to semi-inf substrate f1_plus = rnet_list[0] * f1_minus f2_minus = tnet_list[0] * f1_minus self.vec_coef_down.append(f2_minus) # self.trans_vector = f2_minus # can only calculate the energy flux in homogeneous films if isinstance(self.layers[0], Anallo): num_prop_out = self.layers[0].num_prop_pw_per_pol if num_prop_out != 0: # out = self.layers[0].specular_order flux_TE = np.linalg.norm(f2_minus[0:num_prop_out])**2 flux_TM = np.linalg.norm(f2_minus[neq_PW:neq_PW + num_prop_out])**2 down_fluxes.append(flux_TE + flux_TM) else: print( "Warning: there are no propagating modes in the semi-inf \n substrate therefore cannot calculate energy fluxes here." ) num_prop_in = self.layers[-1].num_prop_pw_per_pol if num_prop_out != 0: # calculate absorptance in each layer for i in range(1, len(down_fluxes) - 1): a_layer = abs( abs(down_fluxes[i]) - abs(down_fluxes[i + 1])) self.a_list.append(a_layer) a_layer = abs(down_fluxes[0] - down_fluxes[-1] - up_flux[0]) self.a_list.append(a_layer) # calculate reflectance in each layer for i in range(1, len(up_flux) - 1): r_layer = abs(up_flux[i]) / abs(down_fluxes[i]) self.r_list.append(r_layer) r_layer = abs(up_flux[0] / down_fluxes[0]) self.r_list.append(r_layer) # calculate transmittance in each layer for i in range(0, len(down_fluxes) - 2): t_layer = abs( abs(down_fluxes[i + 2]) / abs(down_fluxes[i])) self.t_list.append(t_layer) t_layer = abs(down_fluxes[-1] / down_fluxes[0]) self.t_list.append(t_layer) else: self.a_list = np.zeros(len(self.layers) - 2) self.r_list = np.zeros(len(self.layers) - 2) self.t_list = np.zeros(len(self.layers) - 2) print( "Warning: there are no propagating modes in the semi-inf \n superstrate therefore CANNOT CALCULATE ENERGY FLUXES ANYWHERE." ) else: # calculate absorptance in each layer for i in range(1, len(down_fluxes) - 1): a_layer = abs( abs(down_fluxes[i]) - abs(down_fluxes[i + 1])) self.a_list.append(a_layer) a_layer = abs(down_fluxes[0] - down_fluxes[-1] - up_flux[0]) self.a_list.append(a_layer) # calculate reflectance in each layer for i in range(1, len(up_flux) - 1): r_layer = abs(up_flux[i]) / abs(down_fluxes[i]) self.r_list.append(r_layer) r_layer = abs(up_flux[0] / down_fluxes[0]) self.r_list.append(r_layer) # calculate transmittance in each layer for i in range(0, len(down_fluxes) - 2): t_layer = abs(down_fluxes[i + 2]) / abs(down_fluxes[i]) self.t_list.append(t_layer) t_layer = abs(down_fluxes[-1] / down_fluxes[0]) self.t_list.append(t_layer)
def calc_scat(self, pol = 'TE', incoming_amplitudes = None): """ Calculate the transmission and reflection matrices of the stack In relation to the FEM mesh the polarisation is orientated, - vertically for TE - horizontally for TM at normal incidence (polar angle theta = 0, azimuthal angle phi = 0). """ # Can check this against lines ~ 127 in J_overlap.f E components are TE, have diffraction # orders along beta, which is along y. # TODO: Switch to calc_R_T_net, which does not use infinitesimal air # layers. This will require rewriting the parts that calculate fluxes # through each layer. self._check_periods_are_consistent() nu_intfaces = 2*(len(self.layers)-1) neq_PW = self.layers[0].structure.num_pw_per_pol # assumes incident from homogeneous film PW_pols = 2*neq_PW I_air = np.matrix(np.eye(PW_pols),dtype='D') """ Calculate net scattering matrices starting at the bottom 1 is infintesimal air layer 2 is medium in layer (symmetric as air on each side) (r)t12 and (r)tnet lists run from bottom to top! """ r12_list = [] r21_list = [] t12_list = [] t21_list = [] P_list = [] for st1 in self.layers: R12, T12, R21, T21 = r_t_mat(st1.air_ref(), st1) r12_list.append(R12) r21_list.append(R21) t12_list.append(T12) t21_list.append(T21) # Save the reflection matrices to the layers # (for easier introspection/testing) st1.R12, st1.T12, st1.R21, st1.T21 = R12, T12, R21, T21 # initiate (r)tnet as substrate top interface tnet_list = [] rnet_list = [] tnet = t12_list[0] rnet = r12_list[0] tnet_list.append(tnet) rnet_list.append(rnet) inv_t21_list = [] inv_t12_list = [] for i in range(1, len(self.layers) - 1): lay = self.layers[i] # through air layer at bottom of TF to_invert = (I_air - r12_list[i]*rnet) inverted_t21 = np.linalg.solve(to_invert,t21_list[i]) tnet = tnet*inverted_t21 rnet = r21_list[i] + t12_list[i]*rnet*inverted_t21 inv_t21_list.append(inverted_t21) tnet_list.append(tnet) rnet_list.append(rnet) # through TF layer P = lay.prop_fwd(self.heights_nm()[i-1]/self.period) I_TF = np.matrix(np.eye(len(P)),dtype='D') to_invert = (I_TF - r21_list[i]*P*rnet*P) inverted_t12 = np.linalg.solve(to_invert,t12_list[i]) P_inverted_t12 = P*inverted_t12 tnet = tnet*P_inverted_t12 rnet = r12_list[i] + t21_list[i]*P*rnet*P_inverted_t12 to_invert_hat = (I_TF - r21_list[i]*P*r21_list[i]*P) inverted_t12_hat = np.linalg.solve(to_invert_hat,t12_list[i]) P_inverted_t12_hat = P*inverted_t12_hat P_list.append(P) inv_t12_list.append(inverted_t12) tnet_list.append(tnet) rnet_list.append(rnet) # into top semi-infinite medium to_invert = (I_air - r12_list[-1]*rnet) inverted_t21 = np.linalg.solve(to_invert,t21_list[-1]) tnet = tnet*inverted_t21 rnet = r21_list[-1] + t12_list[-1]*rnet*inverted_t21 inv_t21_list.append(inverted_t21) tnet_list.append(tnet) rnet_list.append(rnet) self.R_net, self.T_net = rnet, tnet """ Calculate field expansions for all layers (including air) starting at top Ordering is now top to bottom (inverse of above)! ie f1 is superstrate (top) Calculate net downward energy flux in each infintesimal air layer & super/substrates (see appendix C in Dossou et al. JOSA 2012) """ self.t_list = [] self.r_list = [] self.a_list = [] num_prop_air = self.layers[-1].air_ref().num_prop_pw_per_pol num_prop_in = self.layers[-1].num_prop_pw_per_pol num_prop_out = self.layers[0].num_prop_pw_per_pol out = self.layers[0].specular_order down_fluxes = [] up_flux = [] # Start by composing U matrix which is same for all air layers. # diagonal with 1 for propagating, i for evanescent TE and -i for evanescent TM plane wave orders U_mat = np.matrix(np.zeros((2*PW_pols, 2*PW_pols),complex)) for i in range(0,num_prop_air): U_mat[i,i] = 1.0 U_mat[neq_PW+i,neq_PW+i] = 1.0 U_mat[PW_pols+i,PW_pols+i] = -1.0 U_mat[PW_pols+neq_PW+i,PW_pols+neq_PW+i] = -1.0 for i in range(num_prop_air,neq_PW): U_mat[i,PW_pols+i] = -1.0j U_mat[neq_PW+i,PW_pols+neq_PW+i] = 1.0j U_mat[PW_pols+i,i] = 1.0j U_mat[PW_pols+neq_PW+i,neq_PW+i] = -1.0j if incoming_amplitudes is None: # Set the incident field to be a 0th order plane wave # in a given polarisation, from the semi-inf top layer d_minus = self.layers[-1].specular_incidence(pol) else: d_minus = incoming_amplitudes # total incoming flux flux_TE = np.linalg.norm(d_minus[0:num_prop_in])**2 flux_TM = np.linalg.norm(d_minus[neq_PW:neq_PW+num_prop_in])**2 down_fluxes.append(flux_TE + flux_TM) # up into semi-inf off top air gap d_plus = rnet_list[-1]*d_minus # total reflected flux flux_TE = np.linalg.norm(d_plus[0:num_prop_in])**2 flux_TM = np.linalg.norm(d_plus[neq_PW:neq_PW+num_prop_in])**2 up_flux.append(flux_TE + flux_TM) # incoming from semi-inf into top air gap f1_minus = inv_t21_list[-1]*d_minus for i in range(len(self.layers) - 2): f1_plus = rnet_list[-2*i-2]*f1_minus # net downward flux in infintesimal air layer f_mat = np.matrix(np.concatenate((f1_minus,f1_plus))) flux = f_mat.H*U_mat*f_mat down_fluxes.append(flux) f2_minus = inv_t12_list[-i-1]*f1_minus f2_plus = rnet_list[-2*i-3]*P_list[-i-1]*f2_minus f1_minus = inv_t21_list[-i-2]*P_list[-i-1]*f2_minus # bottom air to semi-inf substrate f1_plus = rnet_list[0]*f1_minus f2_minus = tnet_list[0]*f1_minus self.trans_vector = f2_minus flux_TE = np.linalg.norm(f2_minus[0:num_prop_out])**2 flux_TM = np.linalg.norm(f2_minus[neq_PW:neq_PW+num_prop_out])**2 down_fluxes.append(flux_TE + flux_TM) # calculate absorptance in each layer for i in range(1,len(down_fluxes)-1): a_layer = abs(abs(down_fluxes[i])-abs(down_fluxes[i+1])) self.a_list.append(a_layer) a_layer = abs(down_fluxes[0]-down_fluxes[-1]-up_flux[0]) self.a_list.append(a_layer) # calculate reflectance in each layer for i in range(1,len(up_flux)-1): r_layer = abs(abs(up_flux[i])/abs(down_flux[i])) self.r_list.append(r_layer) r_layer = abs(up_flux[0]/down_fluxes[0]) self.r_list.append(r_layer) # calculate transmittance in each layer for i in range(0,len(down_fluxes)-2): t_layer = abs(abs(down_fluxes[i+2])/abs(down_fluxes[i])) self.t_list.append(t_layer) t_layer = abs(down_fluxes[-1]/down_fluxes[0]) self.t_list.append(t_layer)