LETID - Passivated Wafer#

This example shows how to simulate an accelerated test performed on a well-passivated Si wafer, rather than a solar cell. In a well-passivated wafer, carrier injection (\(\Delta n\)) is linearly proportional to carrier lifetime, assuming surface recombination velocity can be approximated to be zero.

LETID and boron-oxygen LID defect transitions are known to accelerate with increased carrier injection, by term \(\Delta n^{x_{ij}}\), where \(x_{ij}\) is different for each transition \(i \rightarrow j\) and is related to the stoichiometric involvement of excess carriers in the defect reaction.

Requirements:

  • pandas, numpy, matplotlib, scipy

Objectives:

  1. Define necessary wafer device parameters

  2. Define necessary degradation parameters: degraded lifetime and defect states

  3. Create timeseries of temperature and injection

  4. Run through timeseries, calculating defect states

  5. Calculate device degradation and plot

# if running on google colab, uncomment the next line and execute this cell to install the dependencies and prevent "ModuleNotFoundError" in later cells:
# !pip install pvdeg==0.3.3
from pvdeg import letid, collection, utilities, DATA_DIR
import pvdeg
import os
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# This information helps with debugging and getting support :)
import sys, platform
print("Working on a ", platform.system(), platform.release())
print("Python version ", sys.version)
print("Pandas version ", pd.__version__)
print("pvdeg version ", pvdeg.__version__)
Working on a  Linux 6.5.0-1025-azure
Python version  3.11.9 (main, Jul 15 2024, 21:50:21) [GCC 11.4.0]
Pandas version  2.2.2
pvdeg version  0.1.dev1+g4f38099

Device parameters#

For modeling a wafer, we don’t need as many device parameters. Wafer thickness, lifetime, and an optical generation profile, which allows us to calculate generation current, which we take to be device current assuming surface recombination is very low.

wafer_thickness = 180 # um
tau = 350 # us, lifetime of the wafer for demonstration purposes

generation_df = pd.read_excel(os.path.join(DATA_DIR, 'PVL_GenProfile.xlsx'), header = 0) # this is an optical generation profile generated by PVLighthouse's OPAL2 default model for 1-sun, normal incident AM1.5 sunlight on a 180-um thick SiNx-coated, pyramid-textured wafer.
generation = generation_df['Generation (cm-3s-1)']
depth = generation_df['Depth (um)']

j_gen = collection.generation_current(generation, depth)

Degradation parameters#

Here we’ll model LETID under 0.5-sun, open-circuit injection. In a passivated wafer, recombination per volume (\(\frac{\Delta n}{\tau}\)) must equal generation per volume (\(\frac{Jq}{W}\)), which gives \(\Delta n = \frac{Jq\tau}{W}\)

The term \(\Delta n^{x_{ij}}\) is calculated for wafers by the function letid.carrier_factor_wafer, which adjusts the rate of the transition \(i \rightarrow j\) for \(\Delta n\).

Since kinetic parameters are taken from literature that may have used different \(\Delta n\), we adjust transitions by the ratio \(\frac{\Delta n}{\Delta n_{lit}}\).

See Repins et al. 2023 for more details.

mechanism_params = utilities.get_kinetics('repins')
injection = 0.5 #

Demonstrate calculation of dn^x_ij#

  1. Calculate \(\Delta n\) of the wafer

from scipy.constants import elementary_charge as q # Elementary charge, C

dn = (((j_gen * 0.001 * 10000 * injection) * (tau * 1e-6))/ (wafer_thickness * 1e-6)/ q) # calculate excess carrier density
dn
2.57051594134322e+21
  1. Calculate \(\Delta n_{lit}\), the excess carrier density from the literature experiments where kinetic parameters were determined. Assuming literature experiments were carried out on cells, this requires using the calc_dn function

from scipy.constants import convert_temperature

transition = 'bc' # for this example we'll look at the B -> C transition

meas_tau = mechanism_params[f"tau_{transition}"]
meas_temp = mechanism_params[f"temperature_{transition}"]
meas_temp = convert_temperature(meas_temp, 'K', 'C')
meas_suns = mechanism_params[f"suns_{transition}"]
meas_jsc = 40
meas_wafer_thickness = mechanism_params[f"thickness_{transition}"]
meas_srv = mechanism_params[f"srv_{transition}"]
meas_structure = mechanism_params[f"structure_{transition}"]

dn_lit = letid.calc_dn(
                    meas_tau,
                    meas_temp,
                    meas_suns,
                    meas_jsc,
                    wafer_thickness=meas_wafer_thickness,
                    s_rear=meas_srv,
                )
dn_lit
7.064647199192719e+20
  1. Return the exponent \(x_{ij}\) of the transition in question

exponent = mechanism_params[f"x_{transition}"]
exponent
1.2
  1. Calculate \((\frac{\Delta n}{\Delta n_{lit}})^{x_{ij}}\). This is the acceleration factor of the transition due to excess carriers.

(dn/dn_lit)**exponent
4.711029107876425
# the "carrier_factor_wafer" function yields the same result

letid.carrier_factor_wafer(tau, transition, injection, j_gen, wafer_thickness, mechanism_params)
4.711029107876425

Degradation parameters#

To model the device’s degradation, we need to define several more important quantities about the degradation the device will experience. These include undegraded and degraded lifetime (in \(\mu s\)), and starting defect state percentages

tau_0 = 350 # us, carrier lifetime in non-degraded states, e.g. LETID/LID states A or C
tau_deg = 41 # us, carrier lifetime in fully-degraded state, e.g. LETID/LID state B
# starting defect state percentages
nA_0 = 100
nB_0 = 0
nC_0 = 0

mechanism_params = utilities.get_kinetics('repins')
print(mechanism_params)
{'mechanism': 'LETID', 'v_ab': 46700000.0, 'v_ba': 4.7e-25, 'v_bc': 19900000.0, 'v_cb': 0.0, 'ea_ab': 0.827, 'ea_ba': -1.15, 'ea_bc': 0.871, 'ea_cb': 0.0, 'suns_ab': 1.0, 'suns_bc': 1.0, 'temperature_ab': 410, 'temperature_bc': 410, 'tau_ab': 75, 'tau_bc': 75, 'x_ab': 1, 'x_ba': 1.7, 'x_bc': 1.2, 'structure_ab': 'cell', 'structure_bc': 'cell', 'thickness_ab': 200, 'thickness_bc': 200, 'srv_ab': 90, 'srv_bc': 90, 'doi': 'doi:10.1557/s43577-022-00438-8', 'comments': ''}

Set up timeseries#

In this example, we are going to model a wafer exposed to 0.5 suns illumination at \(125\degree C\) for 24 hours, and compare to experimental data from Wyller et al. 2021.

temperature = 125 # degrees celsius
suns = 0.5 # "suns" of injection, e.g 1-sun illumination at open circuit would be 1; dark current injection is given as a fraction of Isc, e.g., injecting Isc would be 1. For this example we assume injection is 0.1*Isc.

timesteps = pd.date_range(start = '2022-01-01 00:00:00', end = '2022-01-02 00:00:00', freq = 'S') # three weeks of 1-minute interval timesteps. In general, we should select small timesteps unless we are sure defect reactions are proceeding very slowly
timesteps = pd.DataFrame(timesteps, columns = ['Datetime'])

temps = np.full(len(timesteps), temperature)
injection = np.full(len(timesteps), suns)

timesteps['Temperature'] = temps
timesteps['Injection'] = injection

timesteps[['NA', 'NB', 'NC', 'tau']] = np.nan # create columns for defect state percentages and lifetime, fill with NaNs for now, to fill iteratively below

timesteps.loc[0, ['NA', 'NB', 'NC']] = nA_0, nB_0, nC_0 # assign first timestep defect state percentages
timesteps.loc[0, 'tau'] = letid.tau_now(tau_0, tau_deg, nB_0) # calculate tau for the first timestep
/tmp/ipykernel_2272/530898593.py:4: FutureWarning: 'S' is deprecated and will be removed in a future version, please use 's' instead.
  timesteps = pd.date_range(start = '2022-01-01 00:00:00', end = '2022-01-02 00:00:00', freq = 'S') # three weeks of 1-minute interval timesteps. In general, we should select small timesteps unless we are sure defect reactions are proceeding very slowly
timesteps
Datetime Temperature Injection NA NB NC tau
0 2022-01-01 00:00:00 125 0.5 100.0 0.0 0.0 350.0
1 2022-01-01 00:00:01 125 0.5 NaN NaN NaN NaN
2 2022-01-01 00:00:02 125 0.5 NaN NaN NaN NaN
3 2022-01-01 00:00:03 125 0.5 NaN NaN NaN NaN
4 2022-01-01 00:00:04 125 0.5 NaN NaN NaN NaN
... ... ... ... ... ... ... ...
86396 2022-01-01 23:59:56 125 0.5 NaN NaN NaN NaN
86397 2022-01-01 23:59:57 125 0.5 NaN NaN NaN NaN
86398 2022-01-01 23:59:58 125 0.5 NaN NaN NaN NaN
86399 2022-01-01 23:59:59 125 0.5 NaN NaN NaN NaN
86400 2022-01-02 00:00:00 125 0.5 NaN NaN NaN NaN

86401 rows × 7 columns

Run through timesteps#

Since each timestep depends on the preceding timestep, we need to calculate in a loop. This will take a few minutes depending on the length of the timeseries.

for index, timestep in timesteps.iterrows():

    # first row tau has already been assigned
    if index == 0:
        pass

    # loop through rows, new tau calculated based on previous NB. Reaction proceeds based on new tau.
    else:
        n_A = timesteps.at[index-1, 'NA']
        n_B = timesteps.at[index-1, 'NB']
        n_C = timesteps.at[index-1, 'NC']

        tau = letid.tau_now(tau_0, tau_deg, n_B)
        j_gen = j_gen

        temperature = timesteps.at[index, 'Temperature']
        injection = timesteps.at[index, 'Injection']

        # calculate defect reaction kinetics: reaction constant and carrier concentration factor.
        k_AB = letid.k_ij(mechanism_params['v_ab'], mechanism_params['ea_ab'], temperature)
        k_BA = letid.k_ij(mechanism_params['v_ba'], mechanism_params['ea_ba'], temperature)
        k_BC = letid.k_ij(mechanism_params['v_bc'], mechanism_params['ea_bc'], temperature)
        k_CB = letid.k_ij(mechanism_params['v_cb'], mechanism_params['ea_cb'], temperature)

        x_ab = letid.carrier_factor_wafer(tau, 'ab', injection, j_gen, wafer_thickness, mechanism_params)
        x_ba = letid.carrier_factor_wafer(tau, 'ba', injection, j_gen, wafer_thickness, mechanism_params)
        x_bc = letid.carrier_factor_wafer(tau, 'bc', injection, j_gen, wafer_thickness, mechanism_params)
        # x_cb there is no known excess carrier acceleration factor for the c->b transition

        # calculate the instantaneous change in NA, NB, and NC
        dN_Adt = (k_BA * n_B * x_ba) - (k_AB * n_A * x_ab)
        dN_Bdt = (k_AB * n_A * x_ab) + (k_CB * n_C) - ((k_BA * x_ba + k_BC * x_bc) * n_B)
        dN_Cdt = (k_BC * n_B * x_bc) - (k_CB * n_C)

        t_step = (timesteps.at[index, 'Datetime'] - timesteps.at[index-1,'Datetime']).total_seconds()

        # assign new defect state percentages
        timesteps.at[index, 'NA'] = n_A + dN_Adt*t_step
        timesteps.at[index, 'NB'] = n_B + dN_Bdt*t_step
        timesteps.at[index, 'NC'] = n_C + dN_Cdt*t_step

Finish calculating degraded device parameters.#

Now that we have calculated defect states, we can calculate all the quantities that depend on defect states. Since this is a wafer, device parameters like Jsc, Voc, FF, etc., aren’t really appropriate

For wafers, results are often presented in terms of normalized defect density (NDD), where

\(NDD(t) = \frac{1}{\tau (t)} - \frac{1}{\tau _0}\)

timesteps['tau'] = letid.tau_now(tau_0, tau_deg, timesteps['NB'])

timesteps['NDD'] = letid.calc_ndd(timesteps['tau'].iloc[0], timesteps['tau'])

timesteps['time (s)'] = (timesteps['Datetime'] - timesteps.iloc[0]['Datetime']).dt.total_seconds() # create a column for seconds elapsed
timesteps
Datetime Temperature Injection NA NB NC tau NDD time (s)
0 2022-01-01 00:00:00 125 0.5 1.000000e+02 0.000000e+00 0.000000 350.000000 0.000000 0.0
1 2022-01-01 00:00:01 125 0.5 9.942178e+01 5.782160e-01 0.000000 335.384693 0.000125 1.0
2 2022-01-01 00:00:02 125 0.5 9.887092e+01 1.128597e+00 0.000486 322.563499 0.000243 2.0
3 2022-01-01 00:00:03 125 0.5 9.834404e+01 1.654564e+00 0.001392 311.194745 0.000356 3.0
4 2022-01-01 00:00:04 125 0.5 9.783845e+01 2.158887e+00 0.002663 301.021824 0.000465 4.0
... ... ... ... ... ... ... ... ... ...
86396 2022-01-01 23:59:56 125 0.5 5.800845e-34 3.370010e-27 100.000000 350.000000 0.000000 86396.0
86397 2022-01-01 23:59:57 125 0.5 5.795712e-34 3.367028e-27 100.000000 350.000000 0.000000 86397.0
86398 2022-01-01 23:59:58 125 0.5 5.790584e-34 3.364048e-27 100.000000 350.000000 0.000000 86398.0
86399 2022-01-01 23:59:59 125 0.5 5.785460e-34 3.361072e-27 100.000000 350.000000 0.000000 86399.0
86400 2022-01-02 00:00:00 125 0.5 5.780341e-34 3.358098e-27 100.000000 350.000000 0.000000 86400.0

86401 rows × 9 columns

Plot the results#

from cycler import cycler
plt.style.use('default')

fig, ax = plt.subplots()

ax.set_prop_cycle(cycler('color', ['tab:blue', 'tab:orange', 'tab:green']) + cycler('linestyle', ['-', '--', '-.']))

ax.plot(timesteps['time (s)'], timesteps[['NA', 'NB', 'NC']].values)
ax.legend(labels = ['$N_A$', '$N_B$', '$N_C$'], loc = 'upper left')
ax.set_ylabel('Defect state percentages [%]')
ax.set_xlabel('Time [s]')

ax2 = ax.twinx()
c_norm = 1.35 #Normalization constants "C_norm". See Repins 2023 for details
ax2.plot(timesteps['time (s)'], timesteps['NDD']*c_norm, c = 'black', label = 'NDD modeled')
ax.set_ylabel('NDD [$\mu s^{-1}$]')

literature_data_file = os.path.join(DATA_DIR, 'wyller data.csv')
data = pd.read_csv(literature_data_file, header = 0)
filtered = data[data['Series'] == '0.5 sun 125C']
ax2.scatter(filtered['X'], filtered['Y'], label = 'NDD data from Wyller', marker = 'v', c = 'black')

ax2.legend(loc = 'center left')

ax.set_xscale('log')

ax.set_title('Accelerated LETID Test (wafer)\n'fr'{temperature}$\degree$C, {suns} suns illumination')

plt.show()
../_images/58e0dae71ac0318bd7c4d1c49156810026aebeb17be335ec32f2e8e35a100066.png

Compare model results to experimental data#

Experimental data (0.5 suns, 125°C, 150°C, 175°C) from Wyller 2021. Comparison with experimental data requires normalization constant (\(C_{norm}\)), which relates NDD to \(N_B\) as described in Repins 2023.

temperatures = [125, 150, 175] # degrees celsius
timesteps125 = timesteps #we already modeled the 125C experiment above
temperature = temperatures[1] # degrees celsius
suns = 0.5 # "suns" of injection, e.g 1-sun illumination at open circuit would be 1; dark current injection is given as a fraction of Isc, e.g., injecting Isc would be 1. For this example we assume injection is 0.1*Isc.

timesteps = pd.date_range(start = '2022-01-01 00:00:00', end = '2022-01-02 00:00:00', freq = 'S') # three weeks of 1-minute interval timesteps. In general, we should select small timesteps unless we are sure defect reactions are proceeding very slowly
timesteps = pd.DataFrame(timesteps, columns = ['Datetime'])

temps = np.full(len(timesteps), temperature)
injection = np.full(len(timesteps), suns)

timesteps['Temperature'] = temps
timesteps['Injection'] = injection

timesteps[['NA', 'NB', 'NC', 'tau']] = np.nan # create columns for defect state percentages and lifetime, fill with NaNs for now, to fill iteratively below

timesteps.loc[0, ['NA', 'NB', 'NC']] = nA_0, nB_0, nC_0 # assign first timestep defect state percentages
timesteps.loc[0, 'tau'] = letid.tau_now(tau_0, tau_deg, nB_0) # calculate tau for the first timestep
/tmp/ipykernel_2272/241191725.py:4: FutureWarning: 'S' is deprecated and will be removed in a future version, please use 's' instead.
  timesteps = pd.date_range(start = '2022-01-01 00:00:00', end = '2022-01-02 00:00:00', freq = 'S') # three weeks of 1-minute interval timesteps. In general, we should select small timesteps unless we are sure defect reactions are proceeding very slowly
for index, timestep in timesteps.iterrows():

    # first row tau has already been assigned
    if index == 0:
        pass

    # loop through rows, new tau calculated based on previous NB. Reaction proceeds based on new tau.
    else:
        n_A = timesteps.at[index-1, 'NA']
        n_B = timesteps.at[index-1, 'NB']
        n_C = timesteps.at[index-1, 'NC']

        tau = letid.tau_now(tau_0, tau_deg, n_B)
        j_gen = j_gen

        temperature = timesteps.at[index, 'Temperature']
        injection = timesteps.at[index, 'Injection']

        # calculate defect reaction kinetics: reaction constant and carrier concentration factor.
        k_AB = letid.k_ij(mechanism_params['v_ab'], mechanism_params['ea_ab'], temperature)
        k_BA = letid.k_ij(mechanism_params['v_ba'], mechanism_params['ea_ba'], temperature)
        k_BC = letid.k_ij(mechanism_params['v_bc'], mechanism_params['ea_bc'], temperature)
        k_CB = letid.k_ij(mechanism_params['v_cb'], mechanism_params['ea_cb'], temperature)

        x_ab = letid.carrier_factor_wafer(tau, 'ab', injection, j_gen, wafer_thickness, mechanism_params)
        x_ba = letid.carrier_factor_wafer(tau, 'ba', injection, j_gen, wafer_thickness, mechanism_params)
        x_bc = letid.carrier_factor_wafer(tau, 'bc', injection, j_gen, wafer_thickness, mechanism_params)
        # x_cb there is no known excess carrier acceleration factor for the c->b transition

        # calculate the instantaneous change in NA, NB, and NC
        dN_Adt = (k_BA * n_B * x_ba) - (k_AB * n_A * x_ab)
        dN_Bdt = (k_AB * n_A * x_ab) + (k_CB * n_C) - ((k_BA * x_ba + k_BC * x_bc) * n_B)
        dN_Cdt = (k_BC * n_B * x_bc) - (k_CB * n_C)

        t_step = (timesteps.at[index, 'Datetime'] - timesteps.at[index-1,'Datetime']).total_seconds()

        # assign new defect state percentages
        timesteps.at[index, 'NA'] = n_A + dN_Adt*t_step
        timesteps.at[index, 'NB'] = n_B + dN_Bdt*t_step
        timesteps.at[index, 'NC'] = n_C + dN_Cdt*t_step
timesteps['tau'] = letid.tau_now(tau_0, tau_deg, timesteps['NB'])
timesteps['NDD'] = letid.calc_ndd(timesteps['tau'].iloc[0], timesteps['tau'])
timesteps['time (s)'] = (timesteps['Datetime'] - timesteps.iloc[0]['Datetime']).dt.total_seconds() # create a column for seconds elapsed
timesteps150 = timesteps
temperature = temperatures[2] # degrees celsius
suns = 0.5 # "suns" of injection, e.g 1-sun illumination at open circuit would be 1; dark current injection is given as a fraction of Isc, e.g., injecting Isc would be 1. For this example we assume injection is 0.1*Isc.

timesteps = pd.date_range(start = '2022-01-01 00:00:00', end = '2022-01-02 00:00:00', freq = 'S') # three weeks of 1-minute interval timesteps. In general, we should select small timesteps unless we are sure defect reactions are proceeding very slowly
timesteps = pd.DataFrame(timesteps, columns = ['Datetime'])

temps = np.full(len(timesteps), temperature)
injection = np.full(len(timesteps), suns)

timesteps['Temperature'] = temps
timesteps['Injection'] = injection

timesteps[['NA', 'NB', 'NC', 'tau']] = np.nan # create columns for defect state percentages and lifetime, fill with NaNs for now, to fill iteratively below

timesteps.loc[0, ['NA', 'NB', 'NC']] = nA_0, nB_0, nC_0 # assign first timestep defect state percentages
timesteps.loc[0, 'tau'] = letid.tau_now(tau_0, tau_deg, nB_0) # calculate tau for the first timestep
/tmp/ipykernel_2272/950821369.py:4: FutureWarning: 'S' is deprecated and will be removed in a future version, please use 's' instead.
  timesteps = pd.date_range(start = '2022-01-01 00:00:00', end = '2022-01-02 00:00:00', freq = 'S') # three weeks of 1-minute interval timesteps. In general, we should select small timesteps unless we are sure defect reactions are proceeding very slowly
for index, timestep in timesteps.iterrows():

    # first row tau has already been assigned
    if index == 0:
        pass

    # loop through rows, new tau calculated based on previous NB. Reaction proceeds based on new tau.
    else:
        n_A = timesteps.at[index-1, 'NA']
        n_B = timesteps.at[index-1, 'NB']
        n_C = timesteps.at[index-1, 'NC']

        tau = letid.tau_now(tau_0, tau_deg, n_B)
        j_gen = j_gen

        temperature = timesteps.at[index, 'Temperature']
        injection = timesteps.at[index, 'Injection']

        # calculate defect reaction kinetics: reaction constant and carrier concentration factor.
        k_AB = letid.k_ij(mechanism_params['v_ab'], mechanism_params['ea_ab'], temperature)
        k_BA = letid.k_ij(mechanism_params['v_ba'], mechanism_params['ea_ba'], temperature)
        k_BC = letid.k_ij(mechanism_params['v_bc'], mechanism_params['ea_bc'], temperature)
        k_CB = letid.k_ij(mechanism_params['v_cb'], mechanism_params['ea_cb'], temperature)

        x_ab = letid.carrier_factor_wafer(tau, 'ab', injection, j_gen, wafer_thickness, mechanism_params)
        x_ba = letid.carrier_factor_wafer(tau, 'ba', injection, j_gen, wafer_thickness, mechanism_params)
        x_bc = letid.carrier_factor_wafer(tau, 'bc', injection, j_gen, wafer_thickness, mechanism_params)
        # x_cb there is no known excess carrier acceleration factor for the c->b transition

        # calculate the instantaneous change in NA, NB, and NC
        dN_Adt = (k_BA * n_B * x_ba) - (k_AB * n_A * x_ab)
        dN_Bdt = (k_AB * n_A * x_ab) + (k_CB * n_C) - ((k_BA * x_ba + k_BC * x_bc) * n_B)
        dN_Cdt = (k_BC * n_B * x_bc) - (k_CB * n_C)

        t_step = (timesteps.at[index, 'Datetime'] - timesteps.at[index-1,'Datetime']).total_seconds()

        # assign new defect state percentages
        timesteps.at[index, 'NA'] = n_A + dN_Adt*t_step
        timesteps.at[index, 'NB'] = n_B + dN_Bdt*t_step
        timesteps.at[index, 'NC'] = n_C + dN_Cdt*t_step
timesteps['tau'] = letid.tau_now(tau_0, tau_deg, timesteps['NB'])
timesteps['NDD'] = letid.calc_ndd(timesteps['tau'].iloc[0], timesteps['tau'])
timesteps['time (s)'] = (timesteps['Datetime'] - timesteps.iloc[0]['Datetime']).dt.total_seconds() # create a column for seconds elapsed
timesteps175 = timesteps
timesteps = pd.concat([timesteps125, timesteps150, timesteps175])
normalization_constants = {125 : 1.35, 150 : 0.6, 175 : 0.35} #Normalization constants "C_norm". See Repins 2023 for details

timesteps['Normalization constant'] = timesteps['Temperature'].map(normalization_constants)
timesteps['NDD-normalized'] = timesteps['NDD']*timesteps['Normalization constant']
grouped_model = timesteps.groupby('Temperature')
literature_data_file = os.path.join(DATA_DIR, 'wyller data.csv')
data = pd.read_csv(literature_data_file, header = 0)

data['Temperature'] = pd.to_numeric(data['Series'].str[-4:-1])
#normalization_constants = {'0.5 sun 125C' : 1/1.35, '0.5 sun 150C' : 1/0.6, '0.5 sun 175C' : 1/0.35}
#data['Normalization constant'] = data['Series'].map(normalization_constants)
#data['Y-normalized'] = data['Y']*data['Normalization constant']
grouped_data = data.groupby('Temperature')
import itertools

fig, ax = plt.subplots()

for name, group in grouped_model:
    ax.plot(group['time (s)']+.1, group['NDD-normalized'], label = f"Modeled NDD {name}$\degree$C")
    ax.legend()


marker = itertools.cycle(('o', 's', 'v', 'o', '*'))

for name, group in grouped_data:
    ax.scatter(group['X'], group['Y'], label = f"Wyller NDD {name}$\degree$C", marker = next(marker))
    ax.legend()

ax.set_xscale('log')

ax.set_xlabel('Time [s]')
ax.set_ylabel('NDD [$\mu s^{-1}$]')

plt.show()
../_images/c0764bbf6c0087200a97de9898785ad372f4ada4246bfe1c080bee94f5cda7c6.png
import itertools

fig, ax = plt.subplots()

colors = itertools.cycle(('red', 'blue', 'green'))

for name, group in grouped_model:
    ax.plot(group['time (s)']+.1, group['NDD-normalized'], color = next(colors), label = f"Modeled {name}$\degree$C", linewidth = 3)
    ax.legend(prop={'size': 12})


marker = itertools.cycle(('o', 's', 'v', 'o', '*'))

for name, group in grouped_data:
    ax.scatter(group['X'], group['Y'], label = f"Literature data  {name}$\degree$C", color = next(colors), marker = next(marker), s = 70)
    ax.legend(prop={'size': 12})

ax.set_xscale('log')

ax.tick_params(axis='both', which='major', labelsize=16)


ax.set_xlabel('Time [s]', fontsize = 16)
ax.set_ylabel('Normalized defect \n density [$\mu s^{-1}$]', fontsize = 16)

ax.set_title('Modeled vs. Literature LETID data', fontsize = 16)


plt.tight_layout()

#fig.savefig('wafer data.png', dpi = 600)
plt.show()
../_images/6047790ad1c86032d7d63f6488d2d209004f586d6bd5b217517055d9ec603bf4.png