diff --git a/Measurements/AMR/amr_GUI.py b/Measurements/AMR/amr_GUI.py
index bd717b7..cdac4fb 100644
--- a/Measurements/AMR/amr_GUI.py
+++ b/Measurements/AMR/amr_GUI.py
@@ -290,7 +290,8 @@ def run_measurement(self):
frequency=frequency,
measure_time=measure_time,
sensitivity=sensitivity,
- save_dir=save_dir
+ save_dir=save_dir,
+ live_plot=False,
)
self.is_measuring = True
diff --git a/Measurements/DCIV/IV_sweep_GUI.py b/Measurements/DCIV/IV_sweep_GUI.py
index 2e3bf55..69b4f4b 100644
--- a/Measurements/DCIV/IV_sweep_GUI.py
+++ b/Measurements/DCIV/IV_sweep_GUI.py
@@ -4,12 +4,55 @@
import tkinter as tk
from tkinter import ttk
import numpy as np
+import threading
+import time
+import pandas as pd
from piec.drivers.sourcemeter.keithley2400 import Keithley2400
from piec.drivers.sourcemeter.virtual_keithley2400 import VirtualKeithley2400
from piec.measurement.iv_sweep import IVSweep
-from piec.analysis.utilities import standard_csv_to_metadata_and_data
from piec.measurement.gui_utils import MeasurementApp
+
+class _LiveIVSweep(IVSweep):
+ """IVSweep subclass that tracks data live for GUI updates.
+
+ Uses underscore-prefixed attributes so _update_metadata() ignores them.
+ """
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self._live_voltages = []
+ self._live_currents = []
+
+ def sweep(self):
+ voltages = np.linspace(self.v_start, self.v_stop, self.num_steps)
+ measured_voltages = []
+ measured_currents = []
+ self._live_voltages = []
+ self._live_currents = []
+
+ print(f"Starting IV sweep: {self.v_start}V to {self.v_stop}V in {self.num_steps} steps...")
+ self.sourcemeter.output(on=True)
+
+ for i, v in enumerate(voltages):
+ self.sourcemeter.set_source_voltage(v)
+ time.sleep(self.dwell_time)
+ measured_v = self.sourcemeter.get_voltage()
+ measured_i = self.sourcemeter.get_current()
+ measured_voltages.append(measured_v)
+ measured_currents.append(measured_i)
+ self._live_voltages.append(measured_v)
+ self._live_currents.append(measured_i)
+
+ if (i + 1) % max(1, self.num_steps // 10) == 0:
+ print(f" Step {i + 1}/{self.num_steps}: V={measured_v:.4f} V, I={measured_i:.6e} A")
+
+ self.data = pd.DataFrame({
+ "voltage (V)": measured_voltages,
+ "current (A)": measured_currents,
+ })
+ print("Sweep complete.")
+
DEFAULTS = {
"sm_address": "VIRTUAL",
"save_dir": r"your\default\save\directory",
@@ -28,6 +71,9 @@ def __init__(self, root):
print("Welcome to the IV Sweep GUI!")
print("Ctrl+Enter: Run Measurement")
+ self.measurement_thread = None
+ self.is_measuring = False
+
visa_resources = self.get_visa_resources()
# Static Inputs (Save Dir is at row 0 in base)
@@ -109,6 +155,10 @@ def refresh_instruments(self):
self.sm_address_entry.set("VIRTUAL")
def run_measurement(self):
+ if self.is_measuring:
+ print("Measurement already in progress...")
+ return
+
print("Running IV Sweep measurement...")
sm_address = self.sm_address_entry.get()
@@ -134,7 +184,7 @@ def run_measurement(self):
else:
sourcemeter = Keithley2400(sm_address)
- self.experiment = IVSweep(
+ self.experiment = _LiveIVSweep(
sourcemeter=sourcemeter,
v_start=v_start,
v_stop=v_stop,
@@ -144,18 +194,57 @@ def run_measurement(self):
sense_mode=sense_mode,
save_dir=save_dir,
)
- self.experiment.run_experiment()
+
+ self.is_measuring = True
+ self.run_button.config(state='disabled')
+
+ self.measurement_thread = threading.Thread(
+ target=self.experiment.run_experiment,
+ daemon=True,
+ )
+ self.measurement_thread.start()
+ self.update_plot_loop()
+
+ def update_plot_loop(self):
+ if not self.is_measuring:
+ return
+
self.plot_data()
+ if self.measurement_thread and self.measurement_thread.is_alive():
+ self.root.after(500, self.update_plot_loop)
+ else:
+ self.is_measuring = False
+ self.run_button.config(state='normal')
+ print("Measurement complete.")
+ self.plot_data()
+
def plot_data(self, event=None):
- self.ax.clear()
- metadata, data = standard_csv_to_metadata_and_data(self.experiment.filename)
- x_data = data[self.x_axis.get()]
- y_data = data[self.y_axis.get()]
+ if not hasattr(self, 'experiment'):
+ return
- self.ax.plot(x_data, y_data, marker=".", color="k", label=f"{self.y_axis.get()} vs {self.x_axis.get()}")
- self.ax.set_xlabel(self.x_axis.get())
- self.ax.set_ylabel(self.y_axis.get())
+ live_v = self.experiment._live_voltages
+ live_i = self.experiment._live_currents
+
+ if not live_v:
+ return
+
+ live_data = {
+ "voltage (V)": live_v,
+ "current (A)": live_i,
+ }
+
+ x_col = self.x_axis.get()
+ y_col = self.y_axis.get()
+
+ if x_col not in live_data or y_col not in live_data:
+ return
+
+ self.ax.clear()
+ self.ax.plot(live_data[x_col], live_data[y_col], marker=".", color="k",
+ label=f"{y_col} vs {x_col}")
+ self.ax.set_xlabel(x_col)
+ self.ax.set_ylabel(y_col)
self.canvas.draw()
diff --git a/Measurements/DCIV/IV_sweep_testing.ipynb b/Measurements/DCIV/IV_sweep_testing.ipynb
new file mode 100644
index 0000000..16f81a6
--- /dev/null
+++ b/Measurements/DCIV/IV_sweep_testing.ipynb
@@ -0,0 +1,352 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "cell-0",
+ "metadata": {},
+ "source": [
+ "# Caretta Lab IV Sweep Testing Notebook"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "cell-1",
+ "metadata": {},
+ "source": [
+ "This python notebook provides an interactive interface with the IV sweep measurement functionality available in the open source Python Integrated Experiment Control (piec) package. The IV sweep uses a sourcemeter to step voltage from a start to stop value, measuring current at each step to characterize the current-voltage relationship of a device. Code documentation and setup details can be found at https://piec.readthedocs.io."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "cell-2",
+ "metadata": {},
+ "source": [
+ "*COPY THIS NOTEBOOK AND RUN IN A LOCAL DIRECTORY TO PREVENT IT FROM BEING OVERWRITTEN BY GITHUB*"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "cell-3",
+ "metadata": {},
+ "source": [
+ "### Imports"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "cell-4",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Warning: if using digilent please check the readme file and install the required dependencies (UL) or try running pip install mcculw\n"
+ ]
+ }
+ ],
+ "source": [
+ "from piec.drivers.sourcemeter.virtual_keithley2400 import VirtualKeithley2400\n",
+ "from piec.measurement.iv_sweep import IVSweep\n",
+ "from piec.analysis.utilities import standard_csv_to_metadata_and_data\n",
+ "from piec.drivers.utilities import PiecManager\n",
+ "\n",
+ "import matplotlib.pyplot as plt\n",
+ "import pandas as pd\n",
+ "import numpy as np\n",
+ "import os"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "cell-5",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "c:\\Users\\Jesalina Phan\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\pyvisa_py\\tcpip.py:122: UserWarning: TCPIP::hislip resource discovery requires the zeroconf package to be installed... try 'pip install zeroconf'\n",
+ " warnings.warn(\n"
+ ]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "()"
+ ]
+ },
+ "execution_count": 2,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "pm = PiecManager()\n",
+ "pm.list_resources()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "cell-6",
+ "metadata": {},
+ "source": [
+ "### Instrument Setup"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "cell-7",
+ "metadata": {},
+ "source": [
+ "The IV sweep requires one instrument:\n",
+ "- **Sourcemeter** (sourcemeter): Sources voltage and measures current simultaneously. Typically a Keithley 2400.\n",
+ "\n",
+ "In piec, each instrument is represented by a class object. Initialize it with the appropriate VISA address. Use `'VIRTUAL'` to run in simulation mode without real hardware."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "cell-8",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Sourcemeter: Virtual Keithley 2400\n"
+ ]
+ }
+ ],
+ "source": [
+ "sourcemeter = VirtualKeithley2400() # Sourcemeter: 'GPIB0::24::INSTR' for real, VirtualKeithley2400() for simulation\n",
+ "\n",
+ "print('Sourcemeter: ', sourcemeter.idn())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "cell-9",
+ "metadata": {},
+ "source": [
+ "### IV Sweep Measurement"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "cell-10",
+ "metadata": {},
+ "source": [
+ "The IV sweep steps voltage linearly from `v_start` to `v_stop` in `num_steps` steps. At each step, the sourcemeter waits `dwell_time` seconds then records the measured voltage and current. The result is an I-V curve characterizing the device under test.\n",
+ "\n",
+ "Key parameters:\n",
+ "- **v_start**: Starting voltage in Volts.\n",
+ "- **v_stop**: Ending voltage in Volts.\n",
+ "- **num_steps**: Number of voltage steps in the sweep.\n",
+ "- **current_compliance**: Maximum allowed current in Amps — protects the device from overcurrent.\n",
+ "- **dwell_time**: Time to wait at each voltage step before measuring (seconds).\n",
+ "- **sense_mode**: `'2W'` for 2-wire (remote sense off) or `'4W'` for 4-wire (Kelvin) sensing."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "id": "cell-11",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "### VARIABLE DEFINITIONS ###\n",
+ "\n",
+ "path = r\"scratch\" # path to save data, make sure directory exists and you have permissions\n",
+ "\n",
+ "v_start = 0.0 # starting voltage (V)\n",
+ "v_stop = 1.0 # ending voltage (V)\n",
+ "num_steps = 50 # number of voltage steps\n",
+ "current_compliance = 0.1 # current compliance limit (A)\n",
+ "dwell_time = 0.1 # wait time at each step before measuring (s)\n",
+ "sense_mode = '2W' # sensing mode: '2W' or '4W'"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "id": "cell-12",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Running IV sweep experiment...\n",
+ "Sourcemeter configured.\n",
+ "Starting IV sweep: 0.0V to 1.0V in 50 steps...\n",
+ " Step 5/50: V=0.0816 V, I=0.000000e+00 A\n",
+ " Step 10/50: V=0.1837 V, I=0.000000e+00 A\n",
+ " Step 15/50: V=0.2857 V, I=0.000000e+00 A\n",
+ " Step 20/50: V=0.3878 V, I=0.000000e+00 A\n",
+ " Step 25/50: V=0.4898 V, I=0.000000e+00 A\n",
+ " Step 30/50: V=0.5918 V, I=0.000000e+00 A\n",
+ " Step 35/50: V=0.6939 V, I=0.000000e+00 A\n",
+ " Step 40/50: V=0.7959 V, I=0.000000e+00 A\n",
+ " Step 45/50: V=0.8980 V, I=0.000000e+00 A\n",
+ " Step 50/50: V=1.0000 V, I=0.000000e+00 A\n",
+ "Sweep complete.\n",
+ "Output off.\n",
+ "IV sweep data saved to scratch\\0_iv_sweep_0p0V_to_1p0V.csv\n",
+ "Experiment complete.\n"
+ ]
+ }
+ ],
+ "source": [
+ "### RUN ###\n",
+ "\n",
+ "experiment = IVSweep(\n",
+ " sourcemeter=sourcemeter,\n",
+ " v_start=v_start,\n",
+ " v_stop=v_stop,\n",
+ " num_steps=num_steps,\n",
+ " current_compliance=current_compliance,\n",
+ " dwell_time=dwell_time,\n",
+ " sense_mode=sense_mode,\n",
+ " save_dir=path,\n",
+ ")\n",
+ "\n",
+ "# Run the experiment — this will configure the sourcemeter,\n",
+ "# step through voltages capturing data, save results, and return.\n",
+ "experiment.run_experiment()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "id": "cell-13",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "
\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " voltage (V) | \n",
+ " current (A) | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 0 | \n",
+ " 0.000000 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " | 1 | \n",
+ " 0.020408 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " | 2 | \n",
+ " 0.040816 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " | 3 | \n",
+ " 0.061224 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " | 4 | \n",
+ " 0.081633 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " voltage (V) current (A)\n",
+ "0 0.000000 0.0\n",
+ "1 0.020408 0.0\n",
+ "2 0.040816 0.0\n",
+ "3 0.061224 0.0\n",
+ "4 0.081633 0.0"
+ ]
+ },
+ "execution_count": 6,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# Retrieve the captured data for further analysis\n",
+ "iv_metadata, iv_df = standard_csv_to_metadata_and_data(experiment.filename)\n",
+ "iv_df.head()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "id": "cell-14",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/png": "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Pn36OnglAABQc1SrYPHAAw/EBRdcEIMGDYqJEyfGrrvuGj179oxZs2aVWn7s2LHRu3fv6NOnT0yaNCmOPvrobJk8eXK2/ZtvvsmOc9lll2VfH3rooZg6dWoceeSR6/iVAQBA9VanUCgUoppILRR77rln3Hrrrdnj5cuXR5s2beK8886Lfv36rVL+hBNOiK+//joee+yx4nV77bVXdOrUKe64445Sn2P8+PHRpUuX+Oijj2KrrbZao3rNnz8/mjZtGvPmzYsmTZr8z68PAACqkvJc51abFovFixfHhAkTokePHsXr6tatmz0eN25cqfuk9SuWT1ILx+rKJ+lNq1OnTjRr1my1ZRYtWpS9ySsuAABQm1WbYDF79uxYtmxZtGjRosT69HjGjBml7pPWl6f8woULszEXqftUWYns6quvzpJb0ZJaTQAAoDarNsGioqWB3Mcff3yknmG33357mWX79++ftWwULdOmTVtn9QQAgKqoflQTm266adSrVy9mzpxZYn163LJly1L3SevXpHxRqEjjKkaPHv2d/ccaNWqULQAAQDVrsWjYsGHsscce8fTTTxevS4O30+Nu3bqVuk9av2L55MknnyxRvihUvPPOO/HUU09F8+bNK/BVAABAzVRtWiySNNXsqaeeGp07d85mbho8eHA269Ppp5+ebU/3oGjdunU2BiL55S9/Gfvvv3/ccMMNcdhhh8WwYcPi5ZdfjjvvvLM4VBx33HHZVLNp5qg0hqNo/MUmm2yShRkAAKCGBYs0feznn38eAwcOzAJAmjZ21KhRxQO0P/7442ymqCLdu3eP+++/Py699NK45JJLYvvtt4+HH344dt5552z7p59+Go8++mj2fTrWisaMGRMHHHDAOn19AABQXVWr+1hUVe5jAQBATVQj72MBAABUXYIFAACQm2ABAADkJlgAAAC5CRYAAEBuggUAAJCbYAEAAOQmWAAAALkJFgAAQG6CBQAAkJtgAQAA5CZYAAAAuQkWAABAboIFAACQm2ABAADkJlgAAAC5CRYAAEBuggUAAJCbYAEAAOQmWAAAALkJFgAAQG6CBQAAkJtgAQAA5CZYAAAAuQkWAABAboIFAACQm2ABAADkJlgAAAC5CRYAAEBuggUAAJBb/fIUnjt3bowYMSKeffbZ+Oijj+Kbb76JzTbbLHbbbbfo2bNndO/ePX+NAACAmtliMX369DjjjDNiiy22iKuuuiq+/fbb6NSpUxx88MGx5ZZbxpgxY+L73/9+dOjQIR544IGKrzUAAFD9WixSi8Spp54aEyZMyMJDaVLYePjhh2Pw4MExbdq0uOiii9Z2XQEAgCqqTqFQKHxXoS+++CKaN2++xgctb/nqbv78+dG0adOYN29eNGnSpLKrAwAA6/w6d426Qq1JSFi+fHk89thja1weAACopYO3S/Puu+/GXXfdFUOHDo3PP/88lixZsnZqBgAA1OzpZtN4invvvTf222+/2HHHHWPs2LExcODA+OSTT9Z+DQEAgJrVYjF+/Pj405/+FMOGDYu2bdvGSSedlIWK2267bbWDugEAgJpvjYPFLrvskg3eOPHEE7MwsdNOO2Xr+/XrV5H1AwAAalJXqKlTp2Zdnw488ECtEwAAwP8WLN5///1sPMXZZ5+d3RQv3adi0qRJUadOnTU9BAAAUNuDRevWrWPAgAHZLFB/+ctfYsaMGbH33nvH0qVLsxmh3n777YqtKQAAULNmhTrooIPir3/9a3z22Wdx6623xujRo6Ndu3bZOAwAAKD2+Z+CRZF0F76f//zn8fLLL8fEiRPjgAMOWHs1AwAAqo06hUKhUNmVqE23OgcAgJp4nbtGLRaHHnpovPDCC99ZbsGCBfG73/0uhgwZsua1BQAAasd9LH70ox/Fsccem6WVI444Ijp37hytWrWKxo0bx5w5c2LKlCnx3HPPxciRI+Owww6L6667ruJrDgAAVL+uUIsWLYrhw4fHAw88kIWI1BySHaBOney+Fj179ow+ffpE+/bto7bRFQoAgNp+nfs/j7FIB//222+jefPm0aBBg6jNBAsAAGr7de4adYUqTXqCtAAAAOSabhYAACARLAAAgNwECwAAIDfBAgAAWPfBYtttt40vvvhilfVz587NtgEAALVPuYPFhx9+GMuWLSv1Pheffvrp2qoXAABQjazxdLOPPvpo8fePP/54ialmU9B4+umnY5tttln7NQQAAGpOsDj66KOL77R96qmnltiWbpCXQsUNN9yw9msIAADUnGCxfPny7Ov3vve9GD9+fGy66aYVWS8AAKAaKfedtz/44IOKqQkAAFBtlTtYJGk8RVpmzZpV3JJR5K677lpbdQMAAGpqsLj88svjiiuuiM6dO8cWW2yRjbkAAABqt3JPN3vHHXfE0KFD48UXX4yHH344RowYUWKpaEOGDMkGijdu3Di6du0aL730Upnlhw8fHu3atcvKd+zYMUaOHFlie6FQiIEDB2Yhab311osePXrEO++8U8GvAgAAanmwWLx4cXTv3j0qwwMPPBAXXHBBDBo0KCZOnBi77rpr9OzZM+uSVZqxY8dG7969o0+fPjFp0qRsZqu0TJ48ubjMtddeG7fccksWmFJY2mCDDbJjLly4cB2+MgAAqGXB4owzzoj7778/KsONN94Yffv2jdNPPz06dOiQhYH1119/teM6br755jj00EPj4osvjvbt28eVV14Zu+++e9x6663FrRWDBw+OSy+9NI466qjYZZdd4t57743p06dnrTHVwSeffBJjxozJvtrHe+BzUDN+FqpqvezjPfA58LOQ5/eB3zmxzt6DSlMop1/84heFZs2aFfbbb7/CueeeWzj//PNLLBVl0aJFhXr16hVGjBhRYv0pp5xSOPLII0vdp02bNoWbbrqpxLqBAwcWdtlll+z79957r5DegkmTJpUok15bep1rat68edlx0td16Y9//GOhbt262XOnr0OGDCl89dVXZS6pjH28Bz4HVfdnoarWyz7eA58DPwt+t1X+Z+BPf/pTYV0rz3VunfRPeYLIgQceuNptaSD36NGjoyKkVoTWrVtn3Zu6detWvP5Xv/pV/Pe//826Ma2sYcOGcc8992TdoYrcdttt2QD0mTNnZsfae++9s2OnMRZFjj/++Oy1pK5XpVm0aFG2FJk/f360adMm5s2bF02aNIl1IaXWrbfeepVZuQAAqJnq1asXH374YWy55Zbr7DnTdW7Tpk3X6Dq33LNCpaaY2u7qq6/OwkllSgPMhQoAgNpj2bJl8e67767TYFHh97FI0ot67733Yr/99stmU0oNHxU59Wy603dKaamlYUXpccuWLUvdJ60vq3zR17RuxRaL9LhTp06rrUv//v2zQeQrt1isS9tvv33UrVu3RLhI78+UKVOylp3SfPrpp9lYE/t4D3wOqubPgp9R74HPgZ+Fmvj7oKrWq7rus91220WVVd5+VrNnzy4cdNBBhTp16mR9vdI4heT0008vXHDBBYWK1KVLl2xcR5Fly5YVWrduXbj66qtLLX/88ccXDj/88BLrunXrVjjrrLOy75cvX15o2bJl4frrry/envqPNWrUqPC3v/2tyo+xSP3s0riT9Nzp65r0u7OP98DnoGr/LFTVetnHe+Bz4Gchz+8Dv3MK6+w9WNvKc51b7mBx8sknF3r27FmYNm1aYcMNNywOFqNGjSp06NChUJGGDRuWXfQPHTq0MGXKlMKZZ56ZDSSfMWNGcd369etXXP75558v1K9fPwsOb775ZmHQoEGFBg0aFF5//fXiMtdcc012jEceeaTw2muvFY466qjC9773vcK3335b5YNFks7DmDFjsq/28R74HNSMn4WqWi/7eA98Dvws5Pl94HdOYZ29B9Vm8HbqPvT4449n95DYaKON4tVXX41tt9023n///Wy61q+++qrimlcisqlir7vuupgxY0bWXSndgyLdKC854IADspvnpRv4rXiDvDSdbBrokroPpftW9OrVq3h7evnpvhh33nlnzJ07N/bZZ59sgPcOO+xQIYNaAACguijPdW65g0UKE+nmdOkifcVg8fLLL2c3lvviiy+ithEsAACo7de55b5B3r777pvdRK5IGrCdBpWkloCypqIFAABqrnLPCpUCxMEHH5y1UCxevDi7j8Qbb7wRX375ZTz//PMVU0sAAKBKK3eLxc477xxvv/12NhbhqKOOiq+//jqOOeaYmDRpUrRt27ZiagkAANScFoslS5bEoYceGnfccUcMGDCg4moFAADU3BaLBg0axGuvvVZxtQEAAGpHV6if/OQn8ec//7liagMAANSOwdtLly6Nu+66K5566qnYY489YoMNNiix/cYbb1yb9QMAAGpisJg8eXLsvvvu2fdpEPeK0tSzAABA7VOuYLFs2bK4/PLLo2PHjrHxxhtXXK0AAICaO8aiXr16ccghh8TcuXMrrkYAAEDtuI/F+++/XzG1AQAAakewuOqqq+Kiiy6Kxx57LD777LOYP39+iQUAAKh96hQKhUJ5dqhbt26pg7XTYdLjNA6jtkmBqmnTpjFv3rxo0qRJZVcHAADW+XVuuWeFGjNmTJ66AQAANVC5g8X+++9fMTUBAABqT7B45plnyty+33775akPAABQG4LFAQccsMq6Fcda1MYxFgAAUNuVe1aoOXPmlFhmzZoVo0aNij333DOeeOKJiqklAABQs1os0qjwlX3/+9+Phg0bxgUXXBATJkxYW3UDAABqaovF6rRo0SKmTp26tg4HAADU5BaL1157rcTjdP+KdKO8a665Jjp16rQ26wYAANTUYJHCQxqsvfJ99fbaa6+466671mbdAACAmhosPvjgg1XuxL3ZZptF48aN12a9AACAmhwstt5664qpCQAAUPMHb48ePTo6dOgQ8+fPX2XbvHnzYqeddopnn312bdcPAACoScFi8ODB0bdv32jSpEmpU9CeddZZceONN67t+gEAADUpWLz66qtx6KGHrnb7IYcc4h4WAABQS61xsJg5c2Y0aNBgtdvr168fn3/++dqqFwAAUBODRevWrWPy5Mll3t9iiy22WFv1AgAAamKw6NWrV1x22WWxcOHCVbZ9++23MWjQoDj88MPXdv0AAIBqoE5h5TvdldEVavfdd4969erFueeeGzvuuGO2/q233oohQ4bEsmXLYuLEidGiRYuobdJMWWkAe5odq7TB7QAAUNOvc9f4PhYpMIwdOzbOPvvs6N+/f/Gdt9NduHv27JmFi9oYKgAAgHLeIC/dHG/kyJExZ86cePfdd7Nwsf3228fGG2/svQQAgFqs3HfeTlKQ2HPPPdd+bQAAgJo9eBsAAGB1BAsAACA3wQIAAMhNsAAAAHITLAAAgNwECwAAIDfBAgAAyE2wAAAAchMsAACA3AQLAAAgN8ECAADITbAAAAByEywAAIDcBAsAACA3wQIAAMhNsAAAAHITLAAAgNwECwAAIDfBAgAAyE2wAAAAchMsAACA3AQLAAAgN8ECAADITbAAAAByEywAAIDcBAsAACA3wQIAAMhNsAAAAHITLAAAgNoTLL788ss46aSTokmTJtGsWbPo06dPfPXVV2Xus3DhwjjnnHOiefPmseGGG8axxx4bM2fOLN7+6quvRu/evaNNmzax3nrrRfv27ePmm29eB68GAABqlmoTLFKoeOONN+LJJ5+Mxx57LJ555pk488wzy9zn/PPPj3/+858xfPjw+O9//xvTp0+PY445pnj7hAkTYvPNN4+//vWv2bEHDBgQ/fv3j1tvvXUdvCIAAKg56hQKhUJUcW+++WZ06NAhxo8fH507d87WjRo1Knr16hWffPJJtGrVapV95s2bF5tttlncf//9cdxxx2Xr3nrrraxVYty4cbHXXnuV+lyphSM93+jRo9e4fvPnz4+mTZtmz5laVAAAoCYoz3VutWixSEEgdX8qChVJjx49om7duvHiiy+Wuk9qjViyZElWrki7du1iq622yo63OulN22STTcqsz6JFi7I3ecUFAABqs2oRLGbMmJF1WVpR/fr1swCQtq1un4YNG2aBZEUtWrRY7T5jx46NBx544Du7WF199dVZcita0hgNAACozSo1WPTr1y/q1KlT5pK6L60LkydPjqOOOioGDRoUhxxySJll0ziM1LJRtEybNm2d1BEAAKqq+pX55BdeeGGcdtppZZbZdttto2XLljFr1qwS65cuXZrNFJW2lSatX7x4ccydO7dEq0WaFWrlfaZMmRIHH3xw1lJx6aWXfme9GzVqlC0AAEAVCBZpcHVavku3bt2ygJDGTeyxxx7ZujS4evny5dG1a9dS90nlGjRoEE8//XQ2zWwyderU+Pjjj7PjFUmzQR100EFx6qmnxm9+85u19toAAKA2qRazQiU/+MEPstaGO+64IxuUffrpp2eDudOsT8mnn36atTrce++90aVLl2zd2WefHSNHjoyhQ4dmo9jPO++84rEURd2fUqjo2bNnXHfddcXPVa9evTUKPEXMCgUAQE1UnuvcSm2xKI/77rsvzj333Cw8pNmgUivELbfcUrw9hY3UIvHNN98Ur7vpppuKy6aZnFKAuO2224q3P/jgg/H5559n97FIS5Gtt946Pvzww3X46gAAoHqrNi0WVZkWCwAAaqIadx8LAACgahMsAACA3AQLAAAgN8ECAADITbAAAAByEywAAIDcBAsAACA3wQIAAMhNsAAAAHITLAAAgNwECwAAIDfBAgAAyE2wAAAAchMsAACA3AQLAAAgN8ECAADITbAAAAByEywAAIDcBAsAACA3wQIAAMhNsAAAAHITLAAAgNwECwAAIDfBAgAAyE2wAAAAchMsAACA3AQLAAAgN8ECAADITbAAAAByEywAAIDcBAsAACA3wQIAAMhNsAAAAHITLAAAgNwECwAAIDfBAgAAyE2wAAAAchMsAACA3AQLAAAgN8ECAADITbAAAAByEywAAIDcBAsAACA3wQIAAMhNsAAAAHITLAAAgNwECwAAIDfBAgAAyE2wAAAAchMsAACA3AQLAAAgN8ECAADITbAAAAByEywAAIDcBAsAACA3wQIAAMhNsAAAAHITLAAAgNwECwAAIDfBAgAAyE2wAAAAchMsAACA2hMsvvzyyzjppJOiSZMm0axZs+jTp0989dVXZe6zcOHCOOecc6J58+ax4YYbxrHHHhszZ84stewXX3wRW265ZdSpUyfmzp1bQa8CAABqpmoTLFKoeOONN+LJJ5+Mxx57LJ555pk488wzy9zn/PPPj3/+858xfPjw+O9//xvTp0+PY445ptSyKajssssuFVR7AACo2eoUCoVCVHFvvvlmdOjQIcaPHx+dO3fO1o0aNSp69eoVn3zySbRq1WqVfebNmxebbbZZ3H///XHcccdl6956661o3759jBs3Lvbaa6/isrfffns88MADMXDgwDj44INjzpw5WavImpo/f340bdo0e87UogIAADVBea5zq0WLRQoC6UK/KFQkPXr0iLp168aLL75Y6j4TJkyIJUuWZOWKtGvXLrbaaqvseEWmTJkSV1xxRdx7773Z8QAAgPKrH9XAjBkzYvPNNy+xrn79+rHJJptk21a3T8OGDVdpeWjRokXxPosWLYrevXvHddddlwWO999/f43qk/ZLy4pJDgAAarNK/RN9v379ssHSZS2p+1JF6d+/f9Y16ic/+Um59rv66quzJqGipU2bNhVWRwAAqA4qtcXiwgsvjNNOO63MMttuu220bNkyZs2aVWL90qVLs5mi0rbSpPWLFy/OZnhasdUizQpVtM/o0aPj9ddfjwcffDB7XDTcZNNNN40BAwbE5ZdfvtpAcsEFF5RosRAuAACozSo1WKTB1Wn5Lt26dcsCQho3scceexSHguXLl0fXrl1L3SeVa9CgQTz99NPZNLPJ1KlT4+OPP86Ol/zjH/+Ib7/9tnifNDj8pz/9aTz77LPRtm3b1danUaNG2QIAAFSjMRapu9Khhx4affv2jTvuuCMblH3uuefGj3/84+IZoT799NNsRqc0CLtLly5ZF6U0hWxqWUhjMdIo9vPOOy8LFUUzQq0cHmbPnl38fOWZFQoAAGq7ahEskvvuuy8LEyk8pNmbUivELbfcUrw9hY3UIvHNN98Ur7vpppuKy6bB1j179ozbbrutkl4BAADUXNXiPhZVnftYAABQE9W4+1gAAABVm2ABAADkJlgAAAC5CRYAAEBuggUAAJCbYAEAAOQmWAAAALkJFgAAQG6CBQAAkJtgAQAA5CZYAAAAuQkWAABAboIFAACQm2ABAADkJlgAAAC5CRYAAEBuggUAAJCbYAEAAOQmWAAAALkJFgAAQG6CBQAAkJtgAQAA5CZYAAAAuQkWAABAboIFAACQm2ABAADkJlgAAAC5CRYAAEBuggUAAJCbYAEAAOQmWAAAALkJFgAAQG6CBQAAkJtgAQAA5CZYAAAAuQkWAABAboIFAAAgWAAAAJVPiwUAAJCbYAEAAOQmWAAAALkJFgAAQG6CBQAAkJtgAQAA5CZYAAAAuQkWAABAboIFAACQm2ABAADkVj//ISgUCtmbMH/+fG8GAAA1RtH1bdH1blkEi7VgwYIF2dc2bdqsjcMBAECVu95t2rRpmWXqFNYkflCm5cuXx/Tp02OjjTaKOnXqrPMUmQLNtGnTokmTJuv0ualczn3t5dzXXs597eXc117zK/laL0WFFCpatWoVdeuWPYpCi8VakN7kLbfcMipT+qAJFrWTc197Ofe1l3Nfezn3tVeTSrzW+66WiiIGbwMAALkJFgAAQG6CRTXXqFGjGDRoUPaV2sW5r72c+9rLua+9nPvaq1E1utYzeBsAAMhNiwUAAJCbYAEAAOQmWAAAALkJFtXAkCFDYptttonGjRtH165d46WXXiqz/PDhw6Ndu3ZZ+Y4dO8bIkSPXWV2pvHP/xz/+Mfbdd9/YeOONs6VHjx7f+Vmh5vzcFxk2bFh2o86jjz66wutI1Tj3c+fOjXPOOSe22GKLbHDnDjvs4Pd+LTn3gwcPjh133DHWW2+97AZq559/fixcuHCd1Ze145lnnokjjjgiuwFd+v398MMPf+c+//nPf2L33XfPfua32267GDp0aNU4HenO21Rdw4YNKzRs2LBw1113Fd54441C3759C82aNSvMnDmz1PLPP/98oV69eoVrr722MGXKlMKll15aaNCgQeH1119f53Vn3Z77E088sTBkyJDCpEmTCm+++WbhtNNOKzRt2rTwySefOBU1/NwX+eCDDwqtW7cu7LvvvoWjjjpqndWXyjv3ixYtKnTu3LnQq1evwnPPPZd9Bv7zn/8UXnnlFaelhp/7++67r9CoUaPsazrvjz/+eGGLLbYonH/++eu87uQzcuTIwoABAwoPPfRQIV2ajxgxoszy77//fmH99dcvXHDBBdm13u9///vs2m/UqFGVfioEiyquS5cuhXPOOaf48bJlywqtWrUqXH311aWWP/744wuHHXZYiXVdu3YtnHXWWRVeVyr33K9s6dKlhY022qhwzz33ODW14Nyn8929e/fCn/70p8Kpp54qWNSSc3/77bcXtt1228LixYvXYS2pCuc+lT3ooINKrEsXmnvvvbcTVI3FGgSLX/3qV4WddtqpxLoTTjih0LNnz0Jl0xWqClu8eHFMmDAh69JSpG7dutnjcePGlbpPWr9i+aRnz56rLU/NOfcr++abb2LJkiWxySabVGBNqSrn/oorrojNN988+vTp46TUonP/6KOPRrdu3bKuUC1atIidd945fvvb38ayZcvWYc2pjHPfvXv3bJ+i7lLvv/9+1gWuV69eTkgNN64KX+vVr+wKsHqzZ8/O/nNI/1msKD1+6623St1nxowZpZZP66nZ535l//d//5f111z5lw8179w/99xz8ec//zleeeWVdVRLqsq5TxeTo0ePjpNOOim7qHz33Xfj5z//efZHhXRDLWruuT/xxBOz/fbZZ5/U+ySWLl0aP/vZz+KSSy5ZR7WmssxYzbXe/Pnz49tvv83G3FQWLRZQA11zzTXZIN4RI0ZkgwCpuRYsWBAnn3xyNnh/0003rezqsI4tX748a6m68847Y4899ogTTjghBgwYEHfccYdzUcOlwbupdeq2226LiRMnxkMPPRT/+te/4sorr6zsqlGLabGowtJFQr169WLmzJkl1qfHLVu2LHWftL485ak5577I9ddfnwWLp556KnbZZZcKrimVfe7fe++9+PDDD7MZRVa82Ezq168fU6dOjbZt2zpRNfTnPs0E1aBBg2y/Iu3bt8/+opm61zRs2LDC603lnPvLLrss+6PCGWeckT1Os0B+/fXXceaZZ2bhMnWlomZquZprvSZNmlRqa0XiU1eFpf8Q0l+gnn766RIXDOlx6lNbmrR+xfLJk08+udry1Jxzn1x77bXZX6tGjRoVnTt3Xke1pTLPfZpa+vXXX8+6QRUtRx55ZBx44IHZ92kKSmruz/3ee++ddX8qCpPJ22+/nQUOoaJmn/s0jm7l8FAUMP/fGGBqqm5V+VqvskeP893Tz6Xp5IYOHZpNKXbmmWdm08/NmDEj237yyScX+vXrV2K62fr16xeuv/76bMrRQYMGmW62lpz7a665Jpuq8MEHHyx89tlnxcuCBQsq8VWwLs79yswKVXvO/ccff5zN/nbuuecWpk6dWnjssccKm2++eeGqq66qxFfBujj36f/3dO7/9re/ZdOPPvHEE4W2bdtms0NSvSxYsCCbKj4t6dL8xhtvzL7/6KOPsu3pvKfzv/J0sxdffHF2rZemmjfdLGsszU+81VZbZReNaTq6F154oXjb/vvvn11ErOjvf/97YYcddsjKp+nI/vWvf3m3a8G533rrrbNfSCsv6T8fav7P/YoEi9p17seOHZtNK54uStPUs7/5zW+y6Yep2ed+yZIlhV//+tdZmGjcuHGhTZs2hZ///OeFOXPmVFLt+V+NGTOm1P+/i853+prO/8r7dOrUKfuspJ/7u+++u1AV1En/VHarCQAAUL0ZYwEAAOQmWAAAALkJFgAAQG6CBQAAkJtgAQAA5CZYAAAAuQkWAABAboIFAACQm2ABQIXaZpttYvDgwVX6Xf7zn/8chxxySLn2mT17dmy++ebxySefVFi9AKoTwQKAUh1xxBFx6KGHlrrt2WefjTp16sRrr71W7ncv7ffwww9XmXd94cKFcdlll8WgQYOyx+edd160b9++1LIff/xx1KtXLx599NHYdNNN45RTTineD6C2EywAKFWfPn3iySefLPUv8nfffXd07tw5dtlll2r/7j344IPRpEmT2HvvvYtf91tvvRVjx45dpezQoUOzVopevXplj08//fS477774ssvv1zn9QaoagQLAEp1+OGHx2abbZZdTK/oq6++iuHDh2cX4Mk//vGP2GmnnaJRo0ZZt6cbbrhhte9o2p788Ic/zFouih6/9957cdRRR0WLFi1iww03jD333DOeeuqpEvt+9tlncdhhh8V6660X3/ve9+L+++9fpZvV3Llz44wzzsjqncLCQQcdFK+++mqZZ3jYsGFZ60yRTp06xe677x533XVXiXKFQiF7L0499dSoX79+ti697latWsWIESN8ioBaT7AAoFTp4jl19UkX0+miukgKFcuWLYvevXvHhAkT4vjjj48f//jH8frrr8evf/3rrFvRymGkyPjx44tbPFJQKHqcwkpqBXj66adj0qRJWResdLGfuh4VSXWZPn16/Oc//8nCzJ133hmzZs0qcfwf/ehH2bp///vfWd1SQDj44IPLbFF47rnnstaXFaXQ9Pe//z2+/vrr4nXpeT/44IP46U9/WqJsly5dsq5hALVeAQBW480330yJojBmzJjidfvuu2/hJz/5Sfb9iSeeWPj+979fYp+LL7640KFDh+LHW2+9deGmm24qfpyON2LEiO98z3faaafC73//+xL1GD9+fPH2d955J1tXdOxnn3220KRJk8LChQtLHKdt27aFP/zhD6U+x5w5c7JjPPPMM6usb9y4ceHuu+8uXnfyyScX9tlnn1WOcf755xcOOOCA73w9ADWdFgsAVqtdu3bRvXv34m5B7777bvbX+aJuUG+++Wbx2IQi6fE777yTtWqsqdRicdFFF2WDpps1a5Z1h0rHLmqxmDp1ataCklogimy33Xax8cYbFz9OXZ7ScZo3b57tX7SkVobU1ao03377bfa1cePGJdanOhxzzDHFr3v+/PlZK0nR615R6pr1zTffrPFrBaip/l8nUQBYjXQxnWZKGjJkSNaFqW3btrH//vuv1fcrhYo0UPz666/PAkO6WD/uuONi8eLFa3yMFCq22GKLrMvSylJQKE0KIWmsx5w5c0p93akbVQpTY8aMyWaDSl2tVpa6WaUxHQC1nRYLAMqUxlDUrVs3Gyx97733ZmMM0sV4kloYnn/++RLl0+MddtghuxAvTYMGDVZpzUj7nHbaadmg7o4dO0bLli3jww8/LN6+4447xtKlS7PxF0XSBf+KgSC1ZsyYMSNr2UjhZMUlTQ1bmoYNG0aHDh1iypQpq2w78MADs0HiKUylJY0j2WCDDVYpN3ny5Nhtt93KeAcBagfBAoAype5EJ5xwQvTv3z8bcJ0CQJELL7wwG3B95ZVXxttvvx333HNP3HrrrVkLxOqkmZzSPikEFAWD7bffPh566KF45ZVXsi5NJ554YixfvrxEl6wePXrEmWeeGS+99FIWMNL3qWWjKOSk7d26dYujjz46nnjiiSyYpCljBwwYEC+//PJq69OzZ89sAPfK0nFTiLr99ttj3LhxpXaDSl2g0iDx8t5cD6AmEiwA+E7pojqFgHQRnqZXXbGVIM2elKZs3XnnnWPgwIFxxRVXlAgfK0vT0aZuT23atCn+S/+NN96YjZdI4znSbFDpeVYcT5Gk1pI0He1+++2XtWz07ds3Ntpoo+LxESkIjBw5Mtue7i+RWk1SK8NHH32U7VfWa0v7zZs3b5Vt6XWk9Wla2a5du66y/ZFHHomtttoq9t13X58ioNark0Zw1/p3AYBqJ924L4WTdL+LNBYijzR2IgWZ1CpTHnvttVf84he/yFpYAGo7LRYAVAujR4+ORx99NJvlKXVxSq0RqVtVaqHI67rrrsu6fJXH7Nmzs5mj0v08AAgtFgBUD48//ng2puP999/PukClblPprttbb711ZVcNAMECAABYG3SFAgAAchMsAACA3AQLAAAgN8ECAADITbAAAAByEywAAIDcBAsAACA3wQIAAMhNsAAAACKv/w+nxdIioQ6CcgAAAABJRU5ErkJggg==",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "# Plot the IV curve: current vs voltage\n",
+ "iv_df.plot(x=\"voltage (V)\", y=\"current (A)\", figsize=(8, 5), marker=\".\", color=\"k\", legend=False)\n",
+ "plt.xlabel(\"Voltage (V)\")\n",
+ "plt.ylabel(\"Current (A)\")\n",
+ "plt.title(\"IV Sweep\")\n",
+ "plt.tight_layout()\n",
+ "plt.show()"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.12.3"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}