Пример #1
0
 def V_bi(self, eV=False):
     measurement_id = self.Project.add_measurement(self.measurements_types['Built-in voltage measurement'], 'Vbi',
                                                   'Built-in voltage',
                                                   measurement_datetime=None, create_new=False)
     point_name_long = 'Vbi'
     Vbi_array, _, measurement_date_array, unit_name_array = self.Project.get_data_points(measurement_id,
                                                                                          point_name_long)
     for i in range(len(Vbi_array)):
         T, _ = self.Project.get_data_point_at_datetime(self.T_measurement_id, 'Temperature',
                                                        measurement_date_array[i], interpolation_type='step')
         if T == self.T:
             Vbi = Vbi_array[i]
             if eV:
                 if unit_name_array[i] == 'J':
                     Vbi /= to_numeric(q)
             else:
                 if unit_name_array[i] == 'V':
                     Vbi *= to_numeric(q)
             return np.float(Vbi)
     point_name_short = 'Vbi'
     point_unit_name = 'V' if eV else 'J'
     point_order = self.Project.get_next_data_point_order(measurement_id, point_name_long)
     if eV:
         Vbi = to_numeric(self.Metal.WF / q) - self.Semiconductor.WF(self.T, z=1e3, eV=eV)
     else:
         Vbi = to_numeric(self.Metal.WF) - self.Semiconductor.WF(self.T, z=1e3, eV=eV)
     self.Project.add_datapoint(point_order, measurement_id, self.tool_id,
                                point_name_long, point_name_short, point_unit_name,
                                Vbi, datetime.datetime.now())
     return np.float(Vbi)
Пример #2
0
def BandsBending_prepare_data(SchDiode, Psi, Vd, z, eV, SchottkyEffect, draw_metal):
    dz = np.gradient(z)
    Psi_points = Psi(z)
    E_points = -np.gradient(Psi_points, dz, edge_order=2)
    '''
    if SchDiode.Semiconductor.dop_type == 'n':
        idx = np.where(E_points > 0)[0]
    else:
        idx = np.where(E_points < 0)[0]
    if idx.size > 0:
        split = np.where(idx[1:]-idx[:-1] > 1)[0]+1
        if split.size > 0:
            idx = np.split(idx, split)[0]
        #print 'Inversion pt', z[idx][-1]*1e6
        FieldInversionPoint = z[idx[-1]]
        FieldInversionPsi = Psi_points[idx[-1]]
    else:
        FieldInversionPoint = 0
        FieldInversionPsi = 0
    '''
    FieldInversionPoint, PHI_bn, _, PHI_b = SchDiode.get_phi_bn(Psi, Vd, SchottkyEffect=False)
    FieldInversionPsi = Psi(FieldInversionPoint)
    idx = np.where(z <= FieldInversionPoint)[0]
    coeff = 1 if eV else to_numeric(q)
    Eg = SchDiode.Semiconductor.band_gap(SchDiode.T, symbolic=False, electron_volts=eV)
    Ec = SchDiode.Ec(Psi, Vd, z, eV=eV)
    type_sign = 1 if SchDiode.Semiconductor.dop_type == 'n' else -1
    Ef = np.zeros_like(z) + coeff * type_sign * Vd
    if FieldInversionPoint > 0:
        # xi = np.float(SchDiode.Semiconductor.Ech_pot(T=SchDiode.T, z=1e3, eV=eV, debug=False))
        Ef[0:idx[-1]] = coeff * FieldInversionPsi * type_sign
        # print idx[-1]
        # Ef[0:idx[-1]] = Ec[0:idx[-1]] - Ef[idx[-1]]
    Ev = Ec - Eg
    if draw_metal:
        Z = np.append(0, z)
        Ec = np.append(0, Ec)
        Ev = np.append(0, Ev)
        Ef = np.append(Ef[0], Ef)
    MirrEnergy = np.zeros_like(Ec)
    if SchottkyEffect:
        for ii, zz in enumerate(Z):
            if zz != 0:
                MirrEnergy[ii] = to_numeric(
                    q ** 2 / (16 * np.pi * epsilon0 * SchDiode.Semiconductor.reference['epsilon'] * zz))
                if eV:
                    MirrEnergy[ii] /= to_numeric(q)
            else:
                MirrEnergy[ii] = Ec[ii]
    return Z, Ec, Ev, Ef, Eg, MirrEnergy, FieldInversionPoint
Пример #3
0
def BandsBending_prepare_data(SchDiode, Psi, Vd, z, eV, SchottkyEffect, draw_metal):
    Psi_points = Psi(z)
    E_points = -np.gradient(Psi_points, z, edge_order=2)
    '''
    if SchDiode.Semiconductor.dop_type == 'n':
        idx = np.where(E_points > 0)[0]
    else:
        idx = np.where(E_points < 0)[0]
    if idx.size > 0:
        split = np.where(idx[1:]-idx[:-1] > 1)[0]+1
        if split.size > 0:
            idx = np.split(idx, split)[0]
        #print 'Inversion pt', z[idx][-1]*1e6
        FieldInversionPoint = z[idx[-1]]
        FieldInversionPsi = Psi_points[idx[-1]]
    else:
        FieldInversionPoint = 0
        FieldInversionPsi = 0
    '''
    FieldInversionPoint, PHI_bn, _, PHI_b = SchDiode.get_phi_bn(Psi, Vd, SchottkyEffect=False)
    FieldInversionPsi = Psi(FieldInversionPoint)
    idx = np.where(z <= FieldInversionPoint)[0]
    coeff = 1 if eV else to_numeric(q)
    Eg = SchDiode.Semiconductor.band_gap(SchDiode.T, symbolic=False, electron_volts=eV)
    Ec = SchDiode.Ec(Psi, Vd, z, eV=eV)
    type_sign = 1 if SchDiode.Semiconductor.dop_type == 'n' else -1
    Ef = np.zeros_like(z) + coeff * type_sign * Vd
    if FieldInversionPoint > 0:
        # xi = np.float(SchDiode.Semiconductor.Ech_pot(T=SchDiode.T, z=1e3, eV=eV, debug=False))
        Ef[0:idx[-1]] = coeff * FieldInversionPsi * type_sign
        # print idx[-1]
        # Ef[0:idx[-1]] = Ec[0:idx[-1]] - Ef[idx[-1]]
    Ev = Ec - Eg
    if draw_metal:
        Z = np.append(0, z)
        Ec = np.append(0, Ec)
        Ev = np.append(0, Ev)
        Ef = np.append(Ef[0], Ef)
    MirrEnergy = np.zeros_like(Ec)
    if SchottkyEffect:
        for ii, zz in enumerate(Z):
            if zz != 0:
                MirrEnergy[ii] = to_numeric(
                    q ** 2 / (16 * np.pi * epsilon0 * SchDiode.Semiconductor.reference['epsilon'] * zz))
                if eV:
                    MirrEnergy[ii] /= to_numeric(q)
            else:
                MirrEnergy[ii] = Ec[ii]
    return Z, Ec, Ev, Ef, Eg, MirrEnergy, FieldInversionPoint
Пример #4
0
 def ThermionicEmissionCurrent(self, Va, phi_bn, debug=False):
     kT = to_numeric(k * self.T)
     q_n = to_numeric(q)
     A = self.Area
     Rs = self.Rs
     if self.Semiconductor.dop_type == 'n':
         Ar = self.Semiconductor.reference['A_R_coeff_n'] * constants['A_R']
     else:
         Ar = self.Semiconductor.reference['A_R_coeff_p'] * constants['A_R']
     Js = Ar * (self.T ** 2) * mp.exp(-q_n * phi_bn / kT)
     if debug: print 'Js, Is =', Js, A * Js
     J = -Js + kT / (q_n * A * Rs) * mp.lambertw((q_n * A * Rs * Js / kT) * mp.exp(q_n * (Va + A * Js * Rs) / kT))
     if debug: print 'J, I =', J, A * J
     Vd = Va - A * J * Rs
     return np.float(Vd), np.float(J)
Пример #5
0
def RhoDiagram(ax, SchDiode, Psi=Psi_zero, z=0):
    print 'Rho Diagram Start'
    rho_semi_num = SchDiode.build_rho_z_Psi(Psi, carriers_charge=True)
    rho = rho_semi_num(z, Psi) / to_numeric(q)
    n, p = SchDiode.n_carriers_theory(Psi, z)
    dopants_rho = []
    for dopant in SchDiode.Semiconductor.dopants:
        Nd = dopant.concentration(z)
        dopants_rho.append(
            Nd * ((dopant.charge_states[1][0] - dopant.charge_states[0][0]) * dopant.F(z) + dopant.charge_states[0][0]))

    ax.plot(z * 1e6, abs(rho * 1e-6), linewidth=2, color='black', linestyle='-')

    if SchDiode.Semiconductor.dop_type == 'n':
        ax.plot(z * 1e6, n * 1e-6, linewidth=2, color='blue', linestyle='-')
    elif SchDiode.Semiconductor.dop_type == 'p':
        ax.plot(z * 1e6, p * 1e-6, linewidth=2, color='red', linestyle='-')
    for dopant_rho in dopants_rho:
        ax.plot(z * 1e6, dopant_rho * 1e-6, linewidth=2, color='green', linestyle='-')

    ax.set_yscale('log')
    # ax.ticklabel_format(axis='y', style='sci', scilimits=(-7,7))
    ax.set_title('Net density of charge')
    ax.set_ylabel('Concentration, cm$^{-3}$')
    ax.set_xlabel(r'Coordinate (z), um')
    ax.grid(True)
    print 'Rho Diagram Stop'
Пример #6
0
 def Ec(self, Psi=Psi_zero, Va=0, z=0, eV=False):
     coeff = 1.0 if eV else to_numeric(q)
     Psi_nodes = Psi(z)
     xi = np.float(self.Semiconductor.Ech_pot(T=self.T, z=1e3, eV=eV, debug=False))
     type_sign = -1 if self.Semiconductor.dop_type == 'p' else 1
     Ec = -coeff * Psi_nodes + xi + coeff * type_sign * Va
     return Ec
Пример #7
0
 def init():
     Eg = SchDiode.Semiconductor.band_gap(SchDiode.T, symbolic=False, electron_volts=eV)
     bands_lines['Ec'].set_data([], [])
     bands_lines['Ev'].set_data([], [])
     bands_lines['Ef'].set_data([], [])
     for dopant in SchDiode.Semiconductor.dopants:
         threshold_dopant = 0.05 if eV else to_numeric(q) * 0.05
         level = dopant.energy_level(SchDiode.T, SchDiode.Semiconductor, electron_volts=eV)
         if level > threshold_dopant and level < (Eg - threshold_dopant):
             dopant_lines[dopant.name].set_data([], [])
     if label_bands:
         bands_labels['Ec'].set_text('')
         bands_labels['Ev'].set_text('')
         bands_labels['Ef'].set_text('')
     V_label.set_text('')
     T_label.set_text('')
     for key in BI_levels.keys():
         BI_levels[key].set_offsets(np.array([]))
         BI_levels[key].set_array(np.array([]))
     fig_list = bands_lines.values()
     fig_list.extend(bands_labels.values())
     fig_list.extend(dopant_lines.values())
     fig_list.extend([V_label, T_label])
     fig_list.extend(BI_levels.values())
     return fig_list
Пример #8
0
 def n_carriers_theory(self, Psi=Psi_zero, z=0):
     '''
     Charge carrier concentration in the main band
     '''
     k_n = to_numeric(k)
     Ef = self.EfEc(Psi, z, eV=False)
     Eg = self.Semiconductor.band_gap(self.T, symbolic=False, electron_volts=False)
     F_n = np.exp(-Ef / (k_n * self.T))
     F_p = np.exp((Ef - Eg) / (k_n * self.T))
     n = np.float(self.Semiconductor.Nc(self.T, symbolic=False)) * F_n
     p = np.float(self.Semiconductor.Nv(self.T, symbolic=False)) * F_p
     if isinstance(z, np.ndarray):
         idx = np.where(z < self.DeadLayer)[0]
         # print idx
         if idx.size > 0:
             n[idx] = 0.0
             p[idx] = 0.0
         idx = np.where(z < self.FieldInversionPoint)[0]
         if idx.size > 0:
             n[idx] = 0.0
             p[idx] = 0.0
     elif z < self.DeadLayer or z < self.FieldInversionPoint:
         n = 0
         p = 0
     return n, p
Пример #9
0
 def set_electrode(self, Metal=None):
     measurement_type = self.Project.add_measurement_type('Metal work function',
                                                          measurement_type_description='Metal work function measurement')
     try:
         metal_label = self.Project.get_project_info('Schottky electrode')
         measurement_id = self.Project.add_measurement(measurement_type, 'Electrode work function',
                                                       'Work function of metal electrode',
                                                       measurement_datetime=None, create_new=False)
         WF_arr, _, _, _ = self.Project.get_data_points(measurement_id, 'Metal electrode work function')
         if len(WF_arr) > 0:
             WF = WF_arr[-1]
     except:
         metal_label = None
         WF = 0
     if Metal is None:
         if metal_label is None:
             raise Exception('Metal electriode is needed for Schottky diode')
         else:
             self.Metal = MetalElectrode(metal_label, WF)
     else:
         if Metal.label != metal_label:
             self.Project.add_project_info_if_changed('Schottky electrode', Metal.label)
             _, WF = self.Project.add_monitored_datapoint(measurement_type, 'Electrode work function',
                                                          'Work function of metal electrode',
                                                          self.tool_id, 'Metal electrode work function', 'Metal WF',
                                                          'J', None, to_numeric(Metal.WF))
         self.Metal = Metal
Пример #10
0
 def energy_level(self,
                  temperature,
                  semiconductor,
                  charge_state_idx=0,
                  electron_volts=False):
     """
     Trap energy level measured from Ec (Conduction band) towards Ev (Valence band)
     """
     if isinstance(temperature, (tuple, list, np.ndarray)):
         energy_level_wrap = partial(self.energy_level,
                                     semiconductor=semiconductor,
                                     charge_state_idx=charge_state_idx,
                                     electron_volts=electron_volts)
         return np.array(map(energy_level_wrap, temperature))
     band_gap = semiconductor.band_gap(temperature,
                                       symbolic=False,
                                       electron_volts=False)
     try:
         level = self.charge_states[charge_state_idx][1].subs(
             'Eg', band_gap)
     except AttributeError:
         level = self.charge_states[charge_state_idx][1]
     if electron_volts:
         level /= q
     return to_numeric(level)
Пример #11
0
 def init():
     Eg = SchDiode.Semiconductor.band_gap(SchDiode.T, symbolic=False, electron_volts=eV)
     bands_lines['Ec'].set_data([], [])
     bands_lines['Ev'].set_data([], [])
     bands_lines['Ef'].set_data([], [])
     for dopant in SchDiode.Semiconductor.dopants:
         threshold_dopant = 0.05 if eV else to_numeric(q) * 0.05
         level = dopant.energy_level(SchDiode.T, SchDiode.Semiconductor, electron_volts=eV)
         if level > threshold_dopant and level < (Eg - threshold_dopant):
             dopant_lines[dopant.name].set_data([], [])
     if label_bands:
         bands_labels['Ec'].set_text('')
         bands_labels['Ev'].set_text('')
         bands_labels['Ef'].set_text('')
     V_label.set_text('')
     T_label.set_text('')
     for key in BI_levels.keys():
         BI_levels[key].set_offsets(np.array([]))
         BI_levels[key].set_array(np.array([]))
     fig_list = bands_lines.values()
     fig_list.extend(bands_labels.values())
     fig_list.extend(dopant_lines.values())
     fig_list.extend([V_label, T_label])
     fig_list.extend(BI_levels.values())
     return fig_list
Пример #12
0
def RhoDiagram(ax, SchDiode, Psi=Psi_zero, z=0):
    print 'Rho Diagram Start'
    rho_semi_num = SchDiode.build_rho_z_Psi(Psi, carriers_charge=True)
    rho = rho_semi_num(z, Psi) / to_numeric(q)
    n, p = SchDiode.n_carriers_theory(Psi, z)
    dopants_rho = []
    for dopant in SchDiode.Semiconductor.dopants:
        Nd = dopant.concentration(z)
        dopants_rho.append(
            Nd * ((dopant.charge_states[1][0] - dopant.charge_states[0][0]) * dopant.F(z) + dopant.charge_states[0][0]))

    ax.plot(z * 1e6, abs(rho * 1e-6), linewidth=2, color='black', linestyle='-')

    if SchDiode.Semiconductor.dop_type == 'n':
        ax.plot(z * 1e6, n * 1e-6, linewidth=2, color='blue', linestyle='-')
    elif SchDiode.Semiconductor.dop_type == 'p':
        ax.plot(z * 1e6, p * 1e-6, linewidth=2, color='red', linestyle='-')
    for dopant_rho in dopants_rho:
        ax.plot(z * 1e6, dopant_rho * 1e-6, linewidth=2, color='green', linestyle='-')

    ax.set_yscale('log')
    # ax.ticklabel_format(axis='y', style='sci', scilimits=(-7,7))
    ax.set_title('Net density of charge')
    ax.set_ylabel('Concentration, cm$^{-3}$')
    ax.set_xlabel(r'Coordinate (z), um')
    ax.grid(True)
    print 'Rho Diagram Stop'
Пример #13
0
 def capture_cross_sections(self, temperature):
     """
     Calculates temperature dependent capture cross section
     :param temperature: Temperature in K
     :return: Temperature dependent capture cross sections
     """
     energy_scale = to_numeric(k * temperature)
     exp_term_e = np.exp(-self.electron_capture_cross_section_activation_energy / energy_scale)
     exp_term_h = np.exp(-self.hole_capture_cross_section_activation_energy / energy_scale)
     return self.electron_capture_cross_section * exp_term_e, self.hole_capture_cross_section * exp_term_h
Пример #14
0
 def dn_carriers_dEf_theory(self, Psi=Psi_zero, z=0):
     k_n = to_numeric(k)
     Ef = self.EfEc(Psi, z, eV=False)
     Eg = self.Semiconductor.band_gap(self.T, symbolic=False, electron_volts=False)
     F_n = np.exp(-Ef / (k_n * self.T))
     F_p = np.exp((Ef - Eg) / (k_n * self.T))
     dn = np.float(self.Semiconductor.Nc(self.T, symbolic=False)) / (k_n * self.T) * F_n
     dp = np.float(-self.Semiconductor.Nv(self.T, symbolic=False)) / to_numeric(k * self.T) * F_p
     if isinstance(z, np.ndarray):
         idx = np.where(z < self.DeadLayer)[0]
         if idx.size > 0:
             dn[idx] = 0.0
             dp[idx] = 0.0
         idx = np.where(z < self.FieldInversionPoint)[0]
         if idx.size > 0:
             dn[idx] = 0.0
             dp[idx] = 0.0
     elif z < self.DeadLayer or z < self.FieldInversionPoint:
         dn = 0
         dp = 0
     return dn, dp
Пример #15
0
 def capture_cross_sections(self, temperature):
     """
     Calculates temperature dependent capture cross section
     :param temperature: Temperature in K
     :return: Temperature dependent capture cross sections
     """
     energy_scale = to_numeric(k * temperature)
     exp_term_e = np.exp(
         -self.electron_capture_cross_section_activation_energy /
         energy_scale)
     exp_term_h = np.exp(
         -self.hole_capture_cross_section_activation_energy / energy_scale)
     return self.electron_capture_cross_section * exp_term_e, self.hole_capture_cross_section * exp_term_h
Пример #16
0
def annotate_energies(ax, SchDiode, arrow_z_pos, Psi, Vd, SchottkyEffect, metal_size, draw_metal, eV, draw_energies):
    annot_fig = {}
    if draw_energies:
        barrier_style = [1, 'black', '--']
        z_barrier, phi_bn, _, phi_b = SchDiode.get_phi_bn(Psi, Vd, SchottkyEffect=SchottkyEffect)
        if not eV:
            phi_bn *= to_numeric(q)
            phi_b *= to_numeric(q)
        z_barrier *= 1e6
        phi_bn_z = -metal_size[0] / 2
        phi_b_z = arrow_z_pos * 1e6
        allowed_band = SchDiode.Ec(Psi, Vd, np.float(phi_b_z * 1e-6), eV=eV)
        if SchDiode.Semiconductor.dop_type == 'p':
            phi_bn *= -1
            allowed_band = SchDiode.Ev(Psi, Vd, np.float(phi_b_z * 1e-6), eV=eV)
            if draw_metal:
                phi_bn_z = -metal_size[0] - 0.1
        annot_fig['zero_line'] = ax.hlines(0, phi_bn_z, 0,
                                           linewidth=barrier_style[0], color=barrier_style[1],
                                           linestyle=barrier_style[2])
        annot_fig['phi_bn_line'] = ax.hlines(phi_bn, phi_bn_z, z_barrier,
                                             linewidth=barrier_style[0], color=barrier_style[1],
                                             linestyle=barrier_style[2])
        annot_fig['phi_bn_arrows'] = ax.annotate('', xy=(phi_bn_z, phi_bn), xycoords='data', xytext=(phi_bn_z, 0),
                                                 textcoords='data', arrowprops={'arrowstyle': '<->'})
        annot_fig['phi_bn_annot'] = ax.annotate('$q\phi_{bn}$', xy=(phi_bn_z, phi_bn / 2), xycoords='data',
                                                xytext=(-5, 0), textcoords='offset points',
                                                horizontalalignment='right', verticalalignment='top')
        annot_fig['phi_b_line'] = ax.hlines(phi_bn, z_barrier, phi_b_z,
                                            linewidth=barrier_style[0], color=barrier_style[1],
                                            linestyle=barrier_style[2])
        annot_fig['phi_b_arrows'] = ax.annotate('', xy=(phi_b_z, phi_bn), xycoords='data',
                                                xytext=(phi_b_z, allowed_band), textcoords='data',
                                                arrowprops={'arrowstyle': '<->'})
        annot_fig['phi_b_annot'] = ax.annotate('$q\phi_{b}$', xy=(phi_b_z, (phi_bn + allowed_band) / 2),
                                               xycoords='data', xytext=(5, 0), textcoords='offset points',
                                               horizontalalignment='left', verticalalignment='top')
        return annot_fig
Пример #17
0
def annotate_energies(ax, SchDiode, arrow_z_pos, Psi, Vd, SchottkyEffect, metal_size, draw_metal, eV, draw_energies):
    annot_fig = {}
    if draw_energies:
        barrier_style = [1, 'black', '--']
        z_barrier, phi_bn, _, phi_b = SchDiode.get_phi_bn(Psi, Vd, SchottkyEffect=SchottkyEffect)
        if not eV:
            phi_bn *= to_numeric(q)
            phi_b *= to_numeric(q)
        z_barrier *= 1e6
        phi_bn_z = -metal_size[0] / 2
        phi_b_z = arrow_z_pos * 1e6
        allowed_band = SchDiode.Ec(Psi, Vd, np.float(phi_b_z * 1e-6), eV=eV)
        if SchDiode.Semiconductor.dop_type == 'p':
            phi_bn *= -1
            allowed_band = SchDiode.Ev(Psi, Vd, np.float(phi_b_z * 1e-6), eV=eV)
            if draw_metal:
                phi_bn_z = -metal_size[0] - 0.1
        annot_fig['zero_line'] = ax.hlines(0, phi_bn_z, 0,
                                           linewidth=barrier_style[0], color=barrier_style[1],
                                           linestyle=barrier_style[2])
        annot_fig['phi_bn_line'] = ax.hlines(phi_bn, phi_bn_z, z_barrier,
                                             linewidth=barrier_style[0], color=barrier_style[1],
                                             linestyle=barrier_style[2])
        annot_fig['phi_bn_arrows'] = ax.annotate('', xy=(phi_bn_z, phi_bn), xycoords='data', xytext=(phi_bn_z, 0),
                                                 textcoords='data', arrowprops={'arrowstyle': '<->'})
        annot_fig['phi_bn_annot'] = ax.annotate('$q\phi_{bn}$', xy=(phi_bn_z, phi_bn / 2), xycoords='data',
                                                xytext=(-5, 0), textcoords='offset points',
                                                horizontalalignment='right', verticalalignment='top')
        annot_fig['phi_b_line'] = ax.hlines(phi_bn, z_barrier, phi_b_z,
                                            linewidth=barrier_style[0], color=barrier_style[1],
                                            linestyle=barrier_style[2])
        annot_fig['phi_b_arrows'] = ax.annotate('', xy=(phi_b_z, phi_bn), xycoords='data',
                                                xytext=(phi_b_z, allowed_band), textcoords='data',
                                                arrowprops={'arrowstyle': '<->'})
        annot_fig['phi_b_annot'] = ax.annotate('$q\phi_{b}$', xy=(phi_b_z, (phi_bn + allowed_band) / 2),
                                               xycoords='data', xytext=(5, 0), textcoords='offset points',
                                               horizontalalignment='left', verticalalignment='top')
        return annot_fig
Пример #18
0
 def d_rho_dDPsi(z, Psi):
     dn_nodes, dp_nodes = SchDiode.dn_carriers_dEf_theory(Psi, z)
     if SchDiode.Semiconductor.dop_type == 'n':
         dp_nodes = np.zeros_like(z)
     elif SchDiode.Semiconductor.dop_type == 'p':
         dn_nodes = np.zeros_like(z)
     d_carriers_nodes = dn_nodes - dp_nodes
     dN_dopants_nodes = np.zeros_like(z)
     for dopant in SchDiode.Semiconductor.dopants:
         dN_dopants_nodes += dopant.concentration(z) * (
             dopant.charge_states[1][0] - dopant.charge_states[0][0]) * dopant.dF(z)
     dN_BI_nodes = np.zeros_like(z)
     for BI in SchDiode.Semiconductor.bonding_interfaces:
         dN_BI_nodes += smooth_dd(z - BI.depth, BI.smooth_dirac_epsilon) * BI.d_density_of_charge
     return (d_carriers_nodes - dN_dopants_nodes - dN_BI_nodes) * to_numeric(q ** 2) / Eps0Eps
Пример #19
0
 def energy_level(self, temperature, semiconductor, charge_state_idx=0, electron_volts=False):
     """
     Trap energy level measured from Ec (Conduction band) towards Ev (Valence band)
     """
     if isinstance(temperature, (tuple, list, np.ndarray)):
         energy_level_wrap = partial(self.energy_level, semiconductor=semiconductor,
                                     charge_state_idx=charge_state_idx, electron_volts=electron_volts)
         return np.array(map(energy_level_wrap, temperature))
     band_gap = semiconductor.band_gap(temperature, symbolic=False, electron_volts=False)
     try:
         level = self.charge_states[charge_state_idx][1].subs('Eg', band_gap)
     except AttributeError:
         level = self.charge_states[charge_state_idx][1]
     if electron_volts:
         level /= q
     return to_numeric(level)
Пример #20
0
    def get_phi_bn(self, Psi=Psi_zero, Va=0, SchottkyEffect=False):
        chg_sign = 1 if self.Semiconductor.dop_type == 'n' else -1
        F = to_numeric(q / (16 * np.pi * epsilon0 * self.Semiconductor.reference['epsilon']))
        if not SchottkyEffect:
            F = 0

        def f(z):
            if F != 0:
                if not isinstance(z, np.ndarray):
                    return chg_sign * (Psi(z) + chg_sign * F / z)
                else:
                    return chg_sign * np.array([Psi(zz) + chg_sign * F / zz for zz in z])
            else:
                if not isinstance(z, np.ndarray):
                    return chg_sign * Psi(z)
                else:
                    #return chg_sign * np.array([Psi(zz) for zz in z])
                    return chg_sign * Psi(z)
        z_0 = np.linspace(1e-10, np.float(self.L), num=50)
        extrema_z = np.zeros_like(z_0)
        warnings.filterwarnings("ignore", category=RuntimeWarning)
        for i in range(z_0.size):
            extrema_z[i] = fmin(f, z_0[i], disp=False)
            # print extrema_z[i]*1e6
        warnings.resetwarnings()
        extrema = f(extrema_z)
        # _, (ax1, ax2, ax3) = plt.subplots(3)
        # ax1.plot(z_0*1e6, f(z_0))
        # ax2.plot(z_0*1e6, extrema_z*1e6)
        # ax3.plot(extrema_z*1e6, extrema)
        # plt.show()

        z_max = extrema_z[np.argmin(extrema)]
        if z_max < 0:
            z_max = 1e-15
        xi = self.Semiconductor.Ech_pot(T=self.T, z=1e3, eV=True, debug=False)
        Eg = self.Semiconductor.band_gap(self.T, symbolic=False, electron_volts=True)
        if self.Semiconductor.dop_type == 'n':
            phi_bn = abs(-f(z_max) + xi + Va)
            delta_phi = abs(self.Ec(Psi, Va, z_max, eV=True) - phi_bn)
            phi_b = phi_bn - xi
        else:
            phi_bn = abs(f(z_max) + xi - Va - Eg)
            delta_phi = abs(self.Ev(Psi, Va, z_max, eV=True) - phi_bn)
            phi_b = phi_bn - Eg + xi
        # print 'Z_max =', z_max*1e6, 'um'
        return np.float(z_max), np.float(phi_bn), np.float(delta_phi), np.float(phi_b)
Пример #21
0
 def rho_z_Psi(z, Psi):
     rho = 0
     if carriers_charge:
         if Psi != Psi_zero:
             n, p = self.n_carriers_theory(Psi, z)
             if self.Semiconductor.dop_type == 'n':
                 p = 0
             elif self.Semiconductor.dop_type == 'p':
                 n = 0
             rho += p - n
     for dopant in self.Semiconductor.dopants:
         Nd = dopant.concentration(z)
         rho += Nd * (
             (dopant.charge_states[1][0] - dopant.charge_states[0][0]) * dopant.F(z) +
             dopant.charge_states[0][
                 0])
     for BI in self.Semiconductor.bonding_interfaces:
         rho += smooth_dd(z - BI.depth, BI.smooth_dirac_epsilon) * BI.density_of_charge
     return to_numeric(q) * rho
Пример #22
0
 def d_rho_dDPsi(z, Psi):
     dn_nodes, dp_nodes = SchDiode.dn_carriers_dEf_theory(Psi, z)
     if SchDiode.Semiconductor.dop_type == 'n':
         dp_nodes = np.zeros_like(z)
     elif SchDiode.Semiconductor.dop_type == 'p':
         dn_nodes = np.zeros_like(z)
     d_carriers_nodes = dn_nodes - dp_nodes
     dN_dopants_nodes = np.zeros_like(z)
     for dopant in SchDiode.Semiconductor.dopants:
         dN_dopants_nodes += dopant.concentration(z) * (
             dopant.charge_states[1][0] -
             dopant.charge_states[0][0]) * dopant.dF(z)
     dN_BI_nodes = np.zeros_like(z)
     for BI in SchDiode.Semiconductor.bonding_interfaces:
         dN_BI_nodes += smooth_dd(
             z - BI.depth,
             BI.smooth_dirac_epsilon) * BI.d_density_of_charge
     return (d_carriers_nodes - dN_dopants_nodes -
             dN_BI_nodes) * to_numeric(q**2) / Eps0Eps
Пример #23
0
def Reccurent_Poisson_solver(SchDiode, Psi=Psi_zero, Vd_guess=None, Vd_error=1e-6,
                             equilibrium_filling=True, fast_traps=None,
                             t=mp.inf, initial_condition_id=-1,
                             rho_rel_err=1e-3, max_iter=100, debug=False):
    recurrent_solver_start_time = time.time()
    Va = SchDiode.Va
    kT_eV = to_numeric(k * SchDiode.T / q)
    measurement_id = add_Electric_Field_Measurement(SchDiode, Va, equilibrium_filling, t, initial_condition_id)
    Solution_found, Psi_found, E, z_nodes, rho_rel_err_points, Vd, Vd_err, J, J_err, BI_F, dopants_F, measurement_id = load_diode_state(
        SchDiode, measurement_id, initial_condition_id, debug)
    if Solution_found:
        Psi = Psi_found
        Vd_guess = Vd[-1]
        J_tmp = J[-1]
        if max(abs(rho_rel_err_points)) <= rho_rel_err:
            if debug: print '==> Solution satisfy rel_error condition'
            return Psi, E, z_nodes, rho_rel_err_points, Vd[0], Vd_err[0], J[0], J_err[
                0], BI_F, dopants_F, measurement_id
        if debug or 1:
            print '==> Solution found does not satisfy rel_error condition'
            print '==> Recalculating...'
            SchDiode.FieldInversionPoint, PHI_bn, _, PHI_b = SchDiode.get_phi_bn(Psi, Vd_guess, SchottkyEffect=False)
    else:
        if debug: print '==> No solutions found'
        if Vd_guess is None:
            Vd_guess = Va
            PHI_b = abs(SchDiode.V_bi(eV=True))
            SchDiode.FieldInversionPoint = 0
        else:
            SchDiode.FieldInversionPoint, PHI_bn, _, PHI_b = SchDiode.get_phi_bn(Psi, Vd_guess, SchottkyEffect=False)
            if Va < PHI_b:
                Vd_guess = Va
        J_tmp = 0

    converged = False
    current_iter = 0
    Vd_tmp_corr = 0
    Vd_guess_monitor_length = 5
    Vd_guess_monitor = np.arange(Vd_guess_monitor_length, dtype=np.float)
    Vd_guess_monitor_count = 0
    stalled = False
    while not converged and not stalled and current_iter < max_iter:
        if debug: print '\nIteration:', current_iter, '**'
        if debug or 1: print 'Vd_tmp =', Vd_guess
        if debug: print 'PHI_b =', PHI_b
        if Vd_guess >= PHI_b:
            Vd_guess = PHI_b - 1 * kT_eV
        if debug: print 'Vd_tmp =', Vd_guess
        Vd_guess_monitor[Vd_guess_monitor_count] = Vd_guess
        Vd_guess_monitor_count += 1
        if Vd_guess_monitor_count == Vd_guess_monitor_length:
            Vd_guess_monitor_count = 0
        if np.unique(Vd_guess_monitor).size == 1:
            stalled = True
            print 'Solution process stalled on iteration', current_iter, '!!!'
            continue
        nodes_num = int(np.floor(SchDiode.L * 1e6 + 1) * 100)
        nodes, _ = np.linspace(0, SchDiode.L, num=nodes_num + 1, endpoint=True, retstep=True)
        Meshes = Poisson_eq_num_solver_amr(SchDiode, nodes, Psi, Vd_guess, equilibrium_filling, fast_traps,
                                           max_iterations=50, residual_threshold=rho_rel_err,
                                           int_residual_threshold=5e-14,
                                           max_level=5, mesh_refinement_threshold=1e-19, debug=debug)
        z_nodes, Psi_points, rho_rel_err_points = Meshes.flatten(debug=False)
        dz = np.gradient(z_nodes)
        E_points = -np.gradient(Psi_points, dz, edge_order=2)
        Psi = interp_Fn(z_nodes, Psi_points, interp_type='last')
        E = interp_Fn(z_nodes, E_points, interp_type='last')
        SchDiode.FieldInversionPoint, PHI_bn, _, PHI_b = SchDiode.get_phi_bn(Psi, Vd_guess, SchottkyEffect=False)
        print '*** !!! Inversion pt (calc) !!!', SchDiode.FieldInversionPoint
        if debug: print 'PHI_b, PHI_bn =', PHI_b, PHI_bn
        Vd_tmp_corr, J_tmp_corr = SchDiode.ThermionicEmissionCurrent(Va, PHI_bn, debug=True)
        if debug: print 'V_corr, J =', Vd_tmp_corr, J_tmp_corr
        Vd_err = abs(Vd_guess - Vd_tmp_corr)
        J_err = abs(J_tmp - J_tmp_corr)
        if debug or 1: print 'Vd err =', Vd_err
        if debug or 1: print 'J err =', J_err, '\n'
        if Vd_err < Vd_error:
            converged = True
        else:
            Vd_guess = Vd_tmp_corr
            J_tmp = J_tmp_corr
        current_iter += 1
    Vd = Vd_tmp_corr
    J = J_tmp
    if debug: print 'Calculation converged'
    recurrent_solver_elapsed_time = time.time() - recurrent_solver_start_time
    if debug: print 'Total recurrent solver execution time =', recurrent_solver_elapsed_time, 's\n'

    save_diode_state(SchDiode, measurement_id, initial_condition_id,
                     z_nodes, Psi_points, E_points, rho_rel_err_points,
                     Vd, Vd_err, J, J_err, debug)
    BI_F = {}
    for BI in SchDiode.Semiconductor.bonding_interfaces:
        for i, trap in enumerate(BI.dsl_tilt.traps):
            BI_F[BI.label + '_tilt_' + trap[0].name + '_F'] = BI.dsl_tilt_f[i]
        for i, trap in enumerate(BI.dsl_twist.traps):
            BI_F[BI.label + '_twist_' + trap[0].name + '_F'] = BI.dsl_twist_f[i]
    dopants_F = {}
    for dopant in SchDiode.Semiconductor.dopants:
        dopants_F[dopant.name + '_F'] = dopant.F(z_nodes)
    recurrent_solver_elapsed_time = time.time() - recurrent_solver_start_time
    if debug: print 'Total recurrent solver execution time =', recurrent_solver_elapsed_time, 's'
    return Psi, E, z_nodes, rho_rel_err_points, Vd, Vd_err, J, J_err, BI_F, dopants_F, measurement_id
Пример #24
0
    def equilibrium_f(self, temperature, semiconductor, fermi_level_from_conduction_band,
                      electron_volts=False, use_mpmath=False, debug=False):
        """
        Calculates trap equilibrium filling for given Fermi level position
        :param temperature: Temperature, K
        :param semiconductor: Semiconductor object
        :param fermi_level_from_conduction_band: distance from Conduction band to Fermi level
        :param electron_volts: if True assume all energy values to be in eV
        :param use_mpmath: if True integration is done using mpmath.quad function instead of numpy.trapz (default)
        :param debug: if True prints out some debug information
        :return: equilibrium f between 0 and 1
        """
        if electron_volts:
            energy_coefficient = to_numeric(q)
            energy_unit = 'eV'
        else:
            energy_coefficient = 1
            energy_unit = 'J'
        energy_scale = to_numeric(k * temperature) / energy_coefficient
        conduction_band = 0
        fermi_level = conduction_band - fermi_level_from_conduction_band
        trap_energy_level = self.energy_level(temperature, semiconductor, charge_state_idx=0,
                                              electron_volts=electron_volts)
        if debug:
            print 'Et = %2.2g ' % ((conduction_band - trap_energy_level)) + energy_unit
            print 'Ef =', fermi_level, energy_unit
        g_ratio = self.charge_states[0][2] / self.charge_states[1][2]

        if self.energy_distribution_function in energy_distribution_functions['Single Level']:
            fermi_level_grid, = np.meshgrid(fermi_level)
            exp_arg = (np.float(conduction_band - trap_energy_level) - fermi_level_grid) / energy_scale
            exp_term = np.exp(exp_arg)
            f = 1 / (1 + g_ratio * exp_term)
        else:
            energy = sym.symbols('E')
            energy_distribution = self.energy_distribution.subs([('Et', trap_energy_level * energy_coefficient),
                                                                 ('Ecb', conduction_band * energy_coefficient)])
            energy_distribution = energy_distribution.subs(q, to_numeric(q))
            if not use_mpmath:
                if debug:
                    print 'Numeric integration (numpy.trapz)'
                energy_range = centered_linspace(conduction_band - trap_energy_level,
                                                 10 * to_numeric(self.energy_spread) / energy_coefficient,
                                                 to_numeric(self.energy_spread) / energy_coefficient / 1000)
                energy_range_grid, fermi_level_grid = np.meshgrid(energy_range, fermi_level)
                fermi_function = 1 / (1 + g_ratio * np.exp((energy_range_grid - fermi_level_grid) / energy_scale))
                energy_distribution_function = sym.lambdify(energy, energy_distribution, 'numpy')
                integrand_array = energy_distribution_function(energy_range_grid * energy_coefficient) * fermi_function
                f = np.trapz(integrand_array, energy_range_grid * energy_coefficient, axis=1)
            else:
                if debug:
                    print 'Numeric integration (mpmath.quad)'
                fermi_level_grid, = np.meshgrid(fermi_level)
                f = np.zeros_like(fermi_level_grid)
                for i, fermi_level_i in enumerate(fermi_level_grid):
                    fermi_function = fermi(energy, fermi_level_i * energy_coefficient, temperature, g_ratio)
                    fermi_function = fermi_function.subs(k, to_numeric(k))
                    integrand = sym.lambdify(energy, energy_distribution * fermi_function)
                    f[i] = mp.quad(integrand,
                                   [to_numeric(energy_coefficient * (conduction_band - trap_energy_level)
                                               - 10 * self.energy_spread),
                                    to_numeric(energy_coefficient * (conduction_band - trap_energy_level)
                                               - 0.5 * self.energy_spread),
                                    to_numeric(energy_coefficient * (conduction_band - trap_energy_level)
                                               + 0.5 * self.energy_spread),
                                    to_numeric(energy_coefficient * (conduction_band - trap_energy_level)
                                               + 10 * self.energy_spread)])
        if debug:
            print 'F =', f
        if f.size == 1:
            f = f[0]
        return f
Пример #25
0
    def emission_rate(self,
                      temperature,
                      semiconductor,
                      f,
                      poole_frenkel_e=None,
                      poole_frenkel_h=None,
                      barrier_lowering_e=None,
                      barrier_lowering_h=None,
                      use_mpmath=False,
                      debug=False):
        """
        Calculate carriers emission rate for bot electrons and holes
        :param temperature: Temperature, K
        :param semiconductor: Semiconductor object
        :param f: Trap occupation from 0.0 to 1.0
        :param poole_frenkel_e: emission rate boost due to Poole-Frenkel effect for electron
        :param poole_frenkel_h: emission rate boost due to Poole-Frenkel effect for electron
        :param barrier_lowering_e: lowering of activation energy for electrons
        :param barrier_lowering_h: lowering of activation energy for holes
        :param use_mpmath: if True integration is done using mpmath.quad function instead of numpy.trapz (default)
        :param debug: if True prints out some debug information
        :return: emission_e, emission_h
        """
        if barrier_lowering_e is None:
            barrier_lowering_e = np.zeros_like(f, dtype=np.float)
        if barrier_lowering_h is None:
            barrier_lowering_h = np.zeros_like(f, dtype=np.float)
        if poole_frenkel_e is None:
            poole_frenkel_e = np.ones_like(f, dtype=np.float)
        if poole_frenkel_h is None:
            poole_frenkel_h = np.ones_like(f, dtype=np.float)
        energy_scale = to_numeric(k * temperature)
        conduction_band = 0
        band_gap = semiconductor.band_gap(temperature,
                                          symbolic=False,
                                          electron_volts=False)
        valence_band = conduction_band - band_gap
        trap_energy_level_e = np.float(
            self.energy_level(temperature,
                              semiconductor,
                              charge_state_idx=1,
                              electron_volts=False))
        trap_energy_level_h = np.float(
            self.energy_level(temperature,
                              semiconductor,
                              charge_state_idx=0,
                              electron_volts=False))
        trap_energy_level_e_positive = conduction_band - trap_energy_level_e
        trap_energy_level_h_positive = conduction_band - trap_energy_level_h

        g_ratio_e = self.charge_states[0][2] / self.charge_states[1][2]
        g_ratio_h = self.charge_states[1][2] / self.charge_states[0][2]

        v_e = np.float(semiconductor.v_T('e', temperature, symbolic=False))
        v_h = np.float(semiconductor.v_T('h', temperature, symbolic=False))
        if debug:
            print '<v_e> =', v_e, 'm/s'
            print '<v_h> =', v_h, 'm/s'
        sigma_n, sigma_p = self.capture_cross_sections(temperature)
        fore_factor_n = np.float(
            sigma_n * v_e * semiconductor.Nc(temperature, symbolic=False) *
            g_ratio_e)
        fore_factor_p = np.float(
            sigma_p * v_h * semiconductor.Nv(temperature, symbolic=False) *
            g_ratio_h)
        fore_factor_n *= poole_frenkel_e
        fore_factor_p *= poole_frenkel_h
        if debug:
            print 'factor_n =', fore_factor_n
            print 'factor_p =', fore_factor_p

        if self.energy_distribution_function in energy_distribution_functions[
                'Single Level']:
            activation_energy_e = conduction_band - trap_energy_level_e_positive - barrier_lowering_e
            activation_energy_h = trap_energy_level_h_positive - valence_band - barrier_lowering_h
            emission_rate_e = fore_factor_n * np.exp(
                -activation_energy_e / energy_scale)
            emission_rate_h = fore_factor_p * np.exp(
                -activation_energy_h / energy_scale)
            emission_time_constant_e = 1 / emission_rate_e
            emission_time_constant_h = 1 / emission_rate_h
            emission_e = emission_rate_e * f
            emission_h = emission_rate_h * (1 - f)
        else:
            quasi_fermi_level = self.f_to_equilibrium_fermi_level(
                temperature,
                semiconductor,
                f,
                electron_volts=False,
                use_mpmath=use_mpmath,
                debug=False)
            quasi_fermi_level = conduction_band - quasi_fermi_level
            if debug:
                print 'Eqf =', quasi_fermi_level / to_numeric(q), 'eV'
            energy = sym.symbols('E')
            energy_distribution_e = self.energy_distribution.subs([
                ('Et', trap_energy_level_e), ('Ecb', conduction_band)
            ])
            energy_distribution_e = energy_distribution_e.subs(
                q, to_numeric(q))
            energy_distribution_h = self.energy_distribution.subs([
                ('Et', trap_energy_level_h), ('Ecb', conduction_band)
            ])
            energy_distribution_h = energy_distribution_h.subs(
                q, to_numeric(q))
            energy_range_e = centered_linspace(
                conduction_band - trap_energy_level_e,
                10 * to_numeric(self.energy_spread),
                to_numeric(self.energy_spread) / 1000)
            energy_range_h = centered_linspace(
                conduction_band - trap_energy_level_h,
                10 * to_numeric(self.energy_spread),
                to_numeric(self.energy_spread) / 1000)
            energy_distribution_function_e = sym.lambdify(
                energy, energy_distribution_e, 'numpy')
            energy_distribution_function_h = sym.lambdify(
                energy, energy_distribution_h, 'numpy')
            energy_range_grid_e, barrier_lowering_grid_e = np.meshgrid(
                energy_range_e, barrier_lowering_e)
            energy_range_grid_h, barrier_lowering_grid_h = np.meshgrid(
                energy_range_h, barrier_lowering_h)
            exp_term_e = np.exp(-(conduction_band - energy_range_grid_e +
                                  barrier_lowering_grid_e) / energy_scale)
            exp_term_h = np.exp(
                -(energy_range_grid_h - barrier_lowering_grid_h - valence_band)
                / energy_scale)
            emission_rate_e = energy_distribution_function_e(
                energy_range_e) * exp_term_e * fore_factor_n
            emission_rate_h = energy_distribution_function_h(
                energy_range_h) * exp_term_h * fore_factor_p
            emission_rate_e_max = np.max(emission_rate_e, axis=1)
            emission_rate_h_max = np.max(emission_rate_h, axis=1)
            emission_time_constant_e = 1 / emission_rate_e_max
            emission_time_constant_h = 1 / emission_rate_h_max
            if not use_mpmath:
                if debug:
                    print 'Numeric integration (numpy.trapz)'
                energy_range_grid_e, fermi_level_grid_e = np.meshgrid(
                    energy_range_e, quasi_fermi_level)
                energy_range_grid_h, fermi_level_grid_h = np.meshgrid(
                    energy_range_h, quasi_fermi_level)
                fermi_function_e = 1 / (1 + g_ratio_e * np.exp(
                    (energy_range_grid_e - fermi_level_grid_e) / energy_scale))
                fermi_function_h = 1 - 1 / (1 + g_ratio_h * np.exp(
                    (energy_range_grid_h - fermi_level_grid_h) / energy_scale))
                exp_term_e = np.exp(-(conduction_band - energy_range_grid_e +
                                      barrier_lowering_e) / energy_scale)
                exp_term_h = np.exp(
                    -(energy_range_grid_h - barrier_lowering_h - valence_band)
                    / energy_scale)
                emission_rate_e = energy_distribution_function_e(
                    energy_range_grid_e) * exp_term_e
                emission_rate_h = energy_distribution_function_h(
                    energy_range_grid_h) * exp_term_h
                integrand_array_e = emission_rate_e * fermi_function_e
                integrand_array_h = emission_rate_h * fermi_function_h
                emission_e = np.trapz(integrand_array_e,
                                      energy_range_grid_e,
                                      axis=1) * fore_factor_n
                emission_h = np.trapz(integrand_array_h,
                                      energy_range_grid_h,
                                      axis=1) * fore_factor_p
            else:
                if debug:
                    print 'Numeric integration (mpmath.quad)'
                exp_term_e = sym.exp(
                    -(conduction_band - energy + barrier_lowering_e) /
                    energy_scale)
                exp_term_h = sym.exp(
                    -(energy - barrier_lowering_h - valence_band) /
                    energy_scale)
                quasi_fermi_level_grid, = np.meshgrid(quasi_fermi_level)
                emission_e = np.zeros_like(quasi_fermi_level_grid)
                emission_h = np.zeros_like(quasi_fermi_level_grid)
                for i, quasi_fermi_level_i in enumerate(
                        quasi_fermi_level_grid):
                    fermi_function_e = fermi(energy, quasi_fermi_level_i,
                                             temperature, g_ratio_e)
                    fermi_function_e = fermi_function_e.subs(k, to_numeric(k))
                    fermi_function_h = fermi(quasi_fermi_level_i, energy,
                                             temperature, g_ratio_h)
                    fermi_function_h = fermi_function_h.subs(k, to_numeric(k))
                    integrand_e = sym.lambdify(
                        energy,
                        energy_distribution_e * fermi_function_e * exp_term_e)
                    integrand_h = sym.lambdify(
                        energy,
                        energy_distribution_h * fermi_function_h * exp_term_h)
                    emission_integral_e = mp.quad(integrand_e, [
                        to_numeric(trap_energy_level_e_positive -
                                   10 * self.energy_spread),
                        to_numeric(trap_energy_level_e_positive -
                                   0.5 * self.energy_spread),
                        to_numeric(trap_energy_level_e_positive +
                                   0.5 * self.energy_spread),
                        to_numeric(trap_energy_level_e_positive +
                                   10 * self.energy_spread)
                    ])
                    emission_integral_h = mp.quad(integrand_h, [
                        to_numeric(trap_energy_level_h_positive -
                                   10 * self.energy_spread),
                        to_numeric(trap_energy_level_h_positive -
                                   0.5 * self.energy_spread),
                        to_numeric(trap_energy_level_h_positive +
                                   0.5 * self.energy_spread),
                        to_numeric(trap_energy_level_h_positive +
                                   10 * self.energy_spread)
                    ])
                    emission_e[i] = np.float(emission_integral_e *
                                             fore_factor_n)
                    emission_h[i] = np.float(emission_integral_h *
                                             fore_factor_p)
        if isinstance(emission_e, np.ndarray):
            if emission_e.size == 1:
                emission_e = emission_e[0]
                emission_h = emission_h[0]
                emission_time_constant_e = emission_time_constant_e[0]
                emission_time_constant_h = emission_time_constant_h[0]
        if debug: print 'emission_e =', emission_e
        if debug: print 'emission_h =', emission_h
        if debug: print 'emission_tau_e =', emission_time_constant_e
        if debug: print 'emission_tau_h =', emission_time_constant_e
        return emission_e, emission_h, emission_time_constant_e, emission_time_constant_h
Пример #26
0
 def energy_distribution_diagram(self,
                                 ax,
                                 temperature,
                                 semiconductor,
                                 trap_concentration=1,
                                 trap_concentration_units='',
                                 fermi_level_from_conduction_band=None,
                                 electron_volts=True,
                                 fancy_labels=False):
     if electron_volts:
         energy_coefficient = to_numeric(q)
         energy_unit = 'eV'
     else:
         energy_coefficient = 1
         energy_unit = 'J'
     energy_scale = to_numeric(k * temperature) / energy_coefficient
     conduction_band = 0
     g_ratio = self.charge_states[0][2] / self.charge_states[1][2]
     band_gap = semiconductor.band_gap(temperature,
                                       symbolic=False,
                                       electron_volts=electron_volts)
     energy_range = np.linspace(-np.float(band_gap),
                                0,
                                num=1001,
                                endpoint=True)
     charge_state_idx = 0
     trap_energy_level = self.energy_level(temperature, semiconductor,
                                           charge_state_idx, electron_volts)
     if fermi_level_from_conduction_band is None:
         fermi_level_from_conduction_band = trap_energy_level
     fermi_level = conduction_band - fermi_level_from_conduction_band
     if self.energy_distribution_function in energy_distribution_functions[
             'Single Level']:
         ax.plot(np.zeros_like(energy_range),
                 energy_range,
                 linewidth=2,
                 color='black',
                 linestyle='-')
         ax_max = ax.get_xlim()[1]
         if trap_concentration > ax_max:
             head_length = 0.03 * trap_concentration
         else:
             head_length = 0.03 * ax_max
         ax.arrow(0,
                  conduction_band - trap_energy_level,
                  trap_concentration - head_length,
                  0,
                  linewidth=2,
                  head_width=head_length,
                  head_length=head_length,
                  fc='black',
                  ec='black')
         f = self.equilibrium_f(temperature, semiconductor,
                                fermi_level_from_conduction_band,
                                electron_volts)
         ax.arrow(0,
                  conduction_band - trap_energy_level,
                  trap_concentration * f,
                  0,
                  linewidth=2,
                  head_width=0,
                  head_length=0,
                  fc='red',
                  ec='red')
     else:
         energy = sym.symbols('E')
         energy_distribution = self.energy_distribution.subs([
             ('Et', trap_energy_level * energy_coefficient),
             ('Ecb', conduction_band * energy_coefficient)
         ])
         energy_distribution = energy_distribution.subs(q, to_numeric(q))
         energy_distribution_function = sym.lambdify(
             energy, energy_distribution, 'numpy')
         fermi_function = 1 / (1 + g_ratio * np.exp(
             (energy_range - fermi_level) / energy_scale))
         energy_states_distribution = energy_distribution_function(
             energy_range * energy_coefficient)
         energy_states_distribution *= trap_concentration * energy_coefficient
         trapped_carriers_distribution = energy_states_distribution * fermi_function
         ax.plot(energy_states_distribution,
                 energy_range,
                 linewidth=2,
                 color='black',
                 linestyle='-')
         # ax.plot(trapped_carriers_distribution, energy_range, linewidth=2, color='red', linestyle='-')
         ax.plot(fermi_function * max(energy_states_distribution),
                 energy_range,
                 linewidth=2,
                 color='black',
                 linestyle='--')
         ax.fill_between(trapped_carriers_distribution,
                         0,
                         energy_range,
                         color='blue',
                         alpha=0.5)
     ax.arrow(0,
              fermi_level,
              ax.get_xlim()[1],
              0,
              linewidth=2,
              head_width=0,
              head_length=0,
              fc='black',
              ec='black')
     ax.set_title('Traps distribution in the Band Gap')
     ax.set_ylabel('Energy, ' + energy_unit)
     ax.set_ylim([-np.float(band_gap), 0])
     x_label = 'Traps distribution, 1/' + energy_unit
     if trap_concentration_units != 1 and trap_concentration_units != '':
         x_label += ' * ' + trap_concentration_units
     ax.set_xlabel(x_label)
     ax.set_xlim([0, max([ax.get_xlim()[1], trap_concentration])])
     if fancy_labels:
         ticks = ax.get_yticks()
         labels = np.array([lbl.get_text() for lbl in ax.get_yticklabels()])
         # print ticks
         # print labels
         if u'Ev' in labels:
             ticks = np.append(ticks, band_gap - trap_energy_level)
             labels = np.append(labels, 'Ec-%2.2g' % trap_energy_level)
         else:
             #ticks = np.array([0, band_gap, band_gap - trap_energy_level])
             ticks = np.array([-band_gap, -trap_energy_level, 0])
             labels = np.array(['Ev', 'Ec-%2.2g' % trap_energy_level, 'Ec'])
         ax.set_yticks(ticks)
         ax.set_yticklabels(labels)
Пример #27
0
 def __str__(self, *args, **kwargs):
     return 'Metal: %s, Workfunction: %2.2f eV (%2.2g J)' % (str(self.label), to_numeric(self.WF / q),
                                                             to_numeric(self.WF))
Пример #28
0
def Reccurent_Poisson_solver(SchDiode,
                             Psi=Psi_zero,
                             Vd_guess=None,
                             Vd_error=1e-6,
                             equilibrium_filling=True,
                             fast_traps=None,
                             t=mp.inf,
                             initial_condition_id=-1,
                             rho_rel_err=1e-3,
                             max_iter=100,
                             save_to_db=True,
                             debug=False):
    recurrent_solver_start_time = time.time()
    Va = SchDiode.Va
    kT_eV = to_numeric(k * SchDiode.T / q)
    if save_to_db:
        measurement_id = add_Electric_Field_Measurement(
            SchDiode, Va, equilibrium_filling, t, initial_condition_id)
        Solution_found, Psi_found, E, z_nodes, rho_rel_err_points, Vd, Vd_err, J, J_err, BI_F, dopants_F, measurement_id = load_diode_state(
            SchDiode, measurement_id, initial_condition_id, debug)
    else:
        Solution_found = False
        measurement_id = -1
    if Solution_found:
        Psi = Psi_found
        Vd_guess = Vd[-1]
        J_tmp = J[-1]
        if max(abs(rho_rel_err_points)) <= rho_rel_err:
            if debug: print '==> Solution satisfy rel_error condition'
            return Psi, E, z_nodes, rho_rel_err_points, Vd[0], Vd_err[0], J[
                0], J_err[0], BI_F, dopants_F, measurement_id
        if debug or 1:
            print '==> Solution found does not satisfy rel_error condition'
            print '==> Recalculating...'
            SchDiode.FieldInversionPoint, PHI_bn, _, PHI_b = SchDiode.get_phi_bn(
                Psi, Vd_guess, SchottkyEffect=False)
    else:
        if debug: print '==> No solutions found'
        if Vd_guess is None:
            Vd_guess = Va
            PHI_b = abs(SchDiode.V_bi(eV=True))
            SchDiode.FieldInversionPoint = 0
        else:
            SchDiode.FieldInversionPoint, PHI_bn, _, PHI_b = SchDiode.get_phi_bn(
                Psi, Vd_guess, SchottkyEffect=False)
            if Va < PHI_b:
                Vd_guess = Va
        J_tmp = 0

    converged = False
    current_iter = 0
    Vd_tmp_corr = 0
    Vd_guess_monitor_length = 5
    Vd_guess_monitor = np.arange(Vd_guess_monitor_length, dtype=np.float)
    Vd_guess_monitor_count = 0
    stalled = False
    while not converged and not stalled and current_iter < max_iter:
        if debug: print '\nIteration:', current_iter, '**'
        if debug or 1: print 'Vd_tmp =', Vd_guess
        if debug: print 'PHI_b =', PHI_b
        if Vd_guess >= PHI_b:
            Vd_guess = PHI_b - 1 * kT_eV
        if debug: print 'Vd_tmp =', Vd_guess
        Vd_guess_monitor[Vd_guess_monitor_count] = Vd_guess
        Vd_guess_monitor_count += 1
        if Vd_guess_monitor_count == Vd_guess_monitor_length:
            Vd_guess_monitor_count = 0
        if np.unique(Vd_guess_monitor).size == 1:
            stalled = True
            print 'Solution process stalled on iteration', current_iter, '!!!'
            continue
        nodes_num = int(np.floor(SchDiode.L * 1e6 + 1) * 100)
        nodes, _ = np.linspace(0,
                               SchDiode.L,
                               num=nodes_num + 1,
                               endpoint=True,
                               retstep=True)
        Meshes = Poisson_eq_num_solver_amr(SchDiode,
                                           nodes,
                                           Psi,
                                           Vd_guess,
                                           equilibrium_filling,
                                           fast_traps,
                                           max_iterations=50,
                                           residual_threshold=rho_rel_err,
                                           int_residual_threshold=5e-14,
                                           max_level=5,
                                           mesh_refinement_threshold=1e-19,
                                           debug=debug)
        z_nodes, Psi_points, rho_rel_err_points = Meshes.flatten(debug=False)
        E_points = -np.gradient(Psi_points, z_nodes, edge_order=2)
        Psi = interp_Fn(z_nodes, Psi_points, interp_type='last')
        E = interp_Fn(z_nodes, E_points, interp_type='last')
        SchDiode.FieldInversionPoint, PHI_bn, _, PHI_b = SchDiode.get_phi_bn(
            Psi, Vd_guess, SchottkyEffect=False)
        print '*** !!! Inversion pt (calc) !!!', SchDiode.FieldInversionPoint
        if debug: print 'PHI_b, PHI_bn =', PHI_b, PHI_bn
        Vd_tmp_corr, J_tmp_corr = SchDiode.ThermionicEmissionCurrent(
            Va, PHI_bn, debug=True)
        if debug: print 'V_corr, J =', Vd_tmp_corr, J_tmp_corr
        Vd_err = abs(Vd_guess - Vd_tmp_corr)
        J_err = abs(J_tmp - J_tmp_corr)
        if debug or 1: print 'Vd err =', Vd_err
        if debug or 1: print 'J err =', J_err, '\n'
        if Vd_err < Vd_error:
            converged = True
        else:
            Vd_guess = Vd_tmp_corr
            J_tmp = J_tmp_corr
        current_iter += 1
    Vd = Vd_tmp_corr
    J = J_tmp
    if debug: print 'Calculation converged'
    recurrent_solver_elapsed_time = time.time() - recurrent_solver_start_time
    if debug:
        print 'Total recurrent solver execution time =', recurrent_solver_elapsed_time, 's\n'
    if save_to_db:
        save_diode_state(SchDiode, measurement_id, initial_condition_id,
                         z_nodes, Psi_points, E_points, rho_rel_err_points, Vd,
                         Vd_err, J, J_err, debug)
    BI_F = {}
    for BI in SchDiode.Semiconductor.bonding_interfaces:
        for i, trap in enumerate(BI.dsl_tilt.traps):
            BI_F[BI.label + '_tilt_' + trap[0].name + '_F'] = BI.dsl_tilt_f[i]
        for i, trap in enumerate(BI.dsl_twist.traps):
            BI_F[BI.label + '_twist_' + trap[0].name +
                 '_F'] = BI.dsl_twist_f[i]
    dopants_F = {}
    for dopant in SchDiode.Semiconductor.dopants:
        dopants_F[dopant.name + '_F'] = dopant.F(z_nodes)
    recurrent_solver_elapsed_time = time.time() - recurrent_solver_start_time
    if debug:
        print 'Total recurrent solver execution time =', recurrent_solver_elapsed_time, 's'
    return Psi, E, z_nodes, rho_rel_err_points, Vd, Vd_err, J, J_err, BI_F, dopants_F, measurement_id
Пример #29
0
def BandsBendingDiagram(ax, SchDiode, Psi=Psi_zero, Vd=0, z=0, BI_F={}, dopants_F={}, eV=True,
                        draw_metal=True, label_bands=True, fancy_ticks=True,
                        SchottkyEffect=False, draw_energies=False):
    print 'BB Diagram Start'
    Z, Ec, Ev, Ef, Eg, MirrEnergy, FieldInversionPoint = BandsBending_prepare_data(SchDiode, Psi, Vd, z, eV,
                                                                                   SchottkyEffect, draw_metal)
    bands_style = [2, 'black', '-']
    Ef_style = [1, 'black', ':']
    lbl_style = [12, 'black']
    inv = ax.transData.inverted()
    dxdy = inv.transform(ax.transData.transform((0, 0)) + np.array([bands_style[0], bands_style[0]]))
    metal_size = [0.2, 1]  # um x eV
    BandsBending_draw_metal(ax, SchDiode, metal_size, bands_style, lbl_style, draw_energies, draw_metal)
    arrow_z_pos = np.max(Z) / 2
    annot_fig = annotate_energies(ax, SchDiode, arrow_z_pos, Psi, Vd, SchottkyEffect, metal_size, draw_metal, eV,
                                  draw_energies)
    bands_lines = {}
    if SchDiode.Semiconductor.dop_type == 'n':
        bands_lines['Ec'], = ax.plot(Z * 1e6, Ec - MirrEnergy, linewidth=bands_style[0], color=bands_style[1],
                                     linestyle=bands_style[2])
        bands_lines['Ev'], = ax.plot(Z * 1e6, Ev, linewidth=bands_style[0], color=bands_style[1],
                                     linestyle=bands_style[2])
    else:
        bands_lines['Ec'], = ax.plot(Z * 1e6, Ec, linewidth=bands_style[0], color=bands_style[1],
                                     linestyle=bands_style[2])
        bands_lines['Ev'], = ax.plot(Z * 1e6, Ev + MirrEnergy, linewidth=bands_style[0], color=bands_style[1],
                                     linestyle=bands_style[2])
    Ef_idx = np.where(Z > FieldInversionPoint)
    # Ef_idx = np.where(Z >= 0)
    bands_lines['Ef'], = ax.plot(Z[Ef_idx] * 1e6, Ef[Ef_idx], linewidth=Ef_style[0], color=Ef_style[1],
                                 linestyle=Ef_style[2])
    bands_labels = {}
    if label_bands:
        trans = transforms.blended_transform_factory(ax.transAxes, ax.transData)
        if SchDiode.Semiconductor.dop_type == 'n':
            lbl_pos_y = [Ec[-1] + 3 * dxdy[1], Ev[-1] - 3 * dxdy[1], Ef[-1] - 3 * dxdy[1]]
            lbl_algn = ['bottom', 'top', 'top']
        else:
            lbl_pos_y = [Ec[-1] + 3 * dxdy[1], Ev[-1] - 3 * dxdy[1], Ef[-1] + 3 * dxdy[1]]
            lbl_algn = ['bottom', 'top', 'bottom']
        bands_labels['Ec'] = ax.text(0.9, lbl_pos_y[0], 'Ec', verticalalignment=lbl_algn[0], horizontalalignment='left',
                                     transform=trans, color=lbl_style[1], fontsize=lbl_style[0])
        bands_labels['Ev'] = ax.text(0.9, lbl_pos_y[1], 'Ev', verticalalignment=lbl_algn[1], horizontalalignment='left',
                                     transform=trans, color=lbl_style[1], fontsize=lbl_style[0])
        bands_labels['Ef'] = ax.text(0.9, lbl_pos_y[2], 'Ef', verticalalignment=lbl_algn[2], horizontalalignment='left',
                                     transform=trans, color=lbl_style[1], fontsize=lbl_style[0])
    dopant_lines = {}
    for dopant in SchDiode.Semiconductor.dopants:
        dopant_style = [1, 'black', '--']
        threshold_dopant = 0.05 if eV else to_numeric(q) * 0.05
        level = dopant.energy_level(SchDiode.T, SchDiode.Semiconductor, electron_volts=eV)
        if level > threshold_dopant and level < (Eg - threshold_dopant):
            if draw_metal:
                dopant_lines[dopant.name], = ax.plot(Z * 1e6, np.append(Ec[1], Ec[1:]) - level,
                                                     linewidth=dopant_style[0], color=dopant_style[1],
                                                     linestyle=dopant_style[2])
            else:
                dopant_lines[dopant.name], = ax.plot(Z * 1e6, Ec - level, linewidth=dopant_style[0],
                                                     color=dopant_style[1], linestyle=dopant_style[2])
    BI_levels = {}
    for j, BI in enumerate(SchDiode.Semiconductor.bonding_interfaces):
        for i, trap in enumerate(BI.dsl_tilt.traps):
            charge_state_idx = 0
            level = trap[0].energy_level(SchDiode.T, SchDiode.Semiconductor, charge_state_idx, electron_volts=eV)
            print 'Et =', level, 'eV'
            F_i = BI_F[BI.label + '_tilt_' + trap[0].name + '_F']
            gray = (1 - np.float(F_i), 1 - np.float(F_i), 1 - np.float(F_i))
            BI_levels[str(j) + '-tilt-' + trap[0].name] = ax.scatter(BI.depth * 1e6,
                                                                     SchDiode.Ec(Psi, Vd, BI.depth, eV=True) - level, s=40,
                                                                     edgecolors='black', c=gray[0], vmin=0, vmax=1,
                                                                     cmap=cm.gray)
        for i, trap in enumerate(BI.dsl_twist.traps):
            charge_state_idx = 0
            level = trap[0].energy_level(SchDiode.T, SchDiode.Semiconductor, charge_state_idx, electron_volts=eV)
            print 'Et =', level, 'eV'
            F_i = BI_F[BI.label + '_twist_' + trap[0].name + '_F']
            gray = (1 - np.float(F_i), 1 - np.float(F_i), 1 - np.float(F_i))
            BI_levels[str(j) + '-twist-' + trap[0].name] = ax.scatter(BI.depth * 1e6,
                                                                      SchDiode.Ec(Psi, Vd, BI.depth, eV=True) - level,
                                                                      s=40, edgecolors='black', c=gray[0], vmin=0,
                                                                      vmax=1, cmap=cm.gray)

    BandsBending_prepare_axes(ax, eV)
    Z_coordinate_ticks(ax, SchDiode, fancy_ticks)
    print 'BB Diagram Stop'
    return bands_lines, bands_labels, annot_fig, dopant_lines, BI_levels
Пример #30
0
    def update_frame(i):
        try:
            V_label.set_text('Va = %2.2f V, Vd = %2.2f V' % (Va[i], Vd[i]))
            T_label.set_text('T = %3.2f K' % T[i])
            Z_coordinate_ticks(ax, SchDiode, fancy_ticks)
            Z, Ec, Ev, Ef, Eg, MirrEnergy, FieldInversionPoint = BandsBending_prepare_data(SchDiode, Psi[i], Vd[i], z,
                                                                                           eV, SchottkyEffect,
                                                                                           draw_metal)
            if SchDiode.Semiconductor.dop_type == 'n':
                bands_lines['Ec'].set_data(Z * 1e6, (Ec - MirrEnergy))
                bands_lines['Ev'].set_data(Z * 1e6, Ev)
            else:
                bands_lines['Ec'].set_data(Z * 1e6, Ec)
                bands_lines['Ev'].set_data(Z * 1e6, Ev + MirrEnergy)
            Ef_idx = np.where(Z > FieldInversionPoint)
            # Ef_idx = np.where(Z >= 0)
            bands_lines['Ef'].set_data(Z[Ef_idx] * 1e6, Ef[Ef_idx])
            if label_bands:
                inv = ax.transData.inverted()
                dxdy = inv.transform(ax.transData.transform((0, 0)) + np.array([2, 2]))
                # trans = transforms.blended_transform_factory(ax.transAxes, ax.transData)
                if SchDiode.Semiconductor.dop_type == 'n':
                    lbl_pos_y = [Ec[-1] + 3 * dxdy[1], Ev[-1] - 3 * dxdy[1], Ef[-1] - 3 * dxdy[1]]
                else:
                    lbl_pos_y = [Ec[-1] + 3 * dxdy[1], Ev[-1] - 3 * dxdy[1], Ef[-1] + 3 * dxdy[1]]
                bands_labels['Ec'].set_y(lbl_pos_y[0])
                bands_labels['Ev'].set_y(lbl_pos_y[1])
                bands_labels['Ef'].set_y(lbl_pos_y[2])
                bands_labels['Ec'].set_text('Ec')
                bands_labels['Ev'].set_text('Ev')
                bands_labels['Ef'].set_text('Ef')

            for dopant in SchDiode.Semiconductor.dopants:
                threshold_dopant = 0.05 if eV else to_numeric(q) * 0.05
                level = dopant.energy_level(SchDiode.T, SchDiode.Semiconductor, electron_volts=eV)
                if level > threshold_dopant and level < (Eg - threshold_dopant):
                    if draw_metal:
                        dopant_lines[dopant.name].set_data(Z * 1e6, np.append(Ec[1], Ec[1:]) - level)
                    else:
                        dopant_lines[dopant.name].set_data(Z * 1e6, Ec - level)

            for j, BI in enumerate(SchDiode.Semiconductor.bonding_interfaces):
                for l, trap in enumerate(BI.dsl_tilt.traps):
                    charge_state_idx = 0
                    level = trap[0].energy_level(SchDiode.T, SchDiode.Semiconductor, charge_state_idx, electron_volts=eV)
                    # print 'Et =', level, 'eV'
                    F_l = BI_F[i][BI.label + '_tilt_' + trap[0].name + '_F']
                    gray = np.array([1 - np.float(F_l), 1 - np.float(F_l), 1 - np.float(F_l), 1.0])
                    # print gray
                    # gray = np.array([(1 - np.float(BI.dsl_tilt_f[l]), 1 - np.float(BI.dsl_tilt_f[l]), 1 - np.float(BI.dsl_tilt_f[l]))])
                    BI_levels[str(j) + '-tilt-' + trap[0].name].set_offsets(
                        np.array([BI.depth * 1e6, SchDiode.Ec(Psi[i], Vd[i], BI.depth, eV=True) - level]))
                    BI_levels[str(j) + '-tilt-' + trap[0].name].set_array(gray)
                for l, trap in enumerate(BI.dsl_twist.traps):
                    charge_state_idx = 0
                    level = trap[0].energy_level(SchDiode.T, SchDiode.Semiconductor, charge_state_idx, electron_volts=eV)
                    # print 'Et =', level, 'eV'
                    F_l = BI_F[i][BI.label + '_twist_' + trap[0].name + '_F']
                    gray = (1 - np.float(F_l), 1 - np.float(F_l), 1 - np.float(F_l))
                    # gray = np.array([(1 - np.float(BI.dsl_twist_f[l]), 1 - np.float(BI.dsl_twist_f[l]), 1 - np.float(BI.dsl_twist_f[l]))])
                    BI_levels[str(j) + '-twist-' + trap[0].name].set_offsets(
                        np.array([BI.depth * 1e6, SchDiode.Ec(Psi[i], Vd[i], BI.depth, eV=True) - level]))
                    # BI_levels[str(j)+'-twist-'+trap[0].name].set_array(gray)


        except Exception as e:
            print '!!! ====>', e
            pass
        fig_list = bands_lines.values()
        fig_list.extend(dopant_lines.values())
        fig_list.extend(bands_labels.values())
        fig_list.extend([V_label, T_label])
        fig_list.extend(BI_levels.values())
        return fig_list
Пример #31
0
    def f_to_equilibrium_fermi_level(self,
                                     temperature,
                                     semiconductor,
                                     f,
                                     electron_volts=False,
                                     use_mpmath=False,
                                     parallel=False,
                                     debug=False):
        """
        Calculates equilibrium Fermi level position for given occupation of the Trap F
        :param temperature: Temperature, K
        :param semiconductor: Semiconductor object
        :param f: Trap occupation from 0.0 to 1.0
        :param electron_volts: if True assume all energy values to be in eV
        :param use_mpmath: if True integration is done using mpmath.quad function instead of numpy.trapz (default)
        :param debug: if True prints out some debug information
        :return: Fermi level as distance from Conduction band to Fermi level

        In calculation we use eV since solver has much better stability in smaller order numbers
        """
        energy_unit = 'eV'
        energy_coefficient = to_numeric(q)
        trap_energy_level = self.energy_level(temperature,
                                              semiconductor,
                                              charge_state_idx=0,
                                              electron_volts=True)
        f_grid, = np.meshgrid(f)

        #print f_grid
        def equation(fermi_level_from_conduction_band, f):
            test_f = self.equilibrium_f(temperature,
                                        semiconductor,
                                        fermi_level_from_conduction_band,
                                        electron_volts=True,
                                        use_mpmath=use_mpmath,
                                        debug=debug)
            residual = f - test_f
            if debug:
                print 'Fermi level type:', type(
                    fermi_level_from_conduction_band)
                print 'Test F =', test_f
                print 'F residual =', residual
            return residual

        def solver(args):
            equation, lower_boundary, upper_boundary, initial_guess, f, use_mpmath = args
            if not use_mpmath:
                warnings.filterwarnings('ignore')
                solution = root(equation, initial_guess, args=f, method='hybr')
                solution = solution.x[0]
                warnings.resetwarnings()
            else:
                equation_wrap = partial(equation, f=f)
                try:
                    solution = mp.findroot(equation_wrap,
                                           (lower_boundary, upper_boundary),
                                           maxsteps=1000,
                                           solver='anderson',
                                           tol=5e-16)
                except ValueError as err:
                    print err
                    print 'Lowering tolerance to 5e-6'
                    solution = mp.findroot(equation_wrap,
                                           (lower_boundary, upper_boundary),
                                           maxsteps=1000,
                                           solver='anderson',
                                           tol=5e-6)
                solution = np.float(solution)
            return solution

        fermi_level_lower_boundary = abs(
            to_numeric(trap_energy_level -
                       2 * self.energy_spread / energy_coefficient))
        fermi_level_upper_boundary = abs(
            to_numeric(trap_energy_level +
                       2 * self.energy_spread / energy_coefficient))
        if debug:
            print 'Fermi level lower boundary = %2.2g ' % fermi_level_lower_boundary + energy_unit
            print 'Fermi level upper boundary = %2.2g ' % fermi_level_upper_boundary + energy_unit
        args = np.empty((len(f_grid), 6), dtype=object)
        args[:, 0] = equation
        args[:, 1] = fermi_level_lower_boundary
        args[:, 2] = fermi_level_upper_boundary
        args[:,
             3] = (fermi_level_lower_boundary + fermi_level_upper_boundary) / 2
        args[:, 4] = f_grid
        args[:, 5] = use_mpmath
        if parallel:
            try:
                from pathos.pools import ProcessPool as Pool
                pool = Pool()
                solutions = np.array(pool.map(solver, args))
            except ImportError:
                print 'Parallel calculation needs pathos! Using standard map() instead.'
                solutions = np.array(map(solver, args))
        else:
            solutions = np.array(map(solver, args))
        if not electron_volts:
            solutions *= energy_coefficient
        return solutions
Пример #32
0
    def f_to_equilibrium_fermi_level(self, temperature, semiconductor, f, electron_volts=False,
                                     use_mpmath=False, parallel=False, debug=False):
        """
        Calculates equilibrium Fermi level position for given occupation of the Trap F
        :param temperature: Temperature, K
        :param semiconductor: Semiconductor object
        :param f: Trap occupation from 0.0 to 1.0
        :param electron_volts: if True assume all energy values to be in eV
        :param use_mpmath: if True integration is done using mpmath.quad function instead of numpy.trapz (default)
        :param debug: if True prints out some debug information
        :return: Fermi level as distance from Conduction band to Fermi level

        In calculation we use eV since solver has much better stability in smaller order numbers
        """
        energy_unit = 'eV'
        energy_coefficient = to_numeric(q)
        trap_energy_level = self.energy_level(temperature, semiconductor, charge_state_idx=0,
                                              electron_volts=True)
        f_grid, = np.meshgrid(f)
        #print f_grid
        def equation(fermi_level_from_conduction_band, f):
            test_f = self.equilibrium_f(temperature, semiconductor, fermi_level_from_conduction_band,
                                        electron_volts=True, use_mpmath=use_mpmath, debug=debug)
            residual = f - test_f
            if debug:
                print 'Fermi level type:', type(fermi_level_from_conduction_band)
                print 'Test F =', test_f
                print 'F residual =', residual
            return residual

        def solver(args):
            equation, lower_boundary, upper_boundary, initial_guess, f, use_mpmath = args
            if not use_mpmath:
                warnings.filterwarnings('ignore')
                solution = root(equation, initial_guess, args=f, method='hybr')
                solution = solution.x[0]
                warnings.resetwarnings()
            else:
                equation_wrap = partial(equation, f=f)
                try:
                    solution = mp.findroot(equation_wrap, (lower_boundary, upper_boundary),
                                           maxsteps=1000, solver='anderson', tol=5e-16)
                except ValueError as err:
                    print err
                    print 'Lowering tolerance to 5e-6'
                    solution = mp.findroot(equation_wrap, (lower_boundary, upper_boundary),
                                           maxsteps=1000, solver='anderson', tol=5e-6)
                solution = np.float(solution)
            return solution

        fermi_level_lower_boundary = abs(to_numeric(trap_energy_level - 2 * self.energy_spread / energy_coefficient))
        fermi_level_upper_boundary = abs(to_numeric(trap_energy_level + 2 * self.energy_spread / energy_coefficient))
        if debug:
            print 'Fermi level lower boundary = %2.2g ' % fermi_level_lower_boundary + energy_unit
            print 'Fermi level upper boundary = %2.2g ' % fermi_level_upper_boundary + energy_unit
        args = np.empty((len(f_grid), 6), dtype=object)
        args[:, 0] = equation
        args[:, 1] = fermi_level_lower_boundary
        args[:, 2] = fermi_level_upper_boundary
        args[:, 3] = (fermi_level_lower_boundary + fermi_level_upper_boundary) / 2
        args[:, 4] = f_grid
        args[:, 5] = use_mpmath
        if parallel:
            try:
                from pathos.pools import ProcessPool as Pool
                pool = Pool()
                solutions = np.array(pool.map(solver, args))
            except ImportError:
                print 'Parallel calculation needs pathos! Using standard map() instead.'
                solutions = np.array(map(solver, args))
        else:
            solutions = np.array(map(solver, args))
        if not electron_volts:
            solutions *= energy_coefficient
        return solutions
Пример #33
0
def traps_kinetics(schottky_diode, initial_condition_id, delta_t_min, delta_t_max, t_stop, fast_traps=None,
                   rho_rel_err=1e-1, df_threshold=1e-3, debug=False, debug_plot=False):
    t_points = []
    potential_t = []
    field_d = []
    z_t = []
    diode_voltage_drop_t = []
    current_density_t = []
    bonding_interfaces_f_t = []
    dopants_f_t = []

    ic_found, potential, z_nodes, _, _, _, _, _, _, _, _, _ = Poisson.load_diode_state(schottky_diode,
                                                                                       initial_condition_id,
                                                                                       debug=True)
    if not ic_found:
        raise(Exception('Initial condition not found. Please check everything'))

    if debug_plot:
        plt.ion()
        axes = Visual.prepare_debug_axes(['Potential', 'Dopants', 'Localized traps'])
        potential_lines = Visual.create_lines(axes['Potential'], ['Start potential', 'Current potential'])
        dopants_lines_names = [dopant.name + '_F' for dopant in schottky_diode.Semiconductor.dopants]
        localized_traps_lines_names = []
        for bi in schottky_diode.Semiconductor.bonding_interfaces:
            localized_traps_lines_names += [bi.label + '_tilt_' + trap[0].name + '_F' for trap in bi.dsl_tilt.traps]
            localized_traps_lines_names += [bi.label + '_twist_' + trap[0].name + '_F' for trap in bi.dsl_twist.traps]
        dopants_lines = Visual.create_lines(axes['Dopants'], dopants_lines_names)
        localized_traps_lines = Visual.create_lines(axes['Localized traps'], localized_traps_lines_names)

    t = 0
    last_state_id = initial_condition_id
    if fast_traps is None:
        fast_traps = []
    while t <= t_stop:
        print 't =', t
        potential, field, z_nodes, _, \
            diode_voltage_drop, _, current_density, _, \
            bonding_interface_f, dopants_f, \
            last_state_id = Poisson.Reccurent_Poisson_solver(schottky_diode, potential,
                                                             equilibrium_filling=False,
                                                             fast_traps=fast_traps,
                                                             t=t, initial_condition_id=last_state_id,
                                                             rho_rel_err=rho_rel_err, max_iter=100, debug=False)
        t_points.append(t)
        potential_t.append(potential)
        field_d.append(field)
        z_t.append(z_nodes)
        diode_voltage_drop_t.append(diode_voltage_drop)
        current_density_t.append(current_density)
        bonding_interfaces_f_t.append(bonding_interface_f)
        dopants_f_t.append(dopants_f)

        fermi_level = schottky_diode.EfEc(potential, z_nodes, eV=False)

        if debug_plot:
            if t == 0:
                potential_lines['Start potential'].set_data(z_nodes * 1e6, -potential(z_nodes))
            potential_lines['Current potential'].set_data(z_nodes * 1e6, -potential(z_nodes))

            for dopant_line_name in dopants_lines_names:
                dopants_lines[dopant_line_name].set_data(z_nodes * 1e6, dopants_f[dopant_line_name])

            for localized_trap_line_name in localized_traps_lines_names:
                localized_trap_f_t = []
                for bonding_interfaces_f_t_i in bonding_interfaces_f_t:
                    localized_trap_f_t.append(bonding_interfaces_f_t_i[localized_trap_line_name])
                localized_traps_lines[localized_trap_line_name].set_data(t_points, localized_trap_f_t)
            Visual.autoscale_axes(axes)
            plt.draw()

        dt = delta_t_max if t_stop - t > delta_t_max else t_stop - t
        if dt == 0:
            break
        if debug:
            print '\n\nT = %2.2f K, t = %2.2g s, dt = %2.2g s' % (schottky_diode.T, t, dt)

        n, p = schottky_diode.n_carriers_theory(potential, z_nodes)
        if schottky_diode.Semiconductor.dop_type == 'n':
            p = np.zeros_like(p)
        elif schottky_diode.Semiconductor.dop_type == 'p':
            n = np.zeros_like(n)

        #fast_traps = []

        dopants_skip_list = []
        df_dopants = {}
        for dopant in schottky_diode.Semiconductor.dopants:
            dopant_key = dopant.name + '_F'
            if dopant.name in fast_traps:
                if debug:
                    print 'This dopant is in a fast-traps list, skipping.'
                # dopant_f = dopant.equilibrium_f(schottky_diode.T, schottky_diode.Semiconductor, fermi_level,
                #                                 electron_volts=False, debug=False)
                # dopant.set_F_interp(z_nodes, dopant_f)
                # dopant.set_dF_interp(z_nodes, np.zeros_like(dopant_f))
                # fast_traps.append(dopant.name)
                dopants_skip_list.append(dopant_key)
                continue
            poole_frenkel_e = 1.0
            poole_frenkel_h = 1.0
            barrier_lowering_e = np.zeros_like(n, dtype=np.float)
            barrier_lowering_h = np.zeros_like(p, dtype=np.float)
            df_dt, tau = dopant.df_dt(schottky_diode.T, schottky_diode.Semiconductor, dopants_f[dopant_key], n, p,
                                      poole_frenkel_e=poole_frenkel_e,
                                      poole_frenkel_h=poole_frenkel_h,
                                      barrier_lowering_e=barrier_lowering_e,
                                      barrier_lowering_h=barrier_lowering_h,
                                      use_mpmath=False, debug=False)
            df_dopants[dopant_key] = df_dt
            max_dt = df_threshold / np.max(np.abs(df_dt))
            if debug:
                print '\nDopant:', dopant.name
                print 'time constant %2.2g s' % tau
                print 'Max dF:', np.max(np.abs(df_dt)), 'th:', df_threshold
                print 'Max dt:', max_dt, 'dt:', dt
            if dt > max_dt > delta_t_min:
                if debug:
                    print 'Setting dt to', max_dt
                dt = max_dt
            elif max_dt < delta_t_min:
                if debug:
                    print 'Traps are too fast. Setting dopant occupation to equilibrium value'
                dopant_f = dopant.equilibrium_f(schottky_diode.T, schottky_diode.Semiconductor, fermi_level,
                                                electron_volts=False, debug=False)
                dopant.set_F_interp(z_nodes, dopant_f)
                dopant.set_dF_interp(z_nodes, np.zeros_like(dopant_f))
                # fast_traps.append(dopant.name)
                dopants_skip_list.append(dopant_key)

        localized_traps_skip_list = []
        df_bonding_interfaces = {}
        for bi in schottky_diode.Semiconductor.bonding_interfaces:
            fermi_level_at_bi = schottky_diode.EfEc(potential, bi.depth, eV=False)
            electric_field_at_bi = field(bi.depth)
            n_bi, p_bi = schottky_diode.n_carriers_theory(potential, bi.depth)
            if schottky_diode.Semiconductor.dop_type == 'n':
                p_bi = 0.0
            elif schottky_diode.Semiconductor.dop_type == 'p':
                n_bi = 0.0
            if debug:
                print '\nBI:', bi.label
                print 'n_BI = %2.4g' % n_bi
                print 'p_BI = %2.4g' % p_bi

            bi_tilt_f = []
            for trap_idx, trap in enumerate(bi.dsl_tilt.traps):
                localized_trap_key = bi.label + '_tilt_' + trap[0].name + '_F'
                print 'BI Density of Charge = %2.2g cm-2' % (bi.density_of_charge / 1e4)
                print 'TILT F = %2.2f' % bi.dsl_tilt_f[trap_idx]
                dsl_charge_density = bi.dsl_tilt_f[trap_idx] * trap[1]
                print 'TILT Density of Charge = %2.2g cm-1' % (dsl_charge_density / 1e2)
                print 'EXT FIELD = %2.2g V*cm' % (electric_field_at_bi / 1e2)
                electric_field_at_bi_r = abs(electric_field_at_bi)
                electric_field_at_bi_theta = 0 if electric_field_at_bi >= 0 else np.pi
                electric_field_at_bi_3d = (electric_field_at_bi_r, electric_field_at_bi_theta, 0.0)
                trap[0].trap_potential.get_potential_by_name('Charged Dislocation').set_linear_charge_density(dsl_charge_density)
                trap[0].trap_potential.get_potential_by_name('External Field').external_field = electric_field_at_bi_3d

                kT = to_numeric(k * schottky_diode.T / q)
                theta = np.linspace(0, np.pi, num=100, endpoint=True)
                barrier_lowering = np.array([trap[0].trap_potential.barrier_lowering(theta_i) for theta_i in theta])
                poole_frenkel = 0.5 * np.trapz(np.sin(theta) * np.exp(abs(barrier_lowering[:, 0]) / kT), theta)
                poole_frenkel_e = 1.0
                poole_frenkel_h = 1.0
                if np.sum(barrier_lowering[:, 0]) < 0:
                    poole_frenkel_e = poole_frenkel
                    print 'emission boost e: %2.4g' % poole_frenkel
                elif np.sum(barrier_lowering[:, 0]) > 0:
                    poole_frenkel_h = poole_frenkel
                    print 'emission boost h: %2.4g' % poole_frenkel
                barrier_lowering_e = 0.0
                barrier_lowering_h = 0.0
                df_dt, tau = trap[0].df_dt(schottky_diode.T, schottky_diode.Semiconductor,
                                           bonding_interface_f[localized_trap_key], n_bi, p_bi,
                                           poole_frenkel_e=poole_frenkel_e,
                                           poole_frenkel_h=poole_frenkel_h,
                                           barrier_lowering_e=barrier_lowering_e,
                                           barrier_lowering_h=barrier_lowering_h,
                                           use_mpmath=False, debug=False)
                df_bonding_interfaces[localized_trap_key] = df_dt
                try:
                    max_dt = df_threshold / abs(df_dt)
                except ZeroDivisionError:
                    max_dt = 1e250
                if debug:
                    print '\nTrap:', trap[0].name
                    print 'time constant %2.2g s' % tau
                    print 'dF: %2.4g, th: %2.2f'% (df_dt, df_threshold)
                    print 'Max dt:', max_dt, 'dt:', dt
                if dt > max_dt > delta_t_min:
                    if debug:
                        print 'Setting dt to', max_dt
                    dt = max_dt
                elif max_dt < delta_t_min:
                    if dt > 10 * max_dt:
                        if debug:
                            print 'Trap is too fast. Setting trap occupation to equilibrium value'
                        bonding_interface_f[localized_trap_key] = trap[0].equilibrium_f(schottky_diode.T,
                                                                                        schottky_diode.Semiconductor,
                                                                                        fermi_level_at_bi,
                                                                                        electron_volts=False, debug=False)
                        localized_traps_skip_list.append(localized_trap_key)
                    else:
                        if debug:
                            print 'Setting dt to', max_dt
                        dt = max_dt
                        # fast_traps.append(bi.label + '_tilt_' + trap[0].name)
                bi_tilt_f.append(bonding_interface_f[localized_trap_key])
            bi_twist_f = []
            for trap in bi.dsl_twist.traps:
                localized_trap_key = bi.label + '_twist_' + trap[0].name + '_F'
                trap[0].trap_potential.get_potential_by_name('External Field').external_field = electric_field_at_bi
                print 'EXT FIELD = %2.2g V*cm' % (electric_field_at_bi / 1e2)
                print trap[0].trap_potential.barrier_lowering()
                barrier_lowering_e = 0.0
                barrier_lowering_h = 0.0
                df_dt, tau = trap[0].df_dt(schottky_diode.T, schottky_diode.Semiconductor,
                                           bonding_interface_f[localized_trap_key], n_bi, p_bi,
                                           barrier_lowering_e=barrier_lowering_e,
                                           barrier_lowering_h=barrier_lowering_h,
                                           use_mpmath=False, debug=False)
                df_bonding_interfaces[localized_trap_key] = df_dt
                try:
                    max_dt = df_threshold / abs(df_dt)
                except ZeroDivisionError:
                    max_dt = 1e250
                if debug:
                    print '\nTrap:', trap[0].name
                    print 'time constant %2.2g s' % tau
                    print 'dF: %2.4g, th: %2.2f'% (df_dt, df_threshold)
                    print 'Max dt:', max_dt, 'dt:', dt
                if dt > max_dt > delta_t_min:
                    if debug:
                        print 'Setting dt to', max_dt
                    dt = max_dt
                elif max_dt < delta_t_min:
                    if dt > 10 * max_dt:
                        if debug:
                            print 'Trap is too fast. Setting trap occupation to equilibrium value'
                        bonding_interface_f[localized_trap_key] = trap[0].equilibrium_f(schottky_diode.T,
                                                                                        schottky_diode.Semiconductor,
                                                                                        fermi_level,
                                                                                        electron_volts=False, debug=False)
                        localized_traps_skip_list.append(localized_trap_key)
                    else:
                        if debug:
                            print 'Setting dt to', max_dt
                        dt = max_dt
                    # fast_traps.append(bi.label + '_twist_' + trap[0].name)
                bi_twist_f.append(bonding_interface_f[localized_trap_key])
            bi.set_traps_f(np.array(bi_tilt_f), np.array(bi_twist_f))
            bi.set_traps_df(np.zeros(len(bi_tilt_f)), np.zeros(len(bi_twist_f)))
        dt = np.float(dt)

        for dopant in schottky_diode.Semiconductor.dopants:
            dopant_key = dopant.name + '_F'
            if dopant_key in dopants_skip_list:
                if debug:
                    print 'Dopant', dopant_key, 'does not need an update'
                continue
            dopants_f[dopant_key] += df_dopants[dopant_key] * dt
            dopants_f[dopant_key][np.where(dopants_f[dopant_key] > 1.0)] = 1.0
            dopants_f[dopant_key][np.where(dopants_f[dopant_key] < 0.0)] = 0.0
            dopant.set_F_interp(z_nodes, dopants_f[dopant_key])
            dopant.set_dF_interp(z_nodes, np.zeros_like(dopants_f[dopant_key]))

        for bi in schottky_diode.Semiconductor.bonding_interfaces:
            bi_tilt_f = []
            for trap in bi.dsl_tilt.traps:
                localized_trap_key = bi.label + '_tilt_' + trap[0].name + '_F'
                if localized_trap_key not in localized_traps_skip_list:
                    bonding_interface_f[localized_trap_key] += df_bonding_interfaces[localized_trap_key] * dt
                    if bonding_interface_f[localized_trap_key] > 1:
                        bonding_interface_f[localized_trap_key] = 1
                    elif bonding_interface_f[localized_trap_key] < 0:
                        bonding_interface_f[localized_trap_key] = 0
                if debug:
                    print 'F:', bonding_interface_f[localized_trap_key]
                bi_tilt_f.append(bonding_interface_f[localized_trap_key])
            bi_twist_f = []
            for trap in bi.dsl_twist.traps:
                localized_trap_key = bi.label + '_twist_' + trap[0].name + '_F'
                if localized_trap_key not in localized_traps_skip_list:
                    bonding_interface_f[localized_trap_key] += df_bonding_interfaces[localized_trap_key] * dt
                    if bonding_interface_f[localized_trap_key] > 1:
                        bonding_interface_f[localized_trap_key] = 1
                    elif bonding_interface_f[localized_trap_key] < 0:
                        bonding_interface_f[localized_trap_key] = 0
                if debug:
                    print 'F:', bonding_interface_f[localized_trap_key]
                bi_twist_f.append(bonding_interface_f[localized_trap_key])
            bi.set_traps_f(np.array(bi_tilt_f), np.array(bi_twist_f))
            bi.set_traps_df(np.zeros(len(bi_tilt_f)), np.zeros(len(bi_twist_f)))
        t += dt

    if debug_plot:
        plt.ioff()

    return t_points, potential_t, field_d, z_t, diode_voltage_drop_t, current_density_t, \
        bonding_interfaces_f_t, dopants_f_t, last_state_id
Пример #34
0
def traps_kinetics(schottky_diode,
                   initial_condition_id,
                   delta_t_min,
                   delta_t_max,
                   t_stop,
                   fast_traps=None,
                   rho_rel_err=1e-1,
                   df_threshold=1e-3,
                   min_t_points=50,
                   dopants_deriv_threshold=5.0e-5,
                   dopants_deriv_window=9,
                   dopants_deriv_z_limit=None,
                   save_to_db=True,
                   potential=None,
                   debug=False,
                   debug_plot=False):
    if dopants_deriv_z_limit is None:
        dopants_deriv_z_limit = 1e8
    z_limit_f = 1e8
    dopants_f_total = {}
    t_points = []
    potential_t = []
    field_d = []
    z_t = []
    diode_voltage_drop_t = []
    current_density_t = []
    bonding_interfaces_f_t = []
    dopants_f_t = []
    n_t = []
    p_t = []

    if save_to_db:
        ic_found, potential, _, z_nodes, _, _, _, _, _, _, _, _ = Poisson.load_diode_state(
            schottky_diode, initial_condition_id, debug=True)
        if not ic_found:
            raise (Exception(
                'Initial condition not found. Please check everything'))

    if debug_plot:
        plt.ion()
        axes = Visual.prepare_debug_axes(
            ['Potential', 'Dopants', 'Localized traps'])
        potential_lines = Visual.create_lines(
            axes['Potential'], ['Start potential', 'Current potential'])
        dopants_lines_names = [
            dopant.name + '_F'
            for dopant in schottky_diode.Semiconductor.dopants
        ]
        localized_traps_lines_names = []
        for bi in schottky_diode.Semiconductor.bonding_interfaces:
            localized_traps_lines_names += [
                bi.label + '_tilt_' + trap[0].name + '_F'
                for trap in bi.dsl_tilt.traps
            ]
            localized_traps_lines_names += [
                bi.label + '_twist_' + trap[0].name + '_F'
                for trap in bi.dsl_twist.traps
            ]
        dopants_lines = Visual.create_lines(axes['Dopants'],
                                            dopants_lines_names)
        localized_traps_lines = Visual.create_lines(
            axes['Localized traps'], localized_traps_lines_names)

    t = 0
    last_state_id = initial_condition_id
    if fast_traps is None:
        fast_traps = []
    slow_traps = {}
    while t <= t_stop:
        print '\n\nt =', t
        potential, field, z_nodes, _, \
            diode_voltage_drop, _, current_density, _, \
            bonding_interface_f, dopants_f, \
            last_state_id = Poisson.Reccurent_Poisson_solver(schottky_diode, potential,
                                                             equilibrium_filling=False,
                                                             fast_traps=fast_traps,
                                                             t=t, initial_condition_id=last_state_id,
                                                             rho_rel_err=rho_rel_err, max_iter=100,
                                                             save_to_db=save_to_db, debug=False)

        t_points.append(t)
        potential_t.append(potential)
        field_d.append(field)
        z_t.append(z_nodes)
        diode_voltage_drop_t.append(diode_voltage_drop)
        current_density_t.append(current_density)
        bonding_interfaces_f_t.append(bonding_interface_f.copy())
        dopants_f_t.append(dopants_f.copy())

        fermi_level = schottky_diode.EfEc(potential, z_nodes, eV=False)

        n, p = schottky_diode.n_carriers_theory(potential, z_nodes)
        if schottky_diode.Semiconductor.dop_type == 'n':
            p = np.zeros_like(p)
        elif schottky_diode.Semiconductor.dop_type == 'p':
            n = np.zeros_like(n)
        n_t.append(n.copy())
        p_t.append(p.copy())

        if debug_plot:
            if t == 0:
                potential_lines['Start potential'].set_data(
                    z_nodes * 1e6, -potential(z_nodes))
            potential_lines['Current potential'].set_data(
                z_nodes * 1e6, -potential(z_nodes))

            for dopant_line_name in dopants_lines_names:
                dopants_lines[dopant_line_name].set_data(
                    z_nodes * 1e6, dopants_f[dopant_line_name])

            for localized_trap_line_name in localized_traps_lines_names:
                localized_trap_f_t = []
                for bonding_interfaces_f_t_i in bonding_interfaces_f_t:
                    localized_trap_f_t.append(
                        bonding_interfaces_f_t_i[localized_trap_line_name])
                localized_traps_lines[localized_trap_line_name].set_data(
                    t_points, localized_trap_f_t)
            Visual.autoscale_axes(axes)
            plt.draw()

        dt = delta_t_max if t_stop - t > delta_t_max else t_stop - t
        if dt == 0:
            for dopant in schottky_diode.Semiconductor.dopants:
                dopant_key = dopant.name + '_F'
                dopants_f_t[-1].update(
                    {dopant_key + '_pf': np.ones_like(z_nodes)})
                dopants_f_t[-1].update(
                    {dopant_key + '_df': np.zeros_like(z_nodes)})
            break
        if debug:
            print '\n\nT = %2.2f K, t = %2.2g s, dt = %2.2g s' % (
                schottky_diode.T, t, dt)

        #fast_traps = []
        dopants_skip_list = []
        df_dopants = {}
        for dopant in schottky_diode.Semiconductor.dopants:
            dopant_key = dopant.name + '_F'
            dopants_deriv_z_limit_idx = np.where(
                z_nodes < dopants_deriv_z_limit)
            if t == 0:
                dopants_f_total[dopant_key] = [
                    np.trapz(
                        dopants_f_t[0][dopant_key][dopants_deriv_z_limit_idx],
                        x=z_nodes[dopants_deriv_z_limit_idx])
                ]
            else:
                dopants_f_total[dopant_key].append(
                    np.trapz(
                        dopants_f_t[-1][dopant_key][dopants_deriv_z_limit_idx],
                        x=z_nodes[dopants_deriv_z_limit_idx]))
            print 'Total Donor charge %2.2g' % dopants_f_total[dopant_key][-1]
            if t > 0:
                loc_der = (dopants_f_total[dopant_key][-1] - dopants_f_total[dopant_key][-2]) \
                          / (t_points[-1] - t_points[-2]) / dopants_f_total[dopant_key][0]
                print 'local derivative %2.2g%%' % (loc_der * 100)
            if dopant.name in fast_traps:
                if debug:
                    print '\nDopant:', dopant.name
                    print 'This dopant is in a fast-traps list, skipping.'
                dopants_skip_list.append(dopant_key)
                continue
            if dopant.name in slow_traps.keys():
                if debug:
                    print '\nDopant:', dopant.name
                    print 'This dopant is in a slow-traps list, skipping.'
                df_dt = np.zeros_like(z_nodes)
                for wp in range(dopants_deriv_window):
                    df_dt[dopants_deriv_z_limit_idx] += dopants_f_t[-wp - 2][
                        dopant_key + '_df'][dopants_deriv_z_limit_idx]
                df_dt /= dopants_deriv_window
                dopants_f_t[-1].update({dopant_key + '_df': df_dt})
                df_dopants[dopant_key] = df_dt
                #dopants_skip_list.append(dopant_key)
                continue
            poole_frenkel_e = np.ones_like(z_nodes, dtype=np.float)
            poole_frenkel_h = np.ones_like(z_nodes, dtype=np.float)
            barrier_lowering_e = np.zeros_like(n, dtype=np.float)
            barrier_lowering_h = np.zeros_like(p, dtype=np.float)
            field_z = field(z_nodes)

            if dopant.trap_potential.get_potential_by_name(
                    'Charged Dislocation') is not None:
                kT = to_numeric(k * schottky_diode.T / q)
                theta_points = 90
                theta = np.linspace(0, np.pi, num=theta_points, endpoint=True)
                #theta = np.linspace(0, np.pi / 2, num=theta_points, endpoint=True)
                barrier_lowering = np.zeros((theta_points, len(z_nodes)),
                                            dtype=np.float)
                max_N_l = dopant.trap_potential.get_potential_by_name('Charged Dislocation')\
                        .max_linear_charge_density
                dsl_charge_density = max_N_l * dopant.F(z_nodes)
                for z_num, local_electric_field in enumerate(field_z):
                    #print z_num, 'of', len(z_nodes)
                    #dsl_charge_density = max_N_l * dopant.F(z_nodes[z_num])
                    local_electric_field_r = abs(local_electric_field)
                    local_electric_field_theta = 0 if local_electric_field >= 0 else np.pi
                    local_electric_field_3d = (local_electric_field_r,
                                               local_electric_field_theta, 0.0)
                    dopant.trap_potential.get_potential_by_name('Charged Dislocation')\
                        .set_linear_charge_density(dsl_charge_density[z_num])
                    dopant.trap_potential.get_potential_by_name('External Field')\
                        .external_field = local_electric_field_3d

                    loc_f = local_electric_field_r
                    loc_a = dopant.trap_potential.get_potential_by_name(
                        'Charged Dislocation').a
                    loc_b = -dopant.trap_potential.get_potential_by_name(
                        'Deformation').a
                    r0 = np.zeros_like(theta)
                    if loc_f < 1.0e-5:
                        if z_nodes[z_num] < z_limit_f:
                            z_limit_f = z_nodes[z_num]
                        #print 'here', loc_f, z_nodes[z_num]*1e6
                        try:
                            r0[:] = loc_b / loc_a
                        except FloatingPointError:
                            pass
                        #print len(r0), r0
                    else:
                        determinant = loc_a**2 + 4 * loc_b * loc_f * np.cos(
                            theta)
                        idx = np.where(determinant >= 0)
                        sqrt = np.sqrt(loc_a**2 +
                                       4 * loc_b * loc_f * np.cos(theta[idx]))
                        sol1 = (-loc_a - sqrt) / (2 * loc_f *
                                                  np.cos(theta[idx]))
                        sol2 = (-loc_a + sqrt) / (2 * loc_f *
                                                  np.cos(theta[idx]))
                        sol1[np.where(sol1 < 0.0)] = 0.0
                        #sol2[np.where(sol2 < 0.0)] = 0.0
                        sol = sol1.copy()
                        #sol2_selection = np.where(sol2 > 0)
                        sol2_selection = np.where((sol2 > 0) & (sol2 < sol1))
                        sol[sol2_selection] = sol2[sol2_selection]
                        sol2_selection = np.where((sol2 > 0) & (sol1 <= 0))
                        sol[sol2_selection] = sol2[sol2_selection]
                        r0[idx] = sol
                        #idx = np.where(r0 < loc_b / loc_a / 3)
                        #r0[idx] = loc_b / loc_a
                        zero_theta_idx = np.where(
                            abs(loc_f * np.cos(theta)) < 1.0e-5)
                        try:
                            r0[zero_theta_idx] = loc_b / loc_a
                        except FloatingPointError:
                            r0[zero_theta_idx] = 0.0
                    non_zero_r_idx = np.where(r0 > 0.0)
                    bl_grid = dopant.trap_potential.potential(
                        r0[non_zero_r_idx], theta[non_zero_r_idx], 0)
                    #np.save('./bl_grid_'+str(z_nodes[z_num]*1e6)+'_'+str(t), bl_grid)
                    #print np.rad2deg(theta[non_zero_r_idx])
                    #print bl_grid[0,:,0].shape, theta.shape
                    #print bl_grid[0,:,0]
                    bl_flat = np.zeros_like(theta)
                    try:
                        bl_flat[non_zero_r_idx] = bl_grid[0, :, 0]
                    except IndexError:
                        pass
                    #print kT
                    #print bl_flat
                    #barrier_lowering[:,z_num] = np.array([dopant.trap_potential.barrier_lowering(theta_i)[0] for theta_i in theta])
                    barrier_lowering[:, z_num] = bl_flat
                    #print barrier_lowering[:, z_num]
                    #print bl_flat - barrier_lowering[:, z_num]
                    #poole_frenkel = 0.5 * np.trapz(np.sin(theta) * np.exp(abs(barrier_lowering[:, 0]) / kT), theta)
                #poole_frenkel = 0.5 * np.trapz(np.exp(abs(barrier_lowering) / kT), theta, axis=0)
                #poole_frenkel = 0.5 + np.trapz(np.exp(abs(barrier_lowering) / kT), theta, axis=0) / np.pi
                poole_frenkel = np.trapz(
                    np.exp(abs(barrier_lowering) / kT), theta, axis=0) / np.pi
                #print poole_frenkel
                if np.sum(barrier_lowering[:, 0]) < 0:
                    poole_frenkel_e = poole_frenkel
                    #print 'emission boost e:', poole_frenkel
                elif np.sum(barrier_lowering[:, 0]) > 0:
                    poole_frenkel_h = poole_frenkel
                    #print 'emission boost h:', poole_frenkel

            try:
                dopants_f_t[-1].update({dopant_key + '_pf': poole_frenkel})
            except:
                dopants_f_t[-1].update(
                    {dopant_key + '_pf': np.ones_like(z_nodes)})

            df_dt, tau = dopant.df_dt(schottky_diode.T,
                                      schottky_diode.Semiconductor,
                                      dopants_f[dopant_key],
                                      n,
                                      p,
                                      poole_frenkel_e=poole_frenkel_e,
                                      poole_frenkel_h=poole_frenkel_h,
                                      barrier_lowering_e=barrier_lowering_e,
                                      barrier_lowering_h=barrier_lowering_h,
                                      use_mpmath=False,
                                      debug=False)
            z_limit_f_idx = np.where(z_nodes < z_limit_f)
            z_limit_f_idx0 = np.where(z_t[0] < z_limit_f)
            dopants_f_t[-1].update({dopant_key + '_df': df_dt})
            df_dopants[dopant_key] = df_dt
            #print df_dt
            df_total = np.sum(df_dt[z_limit_f_idx]) / np.sum(
                dopants_f_t[0][dopant_key][z_limit_f_idx0])
            max_dt_local = df_threshold / np.max(np.abs(df_dt))
            max_dt_total = df_threshold / np.max(np.abs(df_total))
            max_dt = min(max_dt_local, max_dt_total)
            if debug:
                print '\nDopant:', dopant.name
                print 'Z limit of %2.2g m: left %d points of %d' % (
                    z_limit_f, len(z_nodes[z_limit_f_idx]), len(z_nodes))
                print 'Min time constant %2.2g s' % tau
                print 'Max dF local:', np.max(
                    np.abs(df_dt)), 'th:', df_threshold
                print 'Max dF total:', np.max(
                    np.abs(df_total)), 'th:', df_threshold
                print 'Max dt:', max_dt, 'dt:', dt
            if len(t_points) > dopants_deriv_window:
                deriv = np.array(dopants_f_total[dopant_key][-dopants_deriv_window + 1:]) \
                        - np.array(dopants_f_total[dopant_key][-dopants_deriv_window:-1])
                deriv /= dopants_f_total[dopant_key][0]
                deriv /= np.array(
                    t_points[-dopants_deriv_window + 1:]) - np.array(
                        t_points[-dopants_deriv_window:-1])
                deriv = np.average(deriv)
                if debug:
                    print 'Dopants derivative: %2.2g%%' % (deriv * 100)
                    print 'Derivative threshold: %2.2g%%' % (
                        dopants_deriv_threshold * 100)
            else:
                deriv = 1e8
            if abs(deriv) < dopants_deriv_threshold and len(
                    t_points) >= min_t_points:
                if debug:
                    print 'Traps are all set. Adding dopant to slow traps.'
                slow_traps[dopant.name] = deriv
            if dt > max_dt > delta_t_min:
                if debug:
                    print 'Setting dt to', max_dt
                dt = max_dt
            elif max_dt < delta_t_min:
                if debug:
                    print 'Traps are too fast. Setting dopant occupation to equilibrium value'
                dopant_f = dopant.equilibrium_f(schottky_diode.T,
                                                schottky_diode.Semiconductor,
                                                fermi_level,
                                                electron_volts=False,
                                                debug=False)
                dopant.set_F_interp(z_nodes, dopant_f)
                dopant.set_dF_interp(z_nodes, np.zeros_like(dopant_f))
                fast_traps.append(dopant.name)
                dopants_skip_list.append(dopant_key)

        localized_traps_skip_list = []
        df_bonding_interfaces = {}
        for bi in schottky_diode.Semiconductor.bonding_interfaces:
            fermi_level_at_bi = schottky_diode.EfEc(potential,
                                                    bi.depth,
                                                    eV=False)
            electric_field_at_bi = field(bi.depth)
            n_bi, p_bi = schottky_diode.n_carriers_theory(potential, bi.depth)
            if schottky_diode.Semiconductor.dop_type == 'n':
                p_bi = 0.0
            elif schottky_diode.Semiconductor.dop_type == 'p':
                n_bi = 0.0
            if debug:
                print '\nBI:', bi.label
                print 'n_BI = %2.4g' % n_bi
                print 'p_BI = %2.4g' % p_bi

            bi_tilt_f = []
            for trap_idx, trap in enumerate(bi.dsl_tilt.traps):
                localized_trap_key = bi.label + '_tilt_' + trap[0].name + '_F'
                print 'BI Density of Charge = %2.2g cm-2' % (
                    bi.density_of_charge / 1e4)
                print 'TILT F = %2.2f' % bi.dsl_tilt_f[trap_idx]
                dsl_charge_density = bi.dsl_tilt_f[trap_idx] * trap[1]
                print 'TILT Density of Charge = %2.2g cm-1' % (
                    dsl_charge_density / 1e2)
                print 'EXT FIELD = %2.2g V*cm' % (electric_field_at_bi / 1e2)
                electric_field_at_bi_r = abs(electric_field_at_bi)
                electric_field_at_bi_theta = 0 if electric_field_at_bi >= 0 else np.pi
                electric_field_at_bi_3d = (electric_field_at_bi_r,
                                           electric_field_at_bi_theta, 0.0)
                trap[0].trap_potential.get_potential_by_name(
                    'Charged Dislocation').set_linear_charge_density(
                        dsl_charge_density)
                trap[0].trap_potential.get_potential_by_name(
                    'External Field').external_field = electric_field_at_bi_3d

                kT = to_numeric(k * schottky_diode.T / q)
                theta = np.linspace(0, np.pi, num=100, endpoint=True)
                barrier_lowering = np.array([
                    trap[0].trap_potential.barrier_lowering(theta_i)
                    for theta_i in theta
                ])
                poole_frenkel = 0.5 * np.trapz(
                    np.sin(theta) * np.exp(abs(barrier_lowering[:, 0]) / kT),
                    theta)
                poole_frenkel_e = 1.0
                poole_frenkel_h = 1.0
                if np.sum(barrier_lowering[:, 0]) < 0:
                    poole_frenkel_e = poole_frenkel
                    print 'emission boost e: %2.4g' % poole_frenkel
                elif np.sum(barrier_lowering[:, 0]) > 0:
                    poole_frenkel_h = poole_frenkel
                    print 'emission boost h: %2.4g' % poole_frenkel
                barrier_lowering_e = 0.0
                barrier_lowering_h = 0.0
                df_dt, tau = trap[0].df_dt(
                    schottky_diode.T,
                    schottky_diode.Semiconductor,
                    bonding_interface_f[localized_trap_key],
                    n_bi,
                    p_bi,
                    poole_frenkel_e=poole_frenkel_e,
                    poole_frenkel_h=poole_frenkel_h,
                    barrier_lowering_e=barrier_lowering_e,
                    barrier_lowering_h=barrier_lowering_h,
                    use_mpmath=False,
                    debug=False)
                df_bonding_interfaces[localized_trap_key] = df_dt
                try:
                    max_dt = df_threshold / abs(df_dt)
                except ZeroDivisionError:
                    max_dt = 1e250
                if debug:
                    print '\nTrap:', trap[0].name
                    print 'time constant %2.2g s' % tau
                    print 'dF: %2.4g, th: %2.2f' % (df_dt, df_threshold)
                    print 'Max dt:', max_dt, 'dt:', dt
                if dt > max_dt > delta_t_min:
                    if debug:
                        print 'Setting dt to', max_dt
                    dt = max_dt
                elif max_dt < delta_t_min:
                    if dt > 10 * max_dt:
                        if debug:
                            print 'Trap is too fast. Setting trap occupation to equilibrium value'
                        bonding_interface_f[localized_trap_key] = trap[
                            0].equilibrium_f(schottky_diode.T,
                                             schottky_diode.Semiconductor,
                                             fermi_level_at_bi,
                                             electron_volts=False,
                                             debug=False)
                        localized_traps_skip_list.append(localized_trap_key)
                    else:
                        if debug:
                            print 'Setting dt to', max_dt
                        dt = max_dt
                        # fast_traps.append(bi.label + '_tilt_' + trap[0].name)
                bi_tilt_f.append(bonding_interface_f[localized_trap_key])
            bi_twist_f = []
            for trap in bi.dsl_twist.traps:
                localized_trap_key = bi.label + '_twist_' + trap[0].name + '_F'
                trap[0].trap_potential.get_potential_by_name(
                    'External Field').external_field = electric_field_at_bi
                print 'EXT FIELD = %2.2g V*cm' % (electric_field_at_bi / 1e2)
                print trap[0].trap_potential.barrier_lowering()
                barrier_lowering_e = 0.0
                barrier_lowering_h = 0.0
                df_dt, tau = trap[0].df_dt(
                    schottky_diode.T,
                    schottky_diode.Semiconductor,
                    bonding_interface_f[localized_trap_key],
                    n_bi,
                    p_bi,
                    barrier_lowering_e=barrier_lowering_e,
                    barrier_lowering_h=barrier_lowering_h,
                    use_mpmath=False,
                    debug=False)
                df_bonding_interfaces[localized_trap_key] = df_dt
                try:
                    max_dt = df_threshold / abs(df_dt)
                except ZeroDivisionError:
                    max_dt = 1e250
                if debug:
                    print '\nTrap:', trap[0].name
                    print 'time constant %2.2g s' % tau
                    print 'dF: %2.4g, th: %2.2f' % (df_dt, df_threshold)
                    print 'Max dt:', max_dt, 'dt:', dt
                if dt > max_dt > delta_t_min:
                    if debug:
                        print 'Setting dt to', max_dt
                    dt = max_dt
                elif max_dt < delta_t_min:
                    if dt > 10 * max_dt:
                        if debug:
                            print 'Trap is too fast. Setting trap occupation to equilibrium value'
                        bonding_interface_f[localized_trap_key] = trap[
                            0].equilibrium_f(schottky_diode.T,
                                             schottky_diode.Semiconductor,
                                             fermi_level,
                                             electron_volts=False,
                                             debug=False)
                        localized_traps_skip_list.append(localized_trap_key)
                    else:
                        if debug:
                            print 'Setting dt to', max_dt
                        dt = max_dt
                    # fast_traps.append(bi.label + '_twist_' + trap[0].name)
                bi_twist_f.append(bonding_interface_f[localized_trap_key])
            bi.set_traps_f(np.array(bi_tilt_f), np.array(bi_twist_f))
            bi.set_traps_df(np.zeros(len(bi_tilt_f)),
                            np.zeros(len(bi_twist_f)))
        dt = np.float(dt)

        for dopant in schottky_diode.Semiconductor.dopants:
            dopant_key = dopant.name + '_F'
            if dopant_key in dopants_skip_list:
                if debug:
                    print '\nDopant', dopant.name, 'does not need an update'
                continue
            dopants_f_corr = dopants_f[dopant_key] + df_dopants[dopant_key] * dt
            dopants_f_corr[np.where(dopants_f_corr > 1.0)] = 1.0
            dopants_f_corr[np.where(dopants_f_corr < 0.0)] = 0.0
            dopants_f_corr[np.where(
                z_nodes >= z_limit_f)] = dopants_f_corr[z_limit_f_idx][-1]
            dopant.set_F_interp(z_nodes, dopants_f_corr)
            dopant.set_dF_interp(z_nodes, np.zeros_like(dopants_f_corr))

        for bi in schottky_diode.Semiconductor.bonding_interfaces:
            bi_tilt_f = []
            for trap in bi.dsl_tilt.traps:
                localized_trap_key = bi.label + '_tilt_' + trap[0].name + '_F'
                if localized_trap_key not in localized_traps_skip_list:
                    bonding_interface_f[
                        localized_trap_key] += df_bonding_interfaces[
                            localized_trap_key] * dt
                    if bonding_interface_f[localized_trap_key] > 1:
                        bonding_interface_f[localized_trap_key] = 1
                    elif bonding_interface_f[localized_trap_key] < 0:
                        bonding_interface_f[localized_trap_key] = 0
                if debug:
                    print 'F:', bonding_interface_f[localized_trap_key]
                bi_tilt_f.append(bonding_interface_f[localized_trap_key])
            bi_twist_f = []
            for trap in bi.dsl_twist.traps:
                localized_trap_key = bi.label + '_twist_' + trap[0].name + '_F'
                if localized_trap_key not in localized_traps_skip_list:
                    bonding_interface_f[
                        localized_trap_key] += df_bonding_interfaces[
                            localized_trap_key] * dt
                    if bonding_interface_f[localized_trap_key] > 1:
                        bonding_interface_f[localized_trap_key] = 1
                    elif bonding_interface_f[localized_trap_key] < 0:
                        bonding_interface_f[localized_trap_key] = 0
                if debug:
                    print 'F:', bonding_interface_f[localized_trap_key]
                bi_twist_f.append(bonding_interface_f[localized_trap_key])
            bi.set_traps_f(np.array(bi_tilt_f), np.array(bi_twist_f))
            bi.set_traps_df(np.zeros(len(bi_tilt_f)),
                            np.zeros(len(bi_twist_f)))
        t += dt

    if debug_plot:
        plt.ioff()

    return t_points, potential_t, field_d, z_t, diode_voltage_drop_t, current_density_t, \
        bonding_interfaces_f_t, dopants_f_t, n_t, p_t, last_state_id
Пример #35
0
def BandsBendingDiagram(ax, SchDiode, Psi=Psi_zero, Vd=0, z=0, BI_F={}, dopants_F={}, eV=True,
                        draw_metal=True, label_bands=True, fancy_ticks=True,
                        SchottkyEffect=False, draw_energies=False):
    print 'BB Diagram Start'
    Z, Ec, Ev, Ef, Eg, MirrEnergy, FieldInversionPoint = BandsBending_prepare_data(SchDiode, Psi, Vd, z, eV,
                                                                                   SchottkyEffect, draw_metal)
    bands_style = [2, 'black', '-']
    Ef_style = [1, 'black', ':']
    lbl_style = [12, 'black']
    inv = ax.transData.inverted()
    dxdy = inv.transform(ax.transData.transform((0, 0)) + np.array([bands_style[0], bands_style[0]]))
    metal_size = [0.2, 1]  # um x eV
    BandsBending_draw_metal(ax, SchDiode, metal_size, bands_style, lbl_style, draw_energies, draw_metal)
    arrow_z_pos = np.max(Z) / 2
    annot_fig = annotate_energies(ax, SchDiode, arrow_z_pos, Psi, Vd, SchottkyEffect, metal_size, draw_metal, eV,
                                  draw_energies)
    bands_lines = {}
    if SchDiode.Semiconductor.dop_type == 'n':
        bands_lines['Ec'], = ax.plot(Z * 1e6, Ec - MirrEnergy, linewidth=bands_style[0], color=bands_style[1],
                                     linestyle=bands_style[2])
        bands_lines['Ev'], = ax.plot(Z * 1e6, Ev, linewidth=bands_style[0], color=bands_style[1],
                                     linestyle=bands_style[2])
    else:
        bands_lines['Ec'], = ax.plot(Z * 1e6, Ec, linewidth=bands_style[0], color=bands_style[1],
                                     linestyle=bands_style[2])
        bands_lines['Ev'], = ax.plot(Z * 1e6, Ev + MirrEnergy, linewidth=bands_style[0], color=bands_style[1],
                                     linestyle=bands_style[2])
    Ef_idx = np.where(Z > FieldInversionPoint)
    # Ef_idx = np.where(Z >= 0)
    bands_lines['Ef'], = ax.plot(Z[Ef_idx] * 1e6, Ef[Ef_idx], linewidth=Ef_style[0], color=Ef_style[1],
                                 linestyle=Ef_style[2])
    bands_labels = {}
    if label_bands:
        trans = transforms.blended_transform_factory(ax.transAxes, ax.transData)
        if SchDiode.Semiconductor.dop_type == 'n':
            lbl_pos_y = [Ec[-1] + 3 * dxdy[1], Ev[-1] - 3 * dxdy[1], Ef[-1] - 3 * dxdy[1]]
            lbl_algn = ['bottom', 'top', 'top']
        else:
            lbl_pos_y = [Ec[-1] + 3 * dxdy[1], Ev[-1] - 3 * dxdy[1], Ef[-1] + 3 * dxdy[1]]
            lbl_algn = ['bottom', 'top', 'bottom']
        bands_labels['Ec'] = ax.text(0.9, lbl_pos_y[0], 'Ec', verticalalignment=lbl_algn[0], horizontalalignment='left',
                                     transform=trans, color=lbl_style[1], fontsize=lbl_style[0])
        bands_labels['Ev'] = ax.text(0.9, lbl_pos_y[1], 'Ev', verticalalignment=lbl_algn[1], horizontalalignment='left',
                                     transform=trans, color=lbl_style[1], fontsize=lbl_style[0])
        bands_labels['Ef'] = ax.text(0.9, lbl_pos_y[2], 'Ef', verticalalignment=lbl_algn[2], horizontalalignment='left',
                                     transform=trans, color=lbl_style[1], fontsize=lbl_style[0])
    dopant_lines = {}
    for dopant in SchDiode.Semiconductor.dopants:
        dopant_style = [1, 'black', '--']
        threshold_dopant = 0.05 if eV else to_numeric(q) * 0.05
        level = dopant.energy_level(SchDiode.T, SchDiode.Semiconductor, electron_volts=eV)
        if level > threshold_dopant and level < (Eg - threshold_dopant):
            if draw_metal:
                dopant_lines[dopant.name], = ax.plot(Z * 1e6, np.append(Ec[1], Ec[1:]) - level,
                                                     linewidth=dopant_style[0], color=dopant_style[1],
                                                     linestyle=dopant_style[2])
            else:
                dopant_lines[dopant.name], = ax.plot(Z * 1e6, Ec - level, linewidth=dopant_style[0],
                                                     color=dopant_style[1], linestyle=dopant_style[2])
    BI_levels = {}
    for j, BI in enumerate(SchDiode.Semiconductor.bonding_interfaces):
        for i, trap in enumerate(BI.dsl_tilt.traps):
            charge_state_idx = 0
            level = trap[0].energy_level(SchDiode.T, SchDiode.Semiconductor, charge_state_idx, electron_volts=eV)
            print 'Et =', level, 'eV'
            F_i = BI_F[BI.label + '_tilt_' + trap[0].name + '_F']
            gray = (1 - np.float(F_i), 1 - np.float(F_i), 1 - np.float(F_i))
            BI_levels[str(j) + '-tilt-' + trap[0].name] = ax.scatter(BI.depth * 1e6,
                                                                     SchDiode.Ec(Psi, Vd, BI.depth, eV=True) - level, s=40,
                                                                     edgecolors='black', c=gray[0], vmin=0, vmax=1,
                                                                     cmap=cm.gray)
        for i, trap in enumerate(BI.dsl_twist.traps):
            charge_state_idx = 0
            level = trap[0].energy_level(SchDiode.T, SchDiode.Semiconductor, charge_state_idx, electron_volts=eV)
            print 'Et =', level, 'eV'
            F_i = BI_F[BI.label + '_twist_' + trap[0].name + '_F']
            gray = (1 - np.float(F_i), 1 - np.float(F_i), 1 - np.float(F_i))
            BI_levels[str(j) + '-twist-' + trap[0].name] = ax.scatter(BI.depth * 1e6,
                                                                      SchDiode.Ec(Psi, Vd, BI.depth, eV=True) - level,
                                                                      s=40, edgecolors='black', c=gray[0], vmin=0,
                                                                      vmax=1, cmap=cm.gray)

    BandsBending_prepare_axes(ax, eV)
    Z_coordinate_ticks(ax, SchDiode, fancy_ticks)
    print 'BB Diagram Stop'
    return bands_lines, bands_labels, annot_fig, dopant_lines, BI_levels
Пример #36
0
    def emission_rate(self, temperature, semiconductor, f, poole_frenkel_e=1.0, poole_frenkel_h=1.0,
                      barrier_lowering_e=None, barrier_lowering_h=None, use_mpmath=False, debug=False):
        """
        Calculate carriers emission rate for bot electrons and holes
        :param temperature: Temperature, K
        :param semiconductor: Semiconductor object
        :param f: Trap occupation from 0.0 to 1.0
        :param poole_frenkel_e: emission rate boost due to Poole-Frenkel effect for electron
        :param poole_frenkel_h: emission rate boost due to Poole-Frenkel effect for electron
        :param barrier_lowering_e: lowering of activation energy for electrons
        :param barrier_lowering_h: lowering of activation energy for holes
        :param use_mpmath: if True integration is done using mpmath.quad function instead of numpy.trapz (default)
        :param debug: if True prints out some debug information
        :return: emission_e, emission_h
        """
        if barrier_lowering_e is None:
            barrier_lowering_e = np.zeros_like(f, dtype=np.float)
        if barrier_lowering_h is None:
            barrier_lowering_h = np.zeros_like(f, dtype=np.float)
        energy_scale = to_numeric(k * temperature)
        conduction_band = 0
        band_gap = semiconductor.band_gap(temperature, symbolic=False, electron_volts=False)
        valence_band = conduction_band - band_gap
        trap_energy_level_e = np.float(self.energy_level(temperature, semiconductor,
                                                         charge_state_idx=1, electron_volts=False))
        trap_energy_level_h = np.float(self.energy_level(temperature, semiconductor,
                                                         charge_state_idx=0, electron_volts=False))
        trap_energy_level_e_positive = conduction_band - trap_energy_level_e
        trap_energy_level_h_positive = conduction_band - trap_energy_level_h

        g_ratio_e = self.charge_states[0][2] / self.charge_states[1][2]
        g_ratio_h = self.charge_states[1][2] / self.charge_states[0][2]

        v_e = np.float(semiconductor.v_T('e', temperature, symbolic=False))
        v_h = np.float(semiconductor.v_T('h', temperature, symbolic=False))
        if debug:
            print '<v_e> =', v_e, 'm/s'
            print '<v_h> =', v_h, 'm/s'
        sigma_n, sigma_p = self.capture_cross_sections(temperature)
        fore_factor_n = np.float(sigma_n * v_e * semiconductor.Nc(temperature, symbolic=False) * g_ratio_e)
        fore_factor_p = np.float(sigma_p * v_h * semiconductor.Nv(temperature, symbolic=False) * g_ratio_h)
        fore_factor_n *= poole_frenkel_e
        fore_factor_p *= poole_frenkel_h
        if debug:
            print 'factor_n =', fore_factor_n
            print 'factor_p =', fore_factor_p

        if self.energy_distribution_function in energy_distribution_functions['Single Level']:
            activation_energy_e = conduction_band - trap_energy_level_e_positive - barrier_lowering_e
            activation_energy_h = trap_energy_level_h_positive - valence_band - barrier_lowering_h
            emission_rate_e = fore_factor_n * np.exp(-activation_energy_e / energy_scale)
            emission_rate_h = fore_factor_p * np.exp(-activation_energy_h / energy_scale)
            emission_time_constant_e = 1 / emission_rate_e
            emission_time_constant_h = 1 / emission_rate_h
            emission_e = emission_rate_e * f
            emission_h = emission_rate_h * (1 - f)
        else:
            quasi_fermi_level = self.f_to_equilibrium_fermi_level(temperature, semiconductor, f,
                                                                  electron_volts=False,
                                                                  use_mpmath=use_mpmath, debug=False)
            quasi_fermi_level = conduction_band - quasi_fermi_level
            if debug:
                print 'Eqf =', quasi_fermi_level / to_numeric(q), 'eV'
            energy = sym.symbols('E')
            energy_distribution_e = self.energy_distribution.subs([('Et', trap_energy_level_e),
                                                                   ('Ecb', conduction_band)])
            energy_distribution_e = energy_distribution_e.subs(q, to_numeric(q))
            energy_distribution_h = self.energy_distribution.subs([('Et', trap_energy_level_h),
                                                                   ('Ecb', conduction_band)])
            energy_distribution_h = energy_distribution_h.subs(q, to_numeric(q))
            energy_range_e = centered_linspace(conduction_band - trap_energy_level_e,
                                               10 * to_numeric(self.energy_spread),
                                               to_numeric(self.energy_spread) / 1000)
            energy_range_h = centered_linspace(conduction_band - trap_energy_level_h,
                                               10 * to_numeric(self.energy_spread),
                                               to_numeric(self.energy_spread) / 1000)
            energy_distribution_function_e = sym.lambdify(energy, energy_distribution_e, 'numpy')
            energy_distribution_function_h = sym.lambdify(energy, energy_distribution_h, 'numpy')
            energy_range_grid_e, barrier_lowering_grid_e = np.meshgrid(energy_range_e, barrier_lowering_e)
            energy_range_grid_h, barrier_lowering_grid_h = np.meshgrid(energy_range_h, barrier_lowering_h)
            exp_term_e = np.exp(-(conduction_band - energy_range_grid_e + barrier_lowering_grid_e) / energy_scale)
            exp_term_h = np.exp(-(energy_range_grid_h - barrier_lowering_grid_h - valence_band) / energy_scale)
            emission_rate_e = energy_distribution_function_e(energy_range_e) * exp_term_e * fore_factor_n
            emission_rate_h = energy_distribution_function_h(energy_range_h) * exp_term_h * fore_factor_p
            emission_rate_e_max = np.max(emission_rate_e, axis=1)
            emission_rate_h_max = np.max(emission_rate_h, axis=1)
            emission_time_constant_e = 1 / emission_rate_e_max
            emission_time_constant_h = 1 / emission_rate_h_max
            if not use_mpmath:
                if debug:
                    print 'Numeric integration (numpy.trapz)'
                energy_range_grid_e, fermi_level_grid_e = np.meshgrid(energy_range_e, quasi_fermi_level)
                energy_range_grid_h, fermi_level_grid_h = np.meshgrid(energy_range_h, quasi_fermi_level)
                fermi_function_e = 1 / (1 + g_ratio_e * np.exp((energy_range_grid_e - fermi_level_grid_e) / energy_scale))
                fermi_function_h = 1 - 1 / (1 + g_ratio_h * np.exp((energy_range_grid_h - fermi_level_grid_h) / energy_scale))
                exp_term_e = np.exp(-(conduction_band - energy_range_grid_e + barrier_lowering_e) / energy_scale)
                exp_term_h = np.exp(-(energy_range_grid_h - barrier_lowering_h - valence_band) / energy_scale)
                emission_rate_e = energy_distribution_function_e(energy_range_grid_e) * exp_term_e
                emission_rate_h = energy_distribution_function_h(energy_range_grid_h) * exp_term_h
                integrand_array_e = emission_rate_e * fermi_function_e
                integrand_array_h = emission_rate_h * fermi_function_h
                emission_e = np.trapz(integrand_array_e, energy_range_grid_e, axis=1) * fore_factor_n
                emission_h = np.trapz(integrand_array_h, energy_range_grid_h, axis=1) * fore_factor_p
            else:
                if debug:
                    print 'Numeric integration (mpmath.quad)'
                exp_term_e = sym.exp(-(conduction_band - energy + barrier_lowering_e) / energy_scale)
                exp_term_h = sym.exp(-(energy - barrier_lowering_h - valence_band) / energy_scale)
                quasi_fermi_level_grid, = np.meshgrid(quasi_fermi_level)
                emission_e = np.zeros_like(quasi_fermi_level_grid)
                emission_h = np.zeros_like(quasi_fermi_level_grid)
                for i, quasi_fermi_level_i in enumerate(quasi_fermi_level_grid):
                    fermi_function_e = fermi(energy, quasi_fermi_level_i, temperature, g_ratio_e)
                    fermi_function_e = fermi_function_e.subs(k, to_numeric(k))
                    fermi_function_h = fermi(quasi_fermi_level_i, energy, temperature, g_ratio_h)
                    fermi_function_h = fermi_function_h.subs(k, to_numeric(k))
                    integrand_e = sym.lambdify(energy, energy_distribution_e * fermi_function_e * exp_term_e)
                    integrand_h = sym.lambdify(energy, energy_distribution_h * fermi_function_h * exp_term_h)
                    emission_integral_e = mp.quad(integrand_e,
                                                  [to_numeric(trap_energy_level_e_positive - 10 * self.energy_spread),
                                                   to_numeric(trap_energy_level_e_positive - 0.5 * self.energy_spread),
                                                   to_numeric(trap_energy_level_e_positive + 0.5 * self.energy_spread),
                                                   to_numeric(trap_energy_level_e_positive + 10 * self.energy_spread)])
                    emission_integral_h = mp.quad(integrand_h,
                                                  [to_numeric(trap_energy_level_h_positive - 10 * self.energy_spread),
                                                   to_numeric(trap_energy_level_h_positive - 0.5 * self.energy_spread),
                                                   to_numeric(trap_energy_level_h_positive + 0.5 * self.energy_spread),
                                                   to_numeric(trap_energy_level_h_positive + 10 * self.energy_spread)])
                    emission_e[i] = np.float(emission_integral_e * fore_factor_n)
                    emission_h[i] = np.float(emission_integral_h * fore_factor_p)
        if isinstance(emission_e, np.ndarray):
            if emission_e.size == 1:
                emission_e = emission_e[0]
                emission_h = emission_h[0]
                emission_time_constant_e = emission_time_constant_e[0]
                emission_time_constant_h = emission_time_constant_h[0]
        if debug: print 'emission_e =', emission_e
        if debug: print 'emission_h =', emission_h
        if debug: print 'emission_tau_e =', emission_time_constant_e
        if debug: print 'emission_tau_h =', emission_time_constant_e
        return emission_e, emission_h, emission_time_constant_e, emission_time_constant_h
Пример #37
0
    def update_frame(i):
        try:
            V_label.set_text('Va = %2.2f V, Vd = %2.2f V' % (Va[i], Vd[i]))
            T_label.set_text('T = %3.2f K' % T[i])
            Z_coordinate_ticks(ax, SchDiode, fancy_ticks)
            Z, Ec, Ev, Ef, Eg, MirrEnergy, FieldInversionPoint = BandsBending_prepare_data(SchDiode, Psi[i], Vd[i], z,
                                                                                           eV, SchottkyEffect,
                                                                                           draw_metal)
            if SchDiode.Semiconductor.dop_type == 'n':
                bands_lines['Ec'].set_data(Z * 1e6, (Ec - MirrEnergy))
                bands_lines['Ev'].set_data(Z * 1e6, Ev)
            else:
                bands_lines['Ec'].set_data(Z * 1e6, Ec)
                bands_lines['Ev'].set_data(Z * 1e6, Ev + MirrEnergy)
            Ef_idx = np.where(Z > FieldInversionPoint)
            # Ef_idx = np.where(Z >= 0)
            bands_lines['Ef'].set_data(Z[Ef_idx] * 1e6, Ef[Ef_idx])
            if label_bands:
                inv = ax.transData.inverted()
                dxdy = inv.transform(ax.transData.transform((0, 0)) + np.array([2, 2]))
                # trans = transforms.blended_transform_factory(ax.transAxes, ax.transData)
                if SchDiode.Semiconductor.dop_type == 'n':
                    lbl_pos_y = [Ec[-1] + 3 * dxdy[1], Ev[-1] - 3 * dxdy[1], Ef[-1] - 3 * dxdy[1]]
                else:
                    lbl_pos_y = [Ec[-1] + 3 * dxdy[1], Ev[-1] - 3 * dxdy[1], Ef[-1] + 3 * dxdy[1]]
                bands_labels['Ec'].set_y(lbl_pos_y[0])
                bands_labels['Ev'].set_y(lbl_pos_y[1])
                bands_labels['Ef'].set_y(lbl_pos_y[2])
                bands_labels['Ec'].set_text('Ec')
                bands_labels['Ev'].set_text('Ev')
                bands_labels['Ef'].set_text('Ef')

            for dopant in SchDiode.Semiconductor.dopants:
                threshold_dopant = 0.05 if eV else to_numeric(q) * 0.05
                level = dopant.energy_level(SchDiode.T, SchDiode.Semiconductor, electron_volts=eV)
                if level > threshold_dopant and level < (Eg - threshold_dopant):
                    if draw_metal:
                        dopant_lines[dopant.name].set_data(Z * 1e6, np.append(Ec[1], Ec[1:]) - level)
                    else:
                        dopant_lines[dopant.name].set_data(Z * 1e6, Ec - level)

            for j, BI in enumerate(SchDiode.Semiconductor.bonding_interfaces):
                for l, trap in enumerate(BI.dsl_tilt.traps):
                    charge_state_idx = 0
                    level = trap[0].energy_level(SchDiode.T, SchDiode.Semiconductor, charge_state_idx, electron_volts=eV)
                    # print 'Et =', level, 'eV'
                    F_l = BI_F[i][BI.label + '_tilt_' + trap[0].name + '_F']
                    gray = np.array([1 - np.float(F_l), 1 - np.float(F_l), 1 - np.float(F_l), 1.0])
                    # print gray
                    # gray = np.array([(1 - np.float(BI.dsl_tilt_f[l]), 1 - np.float(BI.dsl_tilt_f[l]), 1 - np.float(BI.dsl_tilt_f[l]))])
                    BI_levels[str(j) + '-tilt-' + trap[0].name].set_offsets(
                        np.array([BI.depth * 1e6, SchDiode.Ec(Psi[i], Vd[i], BI.depth, eV=True) - level]))
                    BI_levels[str(j) + '-tilt-' + trap[0].name].set_array(gray)
                for l, trap in enumerate(BI.dsl_twist.traps):
                    charge_state_idx = 0
                    level = trap[0].energy_level(SchDiode.T, SchDiode.Semiconductor, charge_state_idx, electron_volts=eV)
                    # print 'Et =', level, 'eV'
                    F_l = BI_F[i][BI.label + '_twist_' + trap[0].name + '_F']
                    gray = (1 - np.float(F_l), 1 - np.float(F_l), 1 - np.float(F_l))
                    # gray = np.array([(1 - np.float(BI.dsl_twist_f[l]), 1 - np.float(BI.dsl_twist_f[l]), 1 - np.float(BI.dsl_twist_f[l]))])
                    BI_levels[str(j) + '-twist-' + trap[0].name].set_offsets(
                        np.array([BI.depth * 1e6, SchDiode.Ec(Psi[i], Vd[i], BI.depth, eV=True) - level]))
                    # BI_levels[str(j)+'-twist-'+trap[0].name].set_array(gray)


        except Exception as e:
            print '!!! ====>', e
            pass
        fig_list = bands_lines.values()
        fig_list.extend(dopant_lines.values())
        fig_list.extend(bands_labels.values())
        fig_list.extend([V_label, T_label])
        fig_list.extend(BI_levels.values())
        return fig_list
Пример #38
0
 def energy_distribution_diagram(self, ax, temperature, semiconductor,
                                 trap_concentration=1, trap_concentration_units='',
                                 fermi_level_from_conduction_band = None,
                                 electron_volts=True, fancy_labels=False):
     if electron_volts:
         energy_coefficient = to_numeric(q)
         energy_unit = 'eV'
     else:
         energy_coefficient = 1
         energy_unit = 'J'
     energy_scale = to_numeric(k * temperature) / energy_coefficient
     conduction_band = 0
     g_ratio = self.charge_states[0][2] / self.charge_states[1][2]
     band_gap = semiconductor.band_gap(temperature, symbolic=False, electron_volts=electron_volts)
     energy_range = np.linspace(-np.float(band_gap), 0, num=1001, endpoint=True)
     charge_state_idx = 0
     trap_energy_level = self.energy_level(temperature, semiconductor, charge_state_idx, electron_volts)
     if fermi_level_from_conduction_band is None:
         fermi_level_from_conduction_band = trap_energy_level
     fermi_level = conduction_band - fermi_level_from_conduction_band
     if self.energy_distribution_function in energy_distribution_functions['Single Level']:
         ax.plot(np.zeros_like(energy_range), energy_range, linewidth=2, color='black', linestyle='-')
         ax_max = ax.get_xlim()[1]
         if trap_concentration > ax_max:
             head_length = 0.03 * trap_concentration
         else:
             head_length = 0.03 * ax_max
         ax.arrow(0, conduction_band - trap_energy_level, trap_concentration - head_length, 0,
                  linewidth=2, head_width=head_length, head_length=head_length, fc='black', ec='black')
         f = self.equilibrium_f(temperature, semiconductor, fermi_level_from_conduction_band, electron_volts)
         ax.arrow(0, conduction_band - trap_energy_level, trap_concentration * f, 0,
                  linewidth=2, head_width=0, head_length=0, fc='red', ec='red')
     else:
         energy = sym.symbols('E')
         energy_distribution = self.energy_distribution.subs([('Et', trap_energy_level * energy_coefficient),
                                                              ('Ecb', conduction_band * energy_coefficient)])
         energy_distribution = energy_distribution.subs(q, to_numeric(q))
         energy_distribution_function = sym.lambdify(energy, energy_distribution, 'numpy')
         fermi_function = 1 / (1 + g_ratio * np.exp((energy_range - fermi_level) / energy_scale))
         energy_states_distribution = energy_distribution_function(energy_range * energy_coefficient)
         energy_states_distribution *= trap_concentration * energy_coefficient
         trapped_carriers_distribution = energy_states_distribution * fermi_function
         ax.plot(energy_states_distribution, energy_range, linewidth=2, color='black', linestyle='-')
         # ax.plot(trapped_carriers_distribution, energy_range, linewidth=2, color='red', linestyle='-')
         ax.plot(fermi_function * max(energy_states_distribution), energy_range,
                 linewidth=2, color='black', linestyle='--')
         ax.fill_between(trapped_carriers_distribution, 0, energy_range, color='blue', alpha=0.5)
     ax.arrow(0, fermi_level, ax.get_xlim()[1], 0,
              linewidth=2, head_width=0, head_length=0, fc='black', ec='black')
     ax.set_title('Traps distribution in the Band Gap')
     ax.set_ylabel('Energy, ' + energy_unit)
     ax.set_ylim([-np.float(band_gap), 0])
     x_label = 'Traps distribution, 1/' + energy_unit
     if trap_concentration_units != 1 and trap_concentration_units != '':
         x_label += ' * ' + trap_concentration_units
     ax.set_xlabel(x_label)
     ax.set_xlim([0, max([ax.get_xlim()[1], trap_concentration])])
     if fancy_labels:
         ticks = ax.get_yticks()
         labels = np.array([lbl.get_text() for lbl in ax.get_yticklabels()])
         # print ticks
         # print labels
         if u'Ev' in labels:
             ticks = np.append(ticks, band_gap - trap_energy_level)
             labels = np.append(labels, 'Ec-%2.2g' % trap_energy_level)
         else:
             #ticks = np.array([0, band_gap, band_gap - trap_energy_level])
             ticks = np.array([-band_gap, -trap_energy_level, 0])
             labels = np.array(['Ev', 'Ec-%2.2g' % trap_energy_level, 'Ec'])
         ax.set_yticks(ticks)
         ax.set_yticklabels(labels)
Пример #39
0
    def d_equilibrium_f_d_fermi_energy(self,
                                       temperature,
                                       semiconductor,
                                       fermi_level_from_conduction_band,
                                       electron_volts=False,
                                       use_mpmath=False,
                                       debug=False):
        """
        Calculates trap equilibrium filling derivative for given Fermi level position
        on small Fermi level change
        :param temperature: Temperature, K
        :param semiconductor: Semiconductor object
        :param fermi_level_from_conduction_band: distance from Conduction band to Fermi level
        :param electron_volts: if True assume all energy values to be in eV
        :param use_mpmath: if True integration is done using mpmath.quad function instead of numpy.trapz (default)
        :param debug: if True prints out some debug information
        :return: equilibrium f between 0 and 1
        """
        if electron_volts:
            energy_coefficient = to_numeric(q)
            energy_unit = 'eV'
        else:
            energy_coefficient = 1
            energy_unit = 'J'
        energy_scale = to_numeric(k * temperature) / energy_coefficient
        conduction_band = 0
        fermi_level = conduction_band - fermi_level_from_conduction_band
        trap_energy_level = self.energy_level(temperature,
                                              semiconductor,
                                              charge_state_idx=0,
                                              electron_volts=electron_volts)
        if debug:
            print 'Et = %2.2g ' % (
                (conduction_band - trap_energy_level)) + energy_unit
            print 'Ef =,', fermi_level, energy_unit
        g_ratio = self.charge_states[0][2] / self.charge_states[1][2]

        if self.energy_distribution_function in energy_distribution_functions[
                'Single Level']:
            fermi_level_grid, = np.meshgrid(fermi_level)
            exp_arg = (np.float(conduction_band - trap_energy_level) -
                       fermi_level_grid) / energy_scale
            exp_term = np.exp(exp_arg)
            exp_term = np.array(map(mp.exp, exp_arg))
            a = g_ratio / (energy_coefficient * energy_scale)
            #print exp_term #/ (1 + g_ratio * exp_term)
            b = exp_term / (1 + g_ratio * exp_term)**2
            d_f = a * b
            # d_f = g_ratio / (energy_coefficient * energy_scale) * exp_term / (1 + g_ratio * exp_term) ** 2
            d_f[np.where(d_f == np.nan)] = 0
        else:
            energy = sym.symbols('E')
            energy_distribution = self.energy_distribution.subs([
                ('Et', trap_energy_level * energy_coefficient),
                ('Ecb', conduction_band * energy_coefficient)
            ])
            energy_distribution = energy_distribution.subs(q, to_numeric(q))
            if not use_mpmath:
                if debug:
                    print 'Numeric integration (numpy.trapz)'
                energy_range = centered_linspace(
                    conduction_band - trap_energy_level,
                    10 * to_numeric(self.energy_spread) / energy_coefficient,
                    to_numeric(self.energy_spread) / energy_coefficient / 1000)
                energy_range_grid, fermi_level_grid = np.meshgrid(
                    energy_range, fermi_level)
                exp_term = np.exp(
                    (energy_range_grid - fermi_level_grid) / energy_scale)
                fore_factor = g_ratio / (energy_coefficient * energy_scale)
                d_fermi_function = fore_factor * exp_term / (
                    1 + g_ratio * exp_term)**2
                d_fermi_function[np.where(d_fermi_function == np.nan)] = 0
                energy_distribution_function = sym.lambdify(
                    energy, energy_distribution, 'numpy')
                integrand_array = energy_distribution_function(
                    energy_range_grid * energy_coefficient) * d_fermi_function
                d_f = np.trapz(integrand_array,
                               energy_range_grid * energy_coefficient,
                               axis=1)
            else:
                if debug:
                    print 'Numeric integration (mpmath.quad)'
                fermi_level_grid, = np.meshgrid(fermi_level)
                d_f = np.zeros_like(fermi_level_grid)
                for i, fermi_level_i in enumerate(fermi_level_grid):
                    d_fermi_function = d_fermi_d_delta_fermi_energy(
                        energy, fermi_level_i * energy_coefficient,
                        temperature, g_ratio)
                    d_fermi_function = d_fermi_function.subs(k, to_numeric(k))
                    integrand = sym.lambdify(
                        energy, energy_distribution * d_fermi_function)
                    d_f[i] = mp.quad(integrand, [
                        to_numeric(energy_coefficient *
                                   (conduction_band - trap_energy_level) -
                                   10 * self.energy_spread),
                        to_numeric(energy_coefficient *
                                   (conduction_band - trap_energy_level) -
                                   0.5 * self.energy_spread),
                        to_numeric(energy_coefficient *
                                   (conduction_band - trap_energy_level) +
                                   0.5 * self.energy_spread),
                        to_numeric(energy_coefficient *
                                   (conduction_band - trap_energy_level) +
                                   10 * self.energy_spread)
                    ])
        if debug:
            print 'dF =', d_f
        if d_f.size == 1:
            d_f = d_f[0]
        return d_f
Пример #40
0
 def EfEc(self, Psi=Psi_zero, z=0, eV=False):
     coeff = 1 if eV else to_numeric(q)
     Psi_nodes = Psi(z)
     xi = np.float(self.Semiconductor.Ech_pot(T=self.T, z=1e3, eV=eV, debug=False))
     return -coeff * Psi_nodes + xi