Exemple #1
0
def ProcessStructure(device, meshpoints, wavelengths=None, use_Adachi=False):
    """ This function reads a dictionary containing all the device structure, extract the electrical and optical
    properties of the materials, and loads all that information into the Fortran variables. Finally, it initiallise the
    device (in fortran) calculating an initial mesh and all the properties as a function of the possition.

    :param device: A dictionary containing the device structure. See PDD.DeviceStructure
    :param wavelengths: (Optional) Wavelengths at which to calculate the optical properties.
    :param use_Adachi: (Optional) If Adachi model should be use to calculate the dielectric constant of the material.
    :return: Dictionary containing the device structure properties as a function of the position.
    """
    print('Processing structure...')
    # First, we clean any previous data from the Fortran code
    dd.reset()
    output = {}

    if wavelengths is not None:
        output['Optics'] = {}
        output['Optics']['wavelengths'] = wavelengths

    # We dump the structure information to the Fotran module and initialise the structure
    i = 0
    while i < device['numlayers']:
        layer = device['layers'][i]['properties']
        args_list = [
            layer['width'],
            asUnit(layer['band_gap'], 'eV'),
            asUnit(layer['electron_affinity'],
                   'eV'), layer['electron_mobility'], layer['hole_mobility'],
            layer['Nc'], layer['Nv'], layer['electron_minority_lifetime'],
            layer['hole_minority_lifetime'], layer['permittivity'] * Epsi0,
            layer['radiative_recombination'],
            layer['electron_auger_recombination'],
            layer['hole_auger_recombination'], layer['Na'], layer['Nd']
        ]
        dd.addlayer(args=args_list)

        i = i + device['layers'][i]['numlayers']

    # We set the surface recombination velocities. This needs to be improved at some point
    # to consider other boundary conditions
    dd.frontboundary("ohmic", device['sn'], device['sp'], 0)
    dd.backboundary("ohmic", device['sn'], device['sp'], 0)

    dd.initdevice(meshpoints)
    print('...done!\n')

    output['Properties'] = DumpInputProperties()
    return output
Exemple #2
0
    def test_64_quantum_mechanics_schrodinger(self):
        bulk = material("GaAs")(T=293)
        barrier = material("GaAsP")(T=293, P=0.1)

        bulk.strained = False
        barrier.strained = True

        top_layer = Layer(width=si("30nm"), material=bulk)
        inter = Layer(width=si("3nm"), material=bulk)
        barrier_layer = Layer(width=si("15nm"), material=barrier)
        bottom_layer = top_layer

        E = np.linspace(1.15, 1.5, 300) * q
        alfas = np.zeros((len(E), 6))

        alfas[:, 0] = E / q

        alpha_params = {
            "well_width": si("7.2nm"),
            "theta": 0,
            "eps": 12.9 * vacuum_permittivity,
            "espace": E,
            "hwhm": si("6meV"),
            "dimensionality": 0.16,
            "line_shape": "Gauss"
        }

        QW = material("InGaAs")(T=293, In=0.2)
        QW.strained = True
        well_layer = Layer(width=si("7.2nm"), material=QW)

        my_structure = Structure(
            [top_layer, barrier_layer, inter] +
            1 * [well_layer, inter, barrier_layer, inter] + [bottom_layer],
            substrate=bulk)

        band_edge, bands = QM.schrodinger(my_structure,
                                          quasiconfined=0,
                                          num_eigenvalues=20,
                                          alpha_params=alpha_params,
                                          calculate_absorption=True)

        for key in band_edge['E']:
            for i in range(len(band_edge['E'][key])):
                band_edge['E'][key][i] = solcore.asUnit(
                    band_edge['E'][key][i], 'eV') * 1000
                band_edge['E'][key][i] = round(band_edge['E'][key][i])

        Ehh = np.all(np.equal(band_edge['E']['Ehh'], my_energies['Ehh']))
        Elh = np.all(np.equal(band_edge['E']['Elh'], my_energies['Elh']))
        Ee = np.all(np.equal(band_edge['E']['Ee'], my_energies['Ee']))

        idx = 100
        out = [band_edge['alpha'][0][idx] / q, band_edge['alpha'][1][idx]]

        # Test over the energies
        self.assertTrue(Ehh and Elh and Ee)
        # Test over the absorption coefficent at a given energy
        for i, data in enumerate(out):
            self.assertAlmostEqual(out[i], my_absorption[i])
Exemple #3
0
    def _add_solcore_layer(self, layer):
        """ Adds a Solcore layer to the end (bottom) of the stack, extracting its thickness and n and k data.

        :param layer: The Solcore layer
        :return: None
        """
        self.widths.append(solcore.asUnit(layer.width, 'nm'))
        self.models.append([])
        self.n_data.append(np_cache(layer.material.n))
        self.k_data.append(np_cache(layer.material.k))
Exemple #4
0
def strain_calculation_parameters(substrate_material,
                                  layer_material,
                                  should_print=False,
                                  SO=False):
    """Will extract materials parameters and perform a bit of conditioning to the values.
    
    Returns a solcore State object with the following keys:
    
        -- ac, the conduction band deformation potential
        -- av, the valence band deformation potential
        -- b,  the valence band splitting deformation potential
        -- C11, element of the elastic stiffness tensor
        -- C12, element of the elastic stiffness tensor
        -- a0, the unstrained substrate lattice constant
        -- a, the unstrained layer lattice constant
        -- epsilon, in-plain strain
        -- epsilon_perp, out-plain strain
        -- e_xx, in-plain strain (e_xx = epsilon)
        -- e_yy, in-plain strain (e_yy = epsilon)
        -- e_zz, out-plain strain (e_zz = epsilon_perp)
        -- Tre, element of come matrix (Tre = e_xx + e_yy + e_zz)
        -- Pe, parameter use by Chuang
        -- Qe, parameter use by Chuang
        -- cb_hydrostatic_shift, CB moved by this amount
        -- vb_hydrostatic_shift, VB moved by this amount
        -- vb_shear_splitting, VB split by this amount (i.e. HH/LH separation)
        -- delta_Ec, final conduction band shift
        -- delta_Elh, final light hole band shift
        -- delta_Ehh, final heavy hole band shift
    
    Care has to be taken when calculating the energy shifts because different 
    sign conversion are used by different authors. Here we use the approach of
    S. L. Chuang, 'Physics of optoelectronic devices'. 
    
    Note that this requires that the 'a_v' deformation potential to be 
    positive, where as Vurgaftman defines this a negative!
    """

    sub = substrate_material
    mat = layer_material
    k = State()

    # deformation potentials
    k.av = abs(mat.a_v)  # make sure that av is positive for this calculation
    k.ac = mat.a_c  # Vurgaftman uses the convention that this is negative
    k.b = mat.b

    # Matrix elements from elastic stiffness tensor
    k.C11 = mat.c11
    k.C12 = mat.c12
    if should_print: print(sub, mat)
    # Strain fractions
    k.a0 = sub.lattice_constant
    k.a = mat.lattice_constant
    k.epsilon = (k.a0 - k.a) / k.a  # in-plain strain
    k.epsilon_perp = -2 * k.C12 / k.C11 * k.epsilon  # out-plain
    k.e_xx = k.epsilon
    k.e_yy = k.epsilon
    k.e_zz = k.epsilon_perp
    k.Tre = (k.e_xx + k.e_yy + k.e_zz)
    k.Pe = -k.av * k.Tre
    k.Qe = -k.b / 2 * (k.e_xx + k.e_yy - 2 * k.e_zz)

    k.cb_hydrostatic_shift = k.ac * k.Tre
    k.vb_hydrostatic_shift = k.av * k.Tre
    k.vb_shear_splitting = 2 * k.b * (1 + 2 * k.C12 / k.C11) * k.epsilon

    # Shifts and splittings
    k.delta_Ec = k.ac * k.Tre

    if should_print: print(k.ac, k.Tre)

    k.delta_Ehh = -k.Pe - k.Qe
    k.delta_Elh = -k.Pe + k.Qe
    k.delta_Eso = 0.0

    if SO:
        k.delta = mat.spin_orbit_splitting
        shift = k.delta**2 + 2 * k.delta * k.Qe + 9 * k.Qe**2
        k.delta_Elh = -k.Pe + 0.5 * (k.Qe - k.delta + np.sqrt(shift))
        k.delta_Eso = -k.Pe + 0.5 * (k.Qe - k.delta - np.sqrt(shift))

    strain_calculation_asserts(k, should_print=should_print)

    if should_print:
        print()
        print("Lattice:")
        print("a0", k.a0)
        print("a", k.a)
        print()
        print("Deformation potentials:")
        print("ac = ", solcore.asUnit(k.ac, 'eV'))
        print("av = ", solcore.asUnit(k.av, 'eV'))
        print("ac - av = ", solcore.asUnit(k.ac - k.av, 'eV'))
        print("b = ", solcore.asUnit(k.b, 'eV'))
        print()
        print("Matrix elements from elastic stiffness tensor:")
        print("C_11 = ", solcore.asUnit(k.C11, "GPa"))
        print("C_12 = ", solcore.asUnit(k.C12, "GPa"))
        print()
        print("Strain fractions:")
        print("e_xx = e_yy = epsilon = ", k.epsilon)
        print("e_zz = epsilon_perp = ", k.epsilon_perp)
        print("e_xx + e_yy + e_zz = Tre = ", k.Tre)
        print()
        print("Shifts and splittings:")
        print("Pe = -av * Tre = ", solcore.asUnit(k.Pe, 'eV'))
        print("Qe = -b/2*(e_xx + e_yy - 2*e_zz) = ",
              solcore.asUnit(k.Qe, 'eV'))
        print("dEc = ac * Tre = ", solcore.asUnit(k.delta_Ec, 'eV'))
        print("dEhh = av * Tre + b[1 + 2*C_11/C_12]*epsilon = -Pe - Qe = ",
              solcore.asUnit(k.delta_Ehh, 'eV'))
        print("dElh = av * Tre - b[1 + 2*C_11/C_12]*epsilon = -Pe + Qe = ",
              solcore.asUnit(k.delta_Elh, 'eV'))
        print()

    return k
Exemple #5
0
        except ValueError:
            raise ValueError("Critical thickness infinite?")

        result.append(convert(hc / 4, "Ang", final_unit)) if during_growth else result.append(
            convert(hc, "Ang", final_unit))
    return array(result)


if __name__ == "__main__":
    T = linspace(300, 1000, 10)
    from solcore.graphing import *

    result = critical_thickness(layer_material="GaInAs", lattice_material="GaAs", layer_fraction=0.24, T=T,
                                final_unit="nm")
    g = Graph(
        GraphData(T, asUnit(result, 'nm'), label="Matthews-Blakeslee $\\frac{h_c}{4}$"),  # , "-", 1,"red"),
        # yscale="log", 
        # xlim=(0.03,0.53), 
        # ylim=(1,1000), 
        xlabel="Temperature (K)",
        ylabel="Critical Thickness (nm)",
        # grid=True,
        title="InGaAs critical thickness"
        # labels = ([(x_at_024,y_at_024,"x=0.24, $\\frac{h_c}{4}$=%.2fnm"%y_at_024)])
    )
    g.draw()
    T = 300
    x = linspace(0, 1, 100)[1:]
    result = critical_thickness(layer_material="GaInAs", lattice_material="GaAs", layer_fraction=x, T=T,
                                final_unit="nm")
    print(result)
Exemple #6
0
                                  {"__builtins__": self.builtins_replacement},
                                  others)
        in_si_units = siUnits(non_converted_unit,
                              units) if use_units else non_converted_unit
        return in_si_units

    def __assemble_builtins(self):
        self.builtins_replacement = {"max": numpy.max, "min": numpy.min}
        self.builtins_replacement.update(math.__dict__)


if __name__ == "__main__":
    import os

    v = ParameterSystem()
    v.add_source(
        "v",
        os.path.split(__file__)[0] +
        "/plugins/vurgaftman/builtins/endpoints.txt")
    v.add_source(
        "v2",
        os.path.split(__file__)[0] +
        "/plugins/vurgaftman/builtins/bowing_tree.txt")
    print(
        solcore.asUnit(
            v.get_parameter("GaAsSb.5", "band_gap", verbose=True, T=300),
            "eV"))
    print((v.get_parameter("GaAsSb.75", "band_gap", verbose=True,
                           T=300), "eV"))
    print((v.get_parameter("GaAsSb.5", "band_gap", verbose=True, T=300), "eV"))
Exemple #7
0
def test_quantum_mechanics_schrodinger():
    """ Testing schrodinger equation solver
    """
    bulk = material("GaAs")(T=293)
    barrier = material("GaAsP")(T=293, P=0.1)

    bulk.strained = False
    barrier.strained = True

    top_layer = Layer(width=si("30nm"), material=bulk)
    inter = Layer(width=si("3nm"), material=bulk)
    barrier_layer = Layer(width=si("15nm"), material=barrier)
    bottom_layer = top_layer

    E = np.linspace(1.15, 1.5, 300) * q
    alfas = np.zeros((len(E), 6))

    alfas[:, 0] = E / q

    alpha_params = {
        "well_width": si("7.2nm"),
        "theta": 0,
        "eps": 12.9 * vacuum_permittivity,
        "espace": E,
        "hwhm": si("6meV"),
        "dimensionality": 0.16,
        "line_shape": "Gauss",
    }

    QW = material("InGaAs")(T=293, In=0.2)
    QW.strained = True
    well_layer = Layer(width=si("7.2nm"), material=QW)

    my_structure = Structure(
        [
            top_layer,
            barrier_layer,
            inter,
            well_layer,
            inter,
            barrier_layer,
            inter,
            bottom_layer,
        ],
        substrate=bulk,
    )

    band_edge, bands = QM.schrodinger(
        my_structure,
        quasiconfined=0,
        num_eigenvalues=20,
        alpha_params=alpha_params,
        calculate_absorption=True,
    )

    for key in band_edge["E"]:
        for i in range(len(band_edge["E"][key])):
            band_edge["E"][key][i] = solcore.asUnit(band_edge["E"][key][i],
                                                    "eV") * 1000
            band_edge["E"][key][i] = round(band_edge["E"][key][i])

    Ehh = np.all(np.equal(band_edge["E"]["Ehh"], my_energies["Ehh"]))
    Elh = np.all(np.equal(band_edge["E"]["Elh"], my_energies["Elh"]))
    Ee = np.all(np.equal(band_edge["E"]["Ee"], my_energies["Ee"]))

    idx = 100
    out = [band_edge["alpha"][0][idx] / q, band_edge["alpha"][1][idx]]

    # Test over the energies
    assert Ehh and Elh and Ee

    # Test over the absorption coefficent at a given energy
    for i, data in enumerate(out):
        assert out[i] == approx(my_absorption[i], rel=1e-4)
Exemple #8
0
zz = np.linspace(0, 1, 100)
lat = np.zeros_like(zz)
eg = np.zeros_like(zz)

colors = plt.cm.jet(np.linspace(0, 1, len(mat)))

fig = plt.figure(figsize=(6, 4.5))
ax = fig.add_subplot(111)

for j, (m, x) in enumerate(zip(mat, xx)):
    for i, z in enumerate(zz):
        param = {x: z}
        new_mat = material(m)(T=300, **param)

        lat[i] = new_mat.lattice_constant * 1e10
        eg[i] = asUnit(new_mat.band_gap, 'eV')

    ax.plot(lat, eg, label=m, color=colors[j])

lat2 = []
eg2 = []
for (m, p) in zip(mat2, pos):
    new_mat = material(m)(T=300)

    lat2.append(new_mat.lattice_constant * 1e10)
    eg2.append(asUnit(new_mat.band_gap, 'eV'))

    ax.annotate(m, xy=p)

plt.plot(lat2, eg2, 'ko')
Exemple #9
0
def ProcessStructure(device, meshpoints, wavelengths=None, use_Adachi=False):
    """ This function reads a dictionary containing all the device structure, extract the electrical and optical
    properties of the materials, and loads all that information into the Fortran variables. Finally, it initiallise the
    device (in fortran) calculating an initial mesh and all the properties as a function of the possition.

    :param device: A dictionary containing the device structure. See PDD.DeviceStructure
    :param wavelengths: (Optional) Wavelengths at which to calculate the optical properties.
    :param use_Adachi: (Optional) If Adachi model should be use to calculate the dielectric constant of the material.
    :return: Dictionary containing the device structure properties as a function of the position.
    """
    print('Processing structure...')
    # First, we clean any previous data from the Fortran code
    dd.reset()
    output = {}

    if wavelengths is not None:
        output['Optics'] = {}
        output['Optics']['wavelengths'] = wavelengths
        calculate_absorption = True
    else:
        calculate_absorption = False

    # We dump the structure information to the Fotran module and initialise the structure
    i = 0
    first = 0
    last = -1
    while i < device['numlayers']:

        if device['layers'][i]['label'] in [
                'optics', 'Optics', 'metal', 'Metal'
        ]:
            # Optics or metal layers. They are not included in the PDD solver and are just used for optics
            if i == first:
                first = i + 1
                i = i + device['layers'][i]['numlayers']
                continue
            else:
                last = i - device['numlayers'] - 1
                break

        if device['layers'][i]['group'] is None:
            # We have a normal layer
            layer = device['layers'][i]['properties']
            args_list = [
                layer['width'],
                asUnit(layer['band_gap'], 'eV'),
                asUnit(layer['electron_affinity'], 'eV'),
                layer['electron_mobility'], layer['hole_mobility'],
                layer['Nc'], layer['Nv'], layer['electron_minority_lifetime'],
                layer['hole_minority_lifetime'], layer['permittivity'] * Epsi0,
                layer['radiative_recombination'],
                layer['electron_auger_recombination'],
                layer['hole_auger_recombination'], layer['Na'], layer['Nd']
            ]
            dd.addlayer(args=args_list)

            # We load the absorption coeficients if necessary
            if calculate_absorption:
                if 'absorption' in layer.keys():
                    layer['absorption'][1] = np.interp(
                        wavelengths, layer['absorption'][0],
                        layer['absorption'][1]).tolist()
                    layer['absorption'][0] = wavelengths.tolist()
                else:
                    layer['absorption'] = LoadAbsorption(device['layers'][i],
                                                         device['T'],
                                                         wavelengths,
                                                         use_Adachi=use_Adachi)
                dd.addabsorption(layer['absorption'][1], wavelengths)

        else:
            # We have a group of several layers, usually a QW with 'numlayers' repeated 'repeat' times.
            for j in range(device['layers'][i]['repeat']):
                for k in range(device['layers'][i]['numlayers']):
                    layer = device['layers'][i + k]['properties']
                    args_list = [
                        layer['width'],
                        asUnit(layer['band_gap'], 'eV'),
                        asUnit(layer['electron_affinity'],
                               'eV'), layer['electron_mobility'],
                        layer['hole_mobility'], layer['Nc'], layer['Nv'],
                        layer['electron_minority_lifetime'],
                        layer['hole_minority_lifetime'],
                        layer['permittivity'] * Epsi0,
                        layer['radiative_recombination'],
                        layer['electron_auger_recombination'],
                        layer['hole_auger_recombination'], layer['Na'],
                        layer['Nd']
                    ]
                    dd.addlayer(args=args_list)

                    # We load the absorption coeficients if necessary
                    if calculate_absorption:
                        if 'absorption' in layer.keys():
                            layer['absorption'][1] = np.interp(
                                wavelengths, layer['absorption'][0],
                                layer['absorption'][1]).tolist()
                            layer['absorption'][0] = wavelengths.tolist()
                        else:
                            layer['absorption'] = LoadAbsorption(
                                device['layers'][i + k],
                                device['T'],
                                wavelengths,
                                use_Adachi=use_Adachi)
                        dd.addabsorption(layer['absorption'][1], wavelengths)

        i = i + device['layers'][i]['numlayers']

    # We set the surface recombination velocities. This needs to be improved at some to consider other boundary conditions
    dd.frontboundary("ohmic", device['layers'][first]['properties']['sn'],
                     device['layers'][first]['properties']['sp'], 0)
    dd.backboundary("ohmic", device['layers'][last]['properties']['sn'],
                    device['layers'][last]['properties']['sp'], 0)

    dd.initdevice(meshpoints)
    print('...done!\n')

    output['Properties'] = DumpInputProperties()
    return output