예제 #1
0
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)
예제 #2
0
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
예제 #3
0
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
예제 #4
0
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