Data Collection 1 — Colab Notebook Answer Key

Last Updated: March 31, 2026 Download notebook (.ipynb)
  • Team Name: [Write in the name of your team.]
  • Authors: [Write in the name of the authors of this notebook.]
  • Cell Number: [Write in your cell number.]
  • J-V Apparatus Number: [Write in the number of your apparatus (JV1, JV2, or JV3) that you used for your measurements.]
  • EQE Apparatus Number: [Write in the number of the apparatus (EQE1, EQE2, or EQE3) that you used for your measurements.]

Reminder. For consistency across team notebooks, please include Markdown cells and inline comments to clarify key steps.

You can also find additional boilerplate code for creating multi-week subplots in the Multi-Week Plotting Helpers notebook.

1 J-V Analysis

Now that you have two weeks of data, you will begin to make plots that show how the \(J_{sc}\) and PCE change with time. The measurements you took last week prior to stressing will be at time equal to zero hours and the measurements this week will be after approximately 168 hours of stressing.

  1. Calculate the \(J_{sc}\) for each of your pixels using the forward scans. Remember you can copy and paste code from previous notebooks and use AI tools. Consider using a loop in your python code to be more efficient.
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
# Calculate Jsc for each pixel using the forward scan

# Pixel area in cm^2 — UPDATE THIS to match your actual pixel area
pixel_area = 0.14  # cm^2

# Loop over all 8 pixels, loading each J-V file
jsc_list = []
for i in range(1, 9):
    filename = f'2026_02_04_IV_cellR34_pixel{i}.csv'
    try:
        jv_data = pd.read_csv(filename)
        # Isc is the forward-scan current at V = 0
        isc = jv_data.loc[jv_data['Voltage (V)'] == 0.0, 'Forward_mean (mA)'].values[0]
        jsc = abs(isc) / pixel_area  # convert to current density (mA/cm^2)
        jsc_list.append(jsc)
        print(f"Pixel {i}: Isc = {isc:.4f} mA, Jsc = {jsc:.4f} mA/cm^2")
    except FileNotFoundError:
        jsc_list.append(np.nan)
        print(f"Pixel {i}: file not found, skipping")

# Assign to individual variables (used by the CSV-creation cell below)
jsc_pix1, jsc_pix2, jsc_pix3, jsc_pix4, jsc_pix5, jsc_pix6, jsc_pix7, jsc_pix8 = jsc_list
  1. Calculate the Power Conversion Efficiency (PCE) for each of your pixels using the forward scans.

    a. Remember you can copy and paste code from previous notebooks and use AI tools. Consider using a loop in your python code to be more efficient.

    b. The light power hitting the pixel has been set to \(P_{in} = 99.8\ mW/cm^{2}\).

# Calculate PCE for each pixel using the forward scan
P_in = 99.8  # mW/cm^2 (given)

pce_list = []
for i in range(1, 9):
    filename = f'2026_02_04_IV_cellR34_pixel{i}.csv'
    try:
        jv_data = pd.read_csv(filename)
        # Current density at each voltage
        J_fwd = jv_data['Forward_mean (mA)'] / pixel_area  # mA/cm^2
        V = jv_data['Voltage (V)']

        # Power density in the power-generating quadrant (V > 0, J < 0)
        mask = (V > 0) & (J_fwd < 0)
        power_density = V[mask] * J_fwd[mask].abs()  # mW/cm^2
        P_max = power_density.max()

        pce = (P_max / P_in) * 100  # percentage
        pce_list.append(pce)
        print(f"Pixel {i}: Pmax = {P_max:.4f} mW/cm^2, PCE = {pce:.2f}%")
    except FileNotFoundError:
        pce_list.append(np.nan)
        print(f"Pixel {i}: file not found, skipping")

# Assign to individual variables
pce_pix1, pce_pix2, pce_pix3, pce_pix4, pce_pix5, pce_pix6, pce_pix7, pce_pix8 = pce_list
  1. Create a csv file with \(J_{sc}\) and PCE values you just calculated for this week's measurements. This will allow you to easily reference previous weeks' data and plot data from multiple weeks at once. Be sure to download the csv file you create and add it to your team's Google Drive folder.

We have written the code for you to do this below. You just have to make sure the variable and file names are the ones you used.

# This code creates a csv file with your Jsc and PCE values for this week.

import pandas as pd

t = 168  # hours of stressing — update depending on week

week6_jsc_pce = {
    'Pixel Number': [1, 2, 3, 4, 5, 6, 7, 8],
    'Jsc (mA/cm^2)': [jsc_pix1, jsc_pix2, jsc_pix3, jsc_pix4, jsc_pix5, jsc_pix6, jsc_pix7, jsc_pix8],
    'PCE': [pce_pix1, pce_pix2, pce_pix3, pce_pix4, pce_pix5, pce_pix6, pce_pix7, pce_pix8],
    'Time (hr)': [t, t, t, t, t, t, t, t]
}

df = pd.DataFrame(week6_jsc_pce)

# Drop pixels with no data (NaN) so the CSV only contains measured pixels
df = df.dropna()

df.to_csv('2026_02_04_jsc_pce_cellR34.csv', index=False)
print(df)
print("\nData has been written to 2026_02_04_jsc_pce_cellR34.csv")
  1. Copy and paste the code in the above cell into your Colab notebook from the first time you took J-V data to create a csv file of those \(J_{sc}\) and PCE values as well. Make sure the variable and file names are correct. You will need to change at least the date in the csv file name and set t = 0 for the baseline measurement.
  1. Create figures that compare the \(J_{sc}\) measurements and the PCE measurements from the first week to the measurements from this week. Start by uploading both csv files to Colab. The plotting code has been provided below to create one figure for each parameter with multiple sub-plots. Each plot is a different pixel.
# Load the data from the CSV files. Add more weeks as you collect data.
# For testing, we only have one week of sample data.
week6data = pd.read_csv('2026_02_04_jsc_pce_cellR34.csv')

# In practice you would combine multiple weeks:
# week5data = pd.read_csv('2026_01_28_jsc_pce_cellR34.csv')
# combined_df = pd.concat([week5data, week6data])
combined_df = week6data  # single week for testing

# Plot for Jsc (4x2 grid)
fig, axes = plt.subplots(4, 2, figsize=(12, 16))
axes = axes.flatten()

for idx, pixel in enumerate(combined_df['Pixel Number'].unique()):
    if idx >= len(axes):
        break

    subset = combined_df[combined_df['Pixel Number'] == pixel]

    ax = axes[idx]
    ax.scatter(subset['Time (hr)'], subset['Jsc (mA/cm^2)'], color='g')

    ax.set_title(f'Pixel {pixel}')
    ax.set_xlabel('Time (hr)')
    ax.set_ylabel('Jsc (mA/cm^2)')

plt.tight_layout()
plt.show()


# Plot for PCE (4x2 grid)
fig, axes = plt.subplots(4, 2, figsize=(12, 16))
axes = axes.flatten()

for idx, pixel in enumerate(combined_df['Pixel Number'].unique()):
    if idx >= len(axes):
        break

    subset = combined_df[combined_df['Pixel Number'] == pixel]

    ax = axes[idx]
    ax.scatter(subset['Time (hr)'], subset['PCE'], color='b')

    ax.set_title(f'Pixel {pixel}')
    ax.set_xlabel('Time (hr)')
    ax.set_ylabel('PCE')

plt.tight_layout()
plt.show()

Question 1: Discuss what you observe from looking at the comparison of the \(J_{sc}\) and PCE values from this week and the first week. Do you have any pixels showing different behaviors after their first week of stressing?

2 EQE Analysis

  1. Calculate the EQE for each pixel you measured this week.

    a. Remember you can copy and paste code from previous notebooks and use AI tools. Consider using a loop in your python code to be more efficient.

# Calculate EQE for each pixel
# EQE = (I * h * c) / (q * P * lambda)
# With current in nA, power in uW, and wavelength in nm, this simplifies to:
# EQE = 1.2398 * I_nA / (P_uW * lambda_nm)

# Load power data (one file per cell, shared across all pixels)
power_data = pd.read_csv('2026_02_04_power_cellR34.csv')

eqe_results = {}
for i in range(1, 9):
    filename = f'2026_02_04_current_cellR34_pixel{i}.csv'
    try:
        current_data = pd.read_csv(filename)
        eqe = 1.2398 * current_data['Current_mean (nA)'] / (
            power_data['Power_mean (uW)'] * current_data['Wavelength (nm)']
        )
        eqe_results[i] = eqe
        print(f"Pixel {i}: EQE calculated — peak = {eqe.max():.4f} ({eqe.max()*100:.1f}%)")
    except FileNotFoundError:
        print(f"Pixel {i}: file not found, skipping")

# Assign to individual variables (used by the CSV-creation cell below)
eqe_pix1 = eqe_results.get(1, np.nan)
eqe_pix2 = eqe_results.get(2, np.nan)
eqe_pix3 = eqe_results.get(3, np.nan)
eqe_pix4 = eqe_results.get(4, np.nan)
eqe_pix5 = eqe_results.get(5, np.nan)
eqe_pix6 = eqe_results.get(6, np.nan)
eqe_pix7 = eqe_results.get(7, np.nan)
eqe_pix8 = eqe_results.get(8, np.nan)

2) Now we will create a csv file for your EQE data to simplify plotting multiple weeks worth of data on one plot. The code below will help to create a csv file combining all your pixel EQE data from this week.

# Create a combined EQE csv file for all measured pixels

eqe_df = pd.DataFrame({
    'Wavelength (nm)': power_data['Wavelength (nm)']
})

# Add a column for each pixel that has data
for i, eqe in eqe_results.items():
    eqe_df[f'eqe_pix{i}'] = eqe

eqe_df.to_csv('2026_02_04_eqe_cellR34.csv', index=False)
print(eqe_df.head(10))
print(f"\nEQE data has been written to 2026_02_04_eqe_cellR34.csv")
  1. If you did not create a csv file of your EQE data from your first week of baseline data collection, copy and paste the code in the above cell into your Colab notebook from the first week to create a csv file of those EQE data as well. Make sure the variable and file names are correct. You will need to change at least the date in the csv file name.
  1. Update and use the code below to create a figure that compares the EQE measurements from the first week to the measurements from this week. The code included here plots data for the first pixel.
# Load EQE data. For testing we only have one week of sample data.
eqe_week6 = pd.read_csv('2026_02_04_eqe_cellR34.csv')

# In practice you would also load prior weeks:
# eqe_week5 = pd.read_csv('2026_01_28_eqe_cellR34.csv')

fig, axes = plt.subplots(4, 2, figsize=(12, 16))
axes = axes.flatten()

for i in range(8):
    ax = axes[i]
    col = f'eqe_pix{i+1}'

    # Uncomment when you have a prior week's data:
    # if col in eqe_week5.columns:
    #     ax.plot(eqe_week5['Wavelength (nm)'], eqe_week5[col],
    #             label='Week 5', color='blue', linestyle='-')

    if col in eqe_week6.columns:
        ax.plot(eqe_week6['Wavelength (nm)'], eqe_week6[col],
                label='Week 6', color='red', linestyle='-.')

    ax.set_title(f'Pixel {i+1}')
    ax.set_xlabel('Wavelength (nm)')
    ax.set_ylabel('EQE')
    ax.legend()

plt.tight_layout()
plt.show()

Question 2: Discuss what you observe from looking at the comparison of the EQE plots from this week and last week. Do you have any pixels showing different behaviors after their first week of stressing?

3 Author Contributions (required)

List the name of each team member and please describe briefly how each member contributed to the work in lab this week. (e.g., taking a measurement, updating the Colab notebook, etc.)

4 Use of AI (required)

  1. How your team used AI tools: Describe the specific tasks where AI-assisted tools were involved. For example, did AI help with writing, debugging, optimizing, or refining your code? Which components of the analysis did your team use AI on?
  2. When your team used AI tools: Specify at what stage(s) of your programming process you used AI. Was it during initial code development, troubleshooting, etc.?
  3. Which AI tools your team used: Identify the AI tools or platforms your team consulted (e.g., ChatGPT, GitHub Copilot, etc.).