Exemple #1
0
def PlotLevel(H_inputs,
              material,
              H_bench,
              ax,
              phi,
              a1,
              a2,
              b1,
              b2,
              limits,
              return_results=False,
              fixedAB=False,
              no_phi=False):
    '''
    Plots the energy levels as a function of one of the exchange params (a1, a2, b1, or b2). The other three
    params are kept at a fixed value. We select which param to vary over by setting it to None
    
    :param H_inputs: tuple of the inputs required to construct the bulk hamiltonian.
                        (Eg, muBohr, Bvec, vt, vl)
    :param material: string, tells what material we are plotting energies of, for the title
    :param H_bench: hamiltonian object, hamiltonian at param space point which we want to use as benchmark point, should already have exchange added ! 
    :param ax: matplotlib axis object for plotting data on, recieved from PlotLevelBothValleys (see above)
    :param phi: double, tells angle between H field and z hat in radians
    :param a1: exchange parameter, either set to double of its fixed value, or to None if its the free param
    :param a2: see a1
    :param b1: see a1
    :param b2: see a1
    :param limits: tuple of the starting and stopping limits for the free exchange parameter
    :param show: bool, whether to show the plot of the levels found here. Defaults to true. Use this option to hold off on plotting
                        central valley levels so that both can be shown on the same plot
    :param return_results: bool, tells to return level results before plotting or even formatting. Defaults to false. This option is used
                        by SearchProcessPlot to achieve plotting of both valleys together. User should never have to call this option
    :param fixedAB: bool, tells whether plotting in the case of A, B fixed (2D param space). Again, used by SearchProcessPlot but should 
                        not be changed by user. defaults to False ie regular 4d param space
    :param no_phi: bool, tells not to include phi value in legend band labels, defaults to False
                        
    returns: None in most cases, since plotting has already been done.
            If return_results == True, tuple of val_arr, E_levels, labels, colors
    
    '''

    # if A, B are fixed, get their vals which are disguised as a2, b2
    if (fixedAB):
        A, B = a2, b2

    # get, print, delete the k.p bulk hamiltonian
    H = hamiltonian.Hamiltonian(*H_inputs)
    print(H)

    # define number of points to use in parameter space
    N_param = 100
    N_levels = H.N
    del H

    # get the array over the param space
    val_arr = np.linspace(limits[0], limits[1], N_param)

    # get container for the energy levels
    E_levels = np.zeros((N_levels, N_param))

    # define E inputs here so that it persists
    E_inputs = ()

    # iter over this param space
    for i in range(len(val_arr)):

        val = val_arr[i]

        # determine which one is the free param
        # it is the one set to None
        if (a1 == None):

            xlabel = "a1 [eV]"
            E_inputs = (phi, val, a2, b1, b2)

            if (fixedAB):  # have to modify to determine a2, b2

                # in this case a2 is actually A and b2 is B
                a2 = val - A
                b2 = b1 - B

                E_inputs = (phi, val, a2, b1, b2)

        elif (a2 == None):

            xlabel = "a2 [eV]"
            E_inputs = (phi, a1, val, b1, b2)

        elif (b1 == None):

            xlabel = "b1 [eV]"
            E_inputs = (phi, a1, a2, val, b2)

        elif (b2 == None):

            xlabel = "b2 [eV]"
            E_inputs = (phi, a1, a2, b1, val)

        # every time thru we need to recreate H
        H = hamiltonian.Hamiltonian(*H_inputs)

        # construct the exchange matrix
        E = hamiltonian.Exchange(*E_inputs)

        # combine with H and get eigenvalues
        H.Add(E)

        # get the eigenvectors at i = 0 only
        # ordering will always follow how we ordered it here !
        if (i == 0):
            eigvecs = H.Eigvecs()

        #iter thru eigenvals
        for eig_i in range(len(eigvecs)):

            # get the eigval that corresponds to this eigvec
            eigvec = eigvecs[eig_i]
            eigval = H.GetEigenval(eigvec)

            # one energy level val for this param i
            E_levels[eig_i, i] = eigval

        # delete the matrices
        del H
        del E

    #### end loop over i (free param)

    # create H at Eg = 150 meV and E at params = 0 to determine naming
    H_bench.Add(hamiltonian.Exchange(*E_inputs))
    # this is bad code, just a stopgap measure
    # get levels from the banchmark hamiltonian
    Hi_E_levels = np.zeros(N_levels)
    for eig_i in range(N_levels):
        Hi_E_levels[eig_i] = H_bench.GetEigenval(eigvecs[eig_i])

    # use these to determine labels, colors
    # this step should make labels independent of the ordering
    labels, colors = EnergyLevels.GetBandLabels(H_bench.MakeCopy(),
                                                Hi_E_levels, phi, no_phi,
                                                material)

    # show only for oblique phi
    if (return_results):
        return val_arr, E_levels, labels, colors

    # plot
    for eig_i in range(N_levels):

        if (phi == 0):
            ax.plot(val_arr,
                    E_levels[eig_i],
                    label=labels[eig_i],
                    color=colors[eig_i],
                    linestyle='--')
        else:
            ax.plot(val_arr,
                    E_levels[eig_i],
                    label=labels[eig_i],
                    color=colors[eig_i])

    # format the plot
    ax.set_xlabel(xlabel)
    ax.set_ylabel("Energy [eV]")

    # make text box showing fixed parameters
    textstring = ''

    # go thru a, b params
    for param_i in range(4):

        param = [a1, a2, b1, b2][param_i]
        pstring = ["a1", "a2", "b1", "b2"][param_i]

        # check that this is not the free param
        if (param != None):

            #place info in text box
            textstring += pstring + " = " + str(param) + ",\n"

    return textstring
Exemple #2
0
def EgSweep_vs_x(H_inputs, E_inputs, Eg_func, x_lim, temp, material, Eg_bench,
                 ax, no_phi):
    '''
    Finds the dependence of the energy levels on the relative composition of the material, x
    Finds energy gap according to x, temperature, then basically runs the same code as the Eg sweep function
    
    :param H_inputs: tuple of the inputs for the bulk k.p hamiltonian (Eg, muBohr, Bvec, vt, vl), except
                Eg is just a dummy value since it is our dependent variable
    :param E_inputs: tuple of the inputs to construct the exchange matrix (phi, a1, a2, b1, b2)
    :param Eg_func: points to function that determine Eg at a given x, T
    :param x_lim: tuple of the start and stop values for the x sweep
    :param temp: double, the temperature in K
    :param material: string, the name of the material for the title of the plot
    :param Eg_bench: double, value of the band gap to benchmark ( label valence/conduction/spin of bands) at
    :param ax: matplotlib axes object, to plot these energy levels on
    :param no_phi: bool, tells whether to include value pf phi in legend labels (it is redundant if they are on separate axes)
    :param Eg_bench, double, the energy gap to benchmark (ie determine valence/conduction) at
    
    returns None, has already made all the plots
    '''

    # get, delete a version of the k.p bulk hamiltonian
    H = hamiltonian.Hamiltonian(*H_inputs)

    # define number of points to use in parameter space using H
    N_param = 100
    N_levels = H.N
    del H

    # define the return variable
    # 2d array of energy levels for different Eg
    E_levels = np.zeros((N_levels, N_param))

    # construct the exchange matrix
    # this is the same throughout since we are only varying Eg
    E = hamiltonian.Exchange(*E_inputs)
    phi = E_inputs[0]

    # vary over composition x
    x_arr = np.linspace(x_lim[0], x_lim[1], N_param)

    # iter over the x values
    for i in range(len(x_arr)):

        # for this individual x, get energy gap
        x = x_arr[i]
        Eg = Eg_func(x, temp)

        # get the energy levels
        # first construct the bulk matrix
        H_inputs = (Eg, H_inputs[1], H_inputs[2], H_inputs[3], H_inputs[4])
        H = hamiltonian.Hamiltonian(*H_inputs)

        # combine with the exchange matrix
        H.Add(E)

        # first run thru, get eigvecs
        if (i == 0):
            eigvecs = H.Eigvecs()

        # get eigenvalues with eigenvector method
        for eig_i in range(N_levels):

            # get the appropriate eigval and place in holder
            E_levels[eig_i, i] = H.GetEigenval(eigvecs[eig_i])

        # delete H before the next run thru
        del H

    #### end of loop over Eg vals

    # create H at  benchmark Eg (defaults to 150 meV) to determine band labels
    H_inputs = (Eg_bench, H_inputs[1], H_inputs[2], H_inputs[3], H_inputs[4])
    H = hamiltonian.Hamiltonian(*H_inputs)
    H.Add(E)

    # get levels from this H
    Hi_E_levels = np.zeros(N_levels)
    for eig_i in range(N_levels):
        Hi_E_levels[eig_i] = H.GetEigenval(eigvecs[eig_i])

    # determine labels, colors approproately
    labels, colors = EnergyLevels.GetBandLabels(H.MakeCopy(), Hi_E_levels, phi,
                                                no_phi, material)

    # set title and linestyle according to phi
    if (phi == 0):
        style = '--'
        # plots dashed
        ax.set_title("Longitudinal valley (--)", loc='left', fontsize=10)
        ax.set_xlabel("x")
        ax.set_ylabel("Energy [eV]")

    else:  #oblique valley
        style = 'solid'
        # plots solid
        ax.set_title("Oblique valley ($ -$)", loc='right', fontsize=10)
        ax.set_xlabel("x")
        #ax.set_ylabel("Energy [eV]");

    # iter over each level and plot
    for eig_i in range(N_levels):

        # plot level with appropriate label, color
        ax.plot(x_arr,
                E_levels[eig_i],
                color=colors[eig_i],
                linestyle=style,
                label=labels[eig_i])

    return
Exemple #3
0
def EgSweepTransition(H_inputs, E_inputs, Eg_lim, material, Eg_bench):
    '''
    Finds the energy levels as a function of the band gap Eg. Then finds and plots the spin-spin transition energies between levels.
    
    :param H_inputs: tuple of the inputs for the bulk k.p hamiltonian (Eg, muBohr, Bvec, vt, vl), except
                Eg is just a dummy value since it is our dependent variable
    :param E_inputs: tuple of the inputs to construct the exchange matrix (phi, a1, a2, b1, b2)
    :param Eg_lim: tuple of the start and stop values for the Eg sweep
    :param material: string of the name of the material for the title of the plot
    :param Eg_bench: double, the energy gap at point we want to label energy levels at
    '''

    #### iter over Eg, get transition energies,, and plot for both phi's

    # after this is done we want to get intersections
    # need containers for the lines we get
    central_lines = []
    oblique_lines = []

    # iter over both phis
    for phi in [0, 70.5 * np.pi / 180]:

        # get, delete a version of the k.p bulk hamiltonian
        H = hamiltonian.Hamiltonian(*H_inputs)

        # define number of points to use in parameter space using H
        N_param = 100
        N_levels = H.N
        del H

        # define the return variable
        # 2d array of energy levels for different Eg
        E_levels = np.zeros((N_levels, N_param))

        # construct the exchange matrix
        # first reconstruct inputs with appropriate phi
        E_inputs = (phi, *tuple(E_inputs[1:]))
        E = hamiltonian.Exchange(*E_inputs)

        # get linspace to vary over the energy gap
        Eg_arr = np.linspace(Eg_lim[0], Eg_lim[1], N_param)

        # iter over the energy gap values
        for i in range(len(Eg_arr)):

            # for this individual energy gap
            Eg = Eg_arr[i]

            # get the energy levels
            # first construct the bulk matrix
            H_inputs = (Eg, H_inputs[1], H_inputs[2], H_inputs[3], H_inputs[4])
            H = hamiltonian.Hamiltonian(*H_inputs)

            # combine with the exchange matrix
            H.Add(E)

            # first run thru, get eigvecs
            if (i == 0):
                eigvecs = H.Eigvecs()

            # get eigenvalues with eigenvector method
            for eig_i in range(N_levels):

                # get the appropriate eigval and place in holder
                E_levels[eig_i, i] = H.GetEigenval(eigvecs[eig_i])

            # delete H before the next run thru
            del H

        #### end of loop over Eg vals

        # create H at  benchmark Eg (defaults to 150 meV) to determine band labels
        H_inputs = (Eg_bench, H_inputs[1], H_inputs[2], H_inputs[3],
                    H_inputs[4])
        H = hamiltonian.Hamiltonian(*H_inputs)
        H.Add(E)

        # get levels from this H
        Hi_E_levels = np.zeros(N_levels)
        for eig_i in range(N_levels):
            Hi_E_levels[eig_i] = H.GetEigenval(eigvecs[eig_i])

        # determine labels, colors approproately
        labels, colors = EnergyLevels.GetBandLabels(H.MakeCopy(), Hi_E_levels,
                                                    phi, False, material)

        #### now that we have levels and labels we can get transition energies as a function of Eg

        # container for transition energies
        E_up = np.zeros(N_param)
        E_down = np.zeros(N_param)

        # iter over the E levels we found before
        for level_i in range(N_param):

            # get levels
            levels = E_levels[:, level_i]

            # get transition energies from levels
            E_up[level_i], E_down[
                level_i] = EnergyLevels.GetTransitionEnergies(levels, labels)

        # plot transition energies as a function of the gap
        if (phi == 0):  # plot in black for the longitudinal valley
            plt.plot(
                Eg_arr,
                E_up,
                color='black',
                label=
                r"$|L_{6}^{+} \uparrow \rangle \rightarrow |L_{6}^{-} \uparrow \rangle, \phi ="
                + str(phi)[:4] + "$ [rad]",
                linestyle='--')
            plt.plot(
                Eg_arr,
                E_down,
                color='black',
                label=
                r"$|L_{6}^{+} \downarrow \rangle \rightarrow |L_{6}^{-} \downarrow \rangle, \phi ="
                + str(phi)[:4] + "$ [rad]")

            # also place resulting levels in containers for determining intersections
            central_lines.append(E_up)
            central_lines.append(E_down)

        else:  # oblique valley should be red
            plt.plot(
                Eg_arr,
                E_up,
                color='red',
                label=
                r"$|L_{6}^{+}\uparrow \rangle \rightarrow |L_{6}^{-} \uparrow \rangle, \phi ="
                + str(phi)[:4] + "$ [rad]",
                linestyle='--')
            plt.plot(
                Eg_arr,
                E_down,
                color='red',
                label=
                r"$|L_{6}^{+}\downarrow \rangle \rightarrow |L_{6}^{-} \downarrow \rangle, \phi ="
                + str(phi)[:4] + "$ [rad]")

            # also place resulting levels in containers for determining intersections
            oblique_lines.append(E_up)
            oblique_lines.append(E_down)

    ####  make and show the actual plot now that we have done both valleys

    # get intersections of corresponding lines
    # recreate x arr for these lines
    Eg_arr = np.linspace(Eg_lim[0], Eg_lim[1], N_param)

    # want intersection of up, central with down, oblique
    x_int_up, y_int_up = EnergyLevels.GetIntersections(Eg_arr,
                                                       central_lines[0],
                                                       oblique_lines[1])
    # and of down, central with up, oblique
    x_int_down, y_int_down = EnergyLevels.GetIntersections(
        Eg_arr, central_lines[1], oblique_lines[0])

    # convert these points into formatted strings for the plot
    # only if intersections were found (ie returns != None)
    if (type(x_int_up) != type(None) and type(y_int_up) != type(None)):
        label_up = "(" + '{:.5f}'.format(x_int_up) + "," + '{:.5f}'.format(
            y_int_up) + ")"
        # use .format()
    else:  # intersection was not found
        label_up = "(" + str(x_int_up) + "," + str(y_int_up) + ")"

    # similarly for the other intersection
    if (type(x_int_down) != type(None) and type(y_int_down) != type(None)):
        label_down = "(" + '{:.5f}'.format(x_int_down) + "," + '{:.5f}'.format(
            y_int_down) + ")"
        # use .format()
    else:  # intersection was not found
        label_down = "(" + str(x_int_down) + "," + str(y_int_down) + ")"

    # plot the result as a scatter
    # need to figure out a better way of making these double strings
    plt.scatter(np.full(1, x_int_up),
                np.full(1, y_int_up),
                color='black',
                marker='^',
                label=label_up)
    plt.scatter(np.full(1, x_int_down),
                np.full(1, y_int_down),
                color='black',
                marker='o',
                label=label_down)

    # format the plot
    plt.title(material + " $\gamma $ absorption spectrum")
    plt.xlabel("$E_{g}$ [eV]")
    plt.ylabel("$\Delta$E [eV]")

    # make the legend
    ax = plt.subplot()
    chartBox = ax.get_position()
    ax.set_position(
        [chartBox.x0, chartBox.y0, chartBox.width * 0.6, chartBox.height])
    ax.legend(loc='upper center',
              bbox_to_anchor=(1.45, 0.8),
              shadow=True,
              ncol=1)
    leg2 = ax.get_legend()
    leg2.set_draggable(True)
    # makes the legend box draggable !

    # textbox below the legend for exchange param vals
    textstring = "a1 = " + str(E_inputs[1])[:6] + ",\n" + "a2 = " + str(
        E_inputs[2])[:6] + ",\n" + "b1 = " + str(
            E_inputs[3])[:6] + ",\n" + "b2 = " + str(E_inputs[4])[:6] + ", "
    text_x, text_y, = 1.4 * Eg_lim[1], ax.get_ylim()[
        0]  #+ ax2.get_ylim()[1])/3;
    ax.text(text_x, text_y, textstring, fontsize=12)

    plt.show()
Exemple #4
0
def EgSweep(H_inputs,
            E_inputs,
            Eg_lim,
            material,
            Eg_bench,
            ax,
            no_phi,
            help_debug=False):
    '''
    Finds the dependence of the energy levels on the energy gap of the material, Eg
    
    :param H_inputs: tuple, the inputs for the bulk k.p hamiltonian (Eg, muBohr, Bvec, vt, vl), except
                Eg is just a dummy value since it is our dependent variable
    :param E_inputs: tuple, the inputs to construct the exchange matrix (phi, a1, a2, b1, b2)
    :param Eg_lim: tuple, the start and stop values for the Eg sweep
    :param material: string, the name of the material for the title of the plot
    :param Eg_bench: double, the energy gap to benchmark (ie determine valence/conduction) at
    :param ax: matplotlib axis object on which to plot the levels, used in conjunction with EgSweepBothValleys
        to be able to easily plot valleys on same or different axes
    :param no_phi: bool, tells to suppress printing of phi values in legend, used for seperate axis plotting
    :param help_debug: bool, defaults to False, tells function to execute many extra print statements so that user can check for bugs
    '''

    # get, delete a version of the k.p bulk hamiltonian
    H = hamiltonian.Hamiltonian(*H_inputs)

    # print info about H
    if (help_debug):
        print("\n\n")
        print(
            10 * "*" +
            " Entering EgSweep.\nOptional help_debug argument set to true so function will print run info."
        )
        print(H)

    # define number of points to use in parameter space using H
    N_param = 100
    N_levels = H.N
    del H

    # define the return variable
    # 2d array of energy levels for different Eg
    E_levels = np.zeros((N_levels, N_param))

    # construct the exchange matrix
    # this is the same throughout since we are only varying Eg
    E = hamiltonian.Exchange(*E_inputs)
    phi = E_inputs[0]

    # vary over the energy gap
    Eg_arr = np.linspace(Eg_lim[0], Eg_lim[1], N_param)

    # iter over the energy gap values
    for i in range(len(Eg_arr)):

        # for this individual energy gap
        Eg = Eg_arr[i]

        # get the energy levels
        # first construct the bulk matrix
        H_inputs = (Eg, H_inputs[1], H_inputs[2], H_inputs[3], H_inputs[4])
        H = hamiltonian.Hamiltonian(*H_inputs)

        # combine with the exchange matrix
        H.Add(E)

        # first run thru, get eigvecs
        if (i == 0):
            eigvecs = H.Eigvecs()

        # get eigenvalues with eigenvector method
        for eig_i in range(N_levels):

            # get the appropriate eigval and place in holder
            E_levels[eig_i, i] = H.GetEigenval(eigvecs[eig_i])

        # delete H before the next run thru
        del H

    #### end of loop over Eg vals

    # create H at  benchmark Eg (defaults to 150 meV) to determine band labels
    H_inputs = (Eg_bench, H_inputs[1], H_inputs[2], H_inputs[3], H_inputs[4])
    H = hamiltonian.Hamiltonian(*H_inputs)
    H.Add(E)

    # get levels from this H
    Hi_E_levels = np.zeros(N_levels)
    for eig_i in range(N_levels):
        Hi_E_levels[eig_i] = H.GetEigenval(eigvecs[eig_i])

    # determine labels, colors approproately
    labels, colors = EnergyLevels.GetBandLabels(H.MakeCopy(), Hi_E_levels, phi,
                                                no_phi, material)

    # set title and linestyle according to phi
    if (phi == 0):
        style = '--'
        # plots dashed
        ax.set_title("Longitudinal valley (--)", loc='left', fontsize=10)
        ax.set_xlabel("$E_{g}$ [eV]")
        ax.set_ylabel("Energy [eV]")

    else:  #oblique valley
        style = 'solid'
        # plots solid
        ax.set_title("Oblique valley ($ -$)", loc='right', fontsize=10)
        ax.set_xlabel("$E_{g}$ [eV]")
        #ax.set_ylabel("Energy [eV]");

    # iter over each level and plot
    if (help_debug):
        print("phi = " + str(phi) + " endpoints")
    for eig_i in range(N_levels):

        # plot level with appropriate label, color
        ax.plot(Eg_arr,
                E_levels[eig_i],
                color=colors[eig_i],
                linestyle=style,
                label=labels[eig_i])

        # get endpoint to calculate energy differences
        if (help_debug):
            print("E_" + str(eig_i) + " = " + str(E_levels[eig_i, -1]))

    return
Exemple #5
0

################################################################################
# define the benchmark hamiltonian
# this amounts to determing at what input values we want to call lower bands valence
# bands and higher bands conduction bands
# definitely something the user may want to change

# define the benchmark energy level
Eg_bench = 15
# 150 meV

# define resst of bulk hamiltonian
H_bench_inputs = (Eg_bench, mu_Bohr, B_vec, vt, vl)
#uses baseline vals, but user is free to redefine
H_bench = hamiltonian.Hamiltonian(*H_bench_inputs)

################################################################################
# make plots of the energy levels vs band gap
Temp = [1.8, 1.8, 3.5, 4.4]
# loop over all four data sets
for data_i in [0]:  #,1,2,3 ]:

    # loop through scaling
    for scale_factor in [1, 10, 50, 100]:

        # get values for exchange params from Bauer data set
        #scale_factor = 50; # set a multiplicative factor to scale up exchange params if desired
        A, a1, a2, B, b1, b2 = GetExpData(data_i, scale_factor=scale_factor)

        # plot resulting levels for the central valley
# other input params of the bulk hamiltonian
B_vec = np.zeros(3)
# B field in T
vt = 4 * pow(10, 5)
# transverse velocity matrix element
vl = 4 * pow(10, 5)
# longitudinal velocity matrix element

# define bohr magneton, fundamental constant, only change if you're changing unit systems
mu_Bohr = 5.79e-5
# bohr magneton in eV/T

# package up the bulk H inputs as a tuple
H_inputs = (Eg, mu_Bohr, B_vec, vt, vl)
H = hamiltonian.Hamiltonian(Eg, mu_Bohr, B_vec, vt, vl)

################################################################################
# define the inouts to, and general limits of the inputs to, the exchange matrix
# changing these parameters will change value of fixed params in plots or domains of plots

# define the phi values for differnt points in the Brillouin zone
# phi is the angle between applied field and chosen z hat direction
# don't change these, they are built in to the geometry of the problem
phi_central = 0 * np.pi / 180
# gamma bar point aka central valley, in radians
phi_oblique = 70.5 * np.pi / 180
# m bar point aka oblique valley, in radians

# define baseline values for each exchange parameter
# we can and will overwrite these by redefining each variable later in the code