def out_to_in_matrix(phi_sym, angle_vector, theta_intv, phi_intv): if phi_sym == 2 * np.pi: phi_sym = phi_sym - 0.0001 out_to_in = np.zeros((len(angle_vector), len(angle_vector))) binned_theta_out = np.digitize( np.pi - angle_vector[:, 1], theta_intv, right=True) - 1 phi_rebin = fold_phi(angle_vector[:, 2] + np.pi, phi_sym) phi_out = xr.DataArray( phi_rebin, coords={'theta_bin': (['angle_in'], binned_theta_out)}, dims=['angle_in']) bin_out = phi_out.groupby('theta_bin').apply(overall_bin, args=(phi_intv, angle_vector[:, 0])).data out_to_in[bin_out, np.arange(len(angle_vector))] = 1 up_to_down = out_to_in[int(len(angle_vector) / 2):, :int(len(angle_vector) / 2)] down_to_up = out_to_in[:int(len(angle_vector) / 2), int(len(angle_vector) / 2):] return COO(up_to_down), COO(down_to_up)
def RT_wl(i1, wl, n_angles, nx, ny, widths, thetas_in, phis_in, h, xs, ys, nks, surfaces, pol, phi_sym, theta_intv, phi_intv, angle_vector, Fr_or_TMM, n_abs_layers, lookuptable, calc_profile, nm_spacing, side): print('wavelength = ', wl) theta_out = np.zeros((n_angles, nx * ny)) phi_out = np.zeros((n_angles, nx * ny)) A_surface_layers = np.zeros((n_angles, nx*ny, n_abs_layers)) theta_local_incidence = np.zeros((n_angles, nx*ny)) for i2 in range(n_angles): theta = thetas_in[i2] phi = phis_in[i2] r = abs((h + 1) / cos(theta)) r_a_0 = np.real(np.array([r * sin(theta) * cos(phi), r * sin(theta) * sin(phi), r * cos(theta)])) for c, vals in enumerate(product(xs, ys)): I, th_o, phi_o, surface_A = \ single_ray_interface(vals[0], vals[1], nks[:, i1], r_a_0, theta, phi, surfaces, pol, wl, Fr_or_TMM, lookuptable) if th_o < 0: # can do outside loup with np.where th_o = -th_o phi_o = phi_o + np.pi theta_out[i2, c] = th_o phi_out[i2, c] = phi_o A_surface_layers[i2, c] = surface_A[0] theta_local_incidence[i2, c] = np.real(surface_A[1]) #phi_out[theta_out < 0] = phi_out + np.pi #theta_out = abs(theta_out) # discards info about phi! phi_out = fold_phi(phi_out, phi_sym) phis_in = fold_phi(phis_in, phi_sym) if side == -1: not_absorbed = np.where(theta_out < (np.pi+0.1)) thetas_in = np.pi-thetas_in #phis_in = np.pi-phis_in # unsure about this part theta_out[not_absorbed] = np.pi-theta_out[not_absorbed] #phi_out = np.pi-phi_out # unsure about this part #phi_out = fold_phi(phi_out, phi_sym) #phis_in = fold_phi(phis_in, phi_sym) #theta_out = abs(theta_out) # discards info about phi! theta_local_incidence = np.abs(theta_local_incidence) n_thetas = len(theta_intv) - 1 if Fr_or_TMM > 0: # now we need to make bins for the absorption theta_intv = np.append(theta_intv, 11) phi_intv = phi_intv + [np.array([0])] # xarray: can use coordinates in calculations using apply! binned_theta_in = np.digitize(thetas_in, theta_intv, right=True) - 1 binned_theta_out = np.digitize(theta_out, theta_intv, right=True) - 1 #print(binned_theta_out, theta_out, theta_intv) #print(binned_theta_in) #print(binned_theta_out) # -1 to give the correct index for the bins in phi_intv phi_in = xr.DataArray(phis_in, coords={'theta_bin': (['angle_in'], binned_theta_in)}, dims=['angle_in']) bin_in = phi_in.groupby('theta_bin').apply(overall_bin, args=(phi_intv, angle_vector[:, 0])).data phi_out = xr.DataArray(phi_out, coords={'theta_bin': (['angle_in', 'position'], binned_theta_out)}, dims=['angle_in', 'position']) bin_out = phi_out.groupby('theta_bin').apply(overall_bin, args=(phi_intv, angle_vector[:, 0])).data out_mat = np.zeros((len(angle_vector), int(len(angle_vector) / 2))) # everything is coming in from above so we don't need 90 -> 180 in incoming bins A_mat = np.zeros((n_abs_layers, int(len(angle_vector)/2))) n_rays_in_bin = np.zeros(int(len(angle_vector) / 2)) n_rays_in_bin_abs = np.zeros(int(len(angle_vector) / 2)) binned_local_angles = np.digitize(theta_local_incidence, theta_intv, right=True) - 1 local_angle_mat = np.zeros((int((len(theta_intv) -1 )/ 2), int(len(angle_vector) / 2))) if side == 1: offset = 0 else: offset = int(len(angle_vector) / 2) for l1 in range(len(thetas_in)): for l2 in range(nx * ny): n_rays_in_bin[bin_in[l1]-offset] += 1 if binned_theta_out[l1, l2] <= (n_thetas-1): # reflected or transmitted out_mat[bin_out[l1, l2], bin_in[l1]-offset] += 1 #print('RT bin in-offset', bin_in[l1]-offset) #print(thetas_in[l1], binned_theta_out[l1, l2]) else: # absorbed in one of the surface layers n_rays_in_bin_abs[bin_in[l1]-offset] += 1 #print('A bin in', bin_in[l1]-offset, l1, l2) per_layer = A_surface_layers[l1, l2] A_mat[:, bin_in[l1]-offset] += per_layer local_angle_mat[binned_local_angles[l1, l2], bin_in[l1]-offset] += 1 # normalize out_mat = out_mat/n_rays_in_bin overall_abs_frac = n_rays_in_bin_abs/n_rays_in_bin abs_scale = overall_abs_frac/np.sum(A_mat, 0) #print('A_mat', np.sum(A_mat, 0)/n_rays_in_bin_abs) intgr = np.sum(A_mat, 0)/n_rays_in_bin_abs A_mat = abs_scale*A_mat out_mat[np.isnan(out_mat)] = 0 A_mat[np.isnan(A_mat)] = 0 out_mat = COO(out_mat) # sparse matrix A_mat = COO(A_mat) if Fr_or_TMM > 0: local_angle_mat = local_angle_mat/np.sum(local_angle_mat, 0) local_angle_mat[np.isnan(local_angle_mat)] = 0 local_angle_mat = COO(local_angle_mat) #print(calc_profile) if len(calc_profile) > 0: n_a_in = int(len(angle_vector)/2) thetas = angle_vector[:n_a_in, 1] unique_thetas = np.unique(thetas) #from profiles need: project name, pol, nm_spacing #print(local_angle_mat.todense()) #print('going into making profiles') profile = make_profiles_wl(unique_thetas, n_a_in, side, widths, local_angle_mat, wl, lookuptable, pol, nm_spacing, calc_profile) intgr = xr.DataArray(intgr, dims=['global_index'], coords={'global_index': np.arange(0, n_a_in)}).fillna(0) return out_mat, A_mat, local_angle_mat, profile, intgr else: return out_mat, A_mat, local_angle_mat else: return out_mat, A_mat
def mirror_matrix(angle_vector, theta_intv, phi_intv, surf_name, options, front_or_rear='front', save=True): if save: structpath = os.path.join( results_path, options['project_name']) # also need this to get lookup table if not os.path.isdir(structpath): os.mkdir(structpath) savepath_RT = os.path.join(structpath, surf_name + front_or_rear + 'RT.npz') savepath_A = os.path.join(structpath, surf_name + front_or_rear + 'A.npz') if os.path.isfile(savepath_RT) and save: print('Existing angular redistribution matrices found') allArray = load_npz(savepath_RT) else: if front_or_rear == "front": angle_vector_th = angle_vector[:int(len(angle_vector) / 2), 1] angle_vector_phi = angle_vector[:int(len(angle_vector) / 2), 2] phis_out = fold_phi(angle_vector_phi + np.pi, options['phi_symmetry']) else: angle_vector_th = angle_vector[int(len(angle_vector) / 2):, 1] angle_vector_phi = angle_vector[int(len(angle_vector) / 2):, 2] phis_out = fold_phi(angle_vector_phi + np.pi, options['phi_symmetry']) # matrix will be all zeros with just one '1' in each column/row. Just need to determine where it goes binned_theta = np.digitize(angle_vector_th, theta_intv, right=True) - 1 # print(binned_theta_out, theta_out, theta_intv) # print(binned_theta_in) # print(binned_theta_out) # -1 to give the correct index for the bins in phi_intv bin_in = np.arange(len(angle_vector_phi)) phi_ind = [ np.digitize(phi, phi_intv[binned_theta[i1]], right=True) - 1 for i1, phi in enumerate(phis_out) ] overall_bin = [ np.argmin(abs(angle_vector[:, 0] - binned_theta[i1])) + phi_i for i1, phi_i in enumerate(phi_ind) ] whole_matrix = np.zeros((len(overall_bin) * 2, len(overall_bin))) whole_matrix[overall_bin, bin_in] = 1 A_matrix = np.zeros((1, len(overall_bin))) allArray = COO(whole_matrix) absArray = COO(A_matrix) if save: save_npz(savepath_RT, allArray) save_npz(savepath_A, absArray) return allArray
def TMM(layers, incidence, transmission, surf_name, options, coherent=True, coherency_list=None, prof_layers=[], front_or_rear='front', save=True): """Function which takes a layer stack and creates an angular redistribution matrix. :param layers: A list with one or more layers. :param transmission: transmission medium :param incidence: incidence medium :param surf_name: name of the surface (to save the matrices generated. :param options: a list of options :param coherent: whether or not the layer stack is coherent. If None, it is assumed to be fully coherent :param coherency: a list with the same number of entries as the layers, either 'c' for a coherent layer or 'i' for an incoherent layer :param prof_layers: layers for which the absorption profile should be calculated (if None, do not calculate absorption profile at all) :param front_or_rear: a string, either 'front' or 'rear'; front incidence on the stack, from the incidence medium, or rear incidence on the stack, from the transmission medium. :return full_mat: R and T redistribution matrix :return A_mat: matrix describing absorption per layer """ def make_matrix_wl(wl): # binning into matrix, including phi RT_mat = np.zeros((len(theta_bins_in)*2, len(theta_bins_in))) A_mat = np.zeros((n_layers, len(theta_bins_in))) for i1 in range(len(theta_bins_in)): theta = theta_lookup[i1]#angle_vector[i1, 1] data = allres.loc[dict(angle=theta, wl=wl)] R_prob = np.real(data['R'].data.item(0)) T_prob = np.real(data['T'].data.item(0)) Alayer_prob = np.real(data['Alayer'].data) phi_out = phis_out[i1] #print(R_prob, T_prob) # reflection phi_int = phi_intv[theta_bins_in[i1]] phi_ind = np.digitize(phi_out, phi_int, right=True) - 1 bin_out_r = np.argmin(abs(angle_vector[:, 0] - theta_bins_in[i1])) + phi_ind #print(bin_out_r, i1+offset) RT_mat[bin_out_r, i1] = R_prob #print(R_prob) # transmission theta_t = np.abs(-np.arcsin((inc.n(wl * 1e-9) / trns.n(wl * 1e-9)) * np.sin(theta_lookup[i1])) + quadrant) #print('angle in, transmitted', angle_vector_th[i1], theta_t) # theta switches half-plane (th < 90 -> th >90 if ~np.isnan(theta_t): theta_out_bin = np.digitize(theta_t, theta_intv, right=True) - 1 phi_int = phi_intv[theta_out_bin] phi_ind = np.digitize(phi_out, phi_int, right=True) - 1 bin_out_t = np.argmin(abs(angle_vector[:, 0] - theta_out_bin)) + phi_ind RT_mat[bin_out_t, i1] = T_prob #print(bin_out_t, i1+offset) # absorption A_mat[:, i1] = Alayer_prob fullmat = COO(RT_mat) A_mat = COO(A_mat) return fullmat, A_mat structpath = os.path.join(results_path, options['project_name']) if not os.path.isdir(structpath): os.mkdir(structpath) savepath_RT = os.path.join(structpath, surf_name + front_or_rear + 'RT.npz') savepath_A = os.path.join(structpath, surf_name + front_or_rear + 'A.npz') prof_mat_path = os.path.join(results_path, options['project_name'], surf_name + front_or_rear + 'profmat.nc') if os.path.isfile(savepath_RT) and save: print('Existing angular redistribution matrices found') fullmat = load_npz(savepath_RT) A_mat = load_npz(savepath_A) if len(prof_layers) > 0: profile = xr.load_dataarray(prof_mat_path) return fullmat, A_mat, profile else: wavelengths = options['wavelengths']*1e9 # convert to nm theta_intv, phi_intv, angle_vector = make_angle_vector(options['n_theta_bins'], options['phi_symmetry'], options['c_azimuth']) angles_in = angle_vector[:int(len(angle_vector) / 2), :] thetas = np.unique(angles_in[:, 1]) n_angles = len(thetas) n_layers = len(layers) if front_or_rear == 'front': optlayers = OptiStack(layers, substrate=transmission, incidence=incidence) trns = transmission inc = incidence else: optlayers = OptiStack(layers[::-1], substrate=incidence, incidence=transmission) trns = incidence inc = transmission if len(prof_layers) > 0: profile = True z_limit = np.sum(np.array(optlayers.widths)) full_dist = np.arange(0, z_limit, options['nm_spacing']) layer_start = np.insert(np.cumsum(np.insert(optlayers.widths, 0, 0)), 0, 0) layer_end = np.cumsum(np.insert(optlayers.widths, 0, 0)) dist = [] for l in prof_layers: dist = np.hstack((dist, full_dist[np.all((full_dist >= layer_start[l], full_dist < layer_end[l]), 0)])) else: profile = False if options['pol'] == 'u': pols = ['s', 'p'] else: pols = [options['pol']] R = xr.DataArray(np.empty((len(pols), len(wavelengths), n_angles)), dims=['pol', 'wl', 'angle'], coords={'pol': pols, 'wl': wavelengths, 'angle': thetas}, name='R') T = xr.DataArray(np.empty((len(pols), len(wavelengths), n_angles)), dims=['pol', 'wl', 'angle'], coords={'pol': pols, 'wl': wavelengths, 'angle': thetas}, name='T') Alayer = xr.DataArray(np.empty((len(pols), n_angles, len(wavelengths), n_layers)), dims=['pol', 'angle', 'wl', 'layer'], coords={'pol': pols, 'wl': wavelengths, 'angle': thetas, 'layer': range(1, n_layers + 1)}, name='Alayer') theta_t = xr.DataArray(np.empty((len(pols), len(wavelengths), n_angles)), dims=['pol', 'wl', 'angle'], coords={'pol': pols, 'wl': wavelengths, 'angle': thetas}, name='theta_t') if profile: Aprof = xr.DataArray(np.empty((len(pols), n_angles, len(wavelengths), len(dist))), dims=['pol', 'angle', 'wl', 'z'], coords={'pol': pols, 'wl': wavelengths, 'angle': thetas, 'z': dist}, name='Aprof') R_loop = np.empty((len(wavelengths), n_angles)) T_loop = np.empty((len(wavelengths), n_angles)) Alayer_loop = np.empty((n_angles, len(wavelengths), n_layers), dtype=np.complex_) th_t_loop = np.empty((len(wavelengths), n_angles)) if profile: Aprof_loop = np.empty((n_angles, len(wavelengths), len(dist))) tmm_struct = tmm_structure(optlayers, coherent=coherent, coherency_list=coherency_list, no_back_reflection=False) for i2, pol in enumerate(pols): for i3, theta in enumerate(thetas): res = tmm_struct.calculate(wavelengths, angle=theta, pol=pol, profile=profile, layers=prof_layers, nm_spacing = options['nm_spacing']) R_loop[:, i3] = np.real(res['R']) T_loop[:, i3] = np.real(res['T']) Alayer_loop[i3, :, :] = np.real(res['A_per_layer'].T) if profile: Aprof_loop[i3, :, :] = res['profile'] # sometimes get very small negative values (like -1e-20) R_loop[R_loop < 0] = 0 T_loop[T_loop < 0] = 0 Alayer_loop[Alayer_loop < 0] = 0 if front_or_rear == 'rear': Alayer_loop = np.flip(Alayer_loop, axis=2) print('flipping') R.loc[dict(pol=pol)] = R_loop T.loc[dict(pol=pol)] = T_loop Alayer.loc[dict(pol=pol)] = Alayer_loop theta_t.loc[dict(pol=pol)] = th_t_loop if profile: Aprof.loc[dict(pol=pol)] = Aprof_loop Aprof.transpose('pol', 'wl', 'angle', 'z') Alayer = Alayer.transpose('pol', 'wl', 'angle', 'layer') if profile: allres = xr.merge([R, T, Alayer, Aprof]) else: allres = xr.merge([R, T, Alayer]) if options['pol'] == 'u': allres = allres.reduce(np.mean, 'pol').assign_coords(pol='u').expand_dims('pol') # populate matrices if front_or_rear == "front": angle_vector_th = angle_vector[:int(len(angle_vector)/2),1] angle_vector_phi = angle_vector[:int(len(angle_vector)/2),2] phis_out = fold_phi(angle_vector_phi + np.pi, options['phi_symmetry']) theta_lookup = angles_in[:,1] quadrant = np.pi else: angle_vector_th = angle_vector[int(len(angle_vector) / 2):, 1] angle_vector_phi = angle_vector[int(len(angle_vector) / 2):, 2] phis_out = fold_phi(angle_vector_phi + np.pi, options['phi_symmetry']) theta_lookup = angles_in[:,1][::-1] quadrant = 0 phis_out[phis_out == 0] = 1e-10 theta_bins_in = np.digitize(angle_vector_th, theta_intv, right=True) -1 print(theta_bins_in) mats = [make_matrix_wl(wl) for wl in wavelengths] fullmat = stack([item[0] for item in mats]) A_mat = stack([item[1] for item in mats]) if save: save_npz(savepath_RT, fullmat) save_npz(savepath_A, A_mat) return fullmat, A_mat #, allres