{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Voltage Conversion from Full Cell to Half-Cell Potentials\n", "\n", "This notebook demonstrates how to convert full cell voltage measurements to half-cell potentials (vs SHE and vs RHE) using calibration data from electrochemical measurements in a three-electrode configuration.\n", "\n", "## Methodology\n", "\n", "The conversion accounts for:\n", "- Membrane overpotential and ionic resistance\n", "- Nernstian pH gradient effects\n", "- Reference electrode potential corrections\n", "- Current density-dependent ohmic losses\n", "\n", "**Reference:** Arabyarmohammadi, F. et al. Voltage distribution within carbon dioxide reduction electrolysers. *Nature Sustainability* (2025) - https://www.nature.com/articles/s41893-025-01643-4\n" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Calibration Data and Experimental Conditions\n", "\n", "**Experiment conditions:** Neutral CO₂RR in 4cm² cell, Sputtered Copper Catalyst, 0.1M Bicarbonate - ref electrode (3M KCl) 230mV vs SHE\n" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Reference electrode potential: 0.23 V vs SHE\n", "Cathode pH: 12.5, Anode pH: 3\n", "Nernstian pH loss: 0.560 V\n", "Geometric area: 4 cm²\n", "Membrane loss: 0.1 V\n", "Note: Anode measured potential vs reference is interpolated from calibration data\n" ] } ], "source": [ "# Experiment conditions\n", "ref_pot = 0.23 # V Ag/AgCl electrode\n", "cathode_pH = 12.5\n", "anode_pH = 3\n", "Nern_pH_loss = (cathode_pH - anode_pH) * 0.059 \n", "geo_area = 4 # cm²\n", "membrane_loss = 0.1 # V\n", "# Note: anode_measured_potential_vs_ref is now interpolated from calibration data\n", "\n", "print(f\"Reference electrode potential: {ref_pot} V vs SHE\")\n", "print(f\"Cathode pH: {cathode_pH}, Anode pH: {anode_pH}\")\n", "print(f\"Nernstian pH loss: {Nern_pH_loss:.3f} V\")\n", "print(f\"Geometric area: {geo_area} cm²\")\n", "print(f\"Membrane loss: {membrane_loss} V\")\n", "print(f\"Note: Anode measured potential vs reference is interpolated from calibration data\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Calibration Data for Interpolation\n", "\n", "The interpolation functions use calibration data:\n", "- Current density: [50, 100, 200] mA/cm²\n", "- Cathode resistance: [0.48, 0.34, 0.3] Ω\n", "- Anode potential vs reference: [1.3, 1.35, 1.4] V\n", "\n", "These values are embedded in the `interpolate_cathode_R()` and `interpolate_anode_potential_vs_ref()` functions.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Core Conversion Functions\n" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "def she2rhe(ushe, pH, ref_pot):\n", " \"\"\"Convert SHE potential to RHE potential.\"\"\"\n", " ushe = ushe + ref_pot + (0.059 * pH)\n", " return ushe\n", "\n", "def rhe2she(urhe, pH, ref_pot):\n", " \"\"\"Convert RHE potential to SHE potential.\"\"\"\n", " urhe = urhe - (0.059 * pH)\n", " return urhe\n", "\n", "def interpolate_cathode_R(current_density):\n", " \"\"\"\n", " Interpolate cathode resistance R from log(j) vs R calibration data.\n", " \n", " Calibration data:\n", " j = [50, 100, 200] mA/cm²\n", " R = [0.48, 0.34, 0.3] ohm\n", " \n", " Fits log(j) vs R and interpolates R for given current density.\n", " \"\"\"\n", " # Calibration data\n", " j_array = np.array([50, 100, 200]) # mA/cm²\n", " R_array = np.array([0.48, 0.34, 0.3]) # ohm\n", " \n", " # Convert to log scale for j\n", " log_j = np.log10(j_array)\n", " \n", " # Fit linear relationship: R = a * log10(j) + b\n", " fit_params = np.polyfit(log_j, R_array, 1)\n", " a, b = fit_params\n", " \n", " # Interpolate R for given current density\n", " if current_density <= 0:\n", " # Use minimum R if current density is too small\n", " return R_array[-1] # Use the smallest R (at highest j)\n", " \n", " log_j_input = np.log10(current_density)\n", " R_interpolated = a * log_j_input + b\n", " \n", " # Clamp to reasonable bounds (between min and max R values)\n", " R_interpolated = np.clip(R_interpolated, R_array.min(), R_array.max())\n", " \n", " return R_interpolated\n", "\n", "def interpolate_anode_potential_vs_ref(current_density):\n", " \"\"\"\n", " Interpolate anode measured potential vs reference from log(j) vs anode_pot calibration data.\n", " \n", " Calibration data:\n", " j = [50, 100, 200] mA/cm²\n", " anode_pot = [1.3, 1.35, 1.4] V\n", " \n", " Fits log(j) vs anode_pot and interpolates anode_pot for given current density.\n", " \"\"\"\n", " # Calibration data\n", " j_array = np.array([50, 100, 200]) # mA/cm²\n", " anode_pot_array = np.array([1.3, 1.35, 1.4]) # V\n", " \n", " # Convert to log scale for j\n", " log_j = np.log10(j_array)\n", " \n", " # Fit linear relationship: anode_pot = a * log10(j) + b\n", " fit_params = np.polyfit(log_j, anode_pot_array, 1)\n", " a, b = fit_params\n", " \n", " # Interpolate anode_pot for given current density\n", " if current_density <= 0:\n", " # Use minimum anode_pot if current density is too small\n", " return anode_pot_array[0] # Use the smallest anode_pot (at lowest j)\n", " \n", " log_j_input = np.log10(current_density)\n", " anode_pot_interpolated = a * log_j_input + b\n", " \n", " # Clamp to reasonable bounds (between min and max anode_pot values)\n", " anode_pot_interpolated = np.clip(anode_pot_interpolated, anode_pot_array.min(), anode_pot_array.max())\n", " \n", " return anode_pot_interpolated\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Main Conversion Functions\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "def cell2rhe(vcell, ref_pot, anode_pH, \n", " membrane_loss, Nern_pH_loss, current_density, geo_area):\n", " \"\"\"\n", " Convert full cell voltage to cathode potential vs RHE.\n", " \n", " Steps:\n", " 1. Interpolate anode measured potential vs reference from calibration data\n", " 2. Convert anode measured potential (vs reference) to RHE:\n", " V_anode_RHE = anode_measured_potential_vs_ref + ref_pot + 0.059 * anode_pH\n", " 3. Calculate cathode RHE:\n", " V_cathode_RHE = (V_anode_RHE + membrane_loss + Nern_pH_loss) - full_cell_V\n", " 4. Apply IR correction:\n", " V_cathode_RHE = V_cathode_RHE - (i/1000 * R * A)\n", " where i is current density in A/cm², R is interpolated resistance, A is geometric area\n", " \n", " Parameters:\n", " -----------\n", " vcell : float\n", " Full cell voltage (V)\n", " ref_pot : float\n", " Reference electrode potential vs SHE (V)\n", " anode_pH : float\n", " Anode pH\n", " membrane_loss : float\n", " Membrane loss (V)\n", " Nern_pH_loss : float\n", " Nernst pH loss = (cathode_pH - anode_pH) * 0.059 (V)\n", " current_density : float\n", " Current density (mA/cm²)\n", " geo_area : float\n", " Geometric area (cm²)\n", " \n", " Returns:\n", " --------\n", " v_cathode_rhe : float\n", " Cathode potential vs RHE (V)\n", " \"\"\"\n", " # Step 1: Interpolate anode measured potential vs reference\n", " anode_measured_potential_vs_ref = interpolate_anode_potential_vs_ref(current_density)\n", " \n", " # Step 2: Convert anode measured potential to RHE\n", " v_anode_rhe = anode_measured_potential_vs_ref + ref_pot + 0.059 * anode_pH\n", " \n", " # Step 3: Calculate cathode RHE with membrane and Nernst pH losses\n", " v_cathode_rhe = (v_anode_rhe + membrane_loss + Nern_pH_loss) - vcell\n", " \n", " # Step 4: Interpolate R from calibration data\n", " R = interpolate_cathode_R(current_density) # current_density in mA/cm², R in ohm\n", " \n", " # Step 5: Apply IR correction\n", " \n", " # Convert current density from mA/cm² to A/cm² and apply IR correction\n", " # i/1000 converts mA/cm² to A/cm²\n", " IR_drop = (current_density / 1000.0) * R * geo_area\n", " v_cathode_rhe = v_cathode_rhe - IR_drop\n", " \n", " return v_cathode_rhe\n", "\n", "def fullcell2halfcell(vcell, current_density, custom_params=None):\n", " \"\"\"\n", " Main function to convert a voltage value from full cell to half cell vs SHE or RHE.\n", " \n", " Parameters:\n", " -----------\n", " vcell : float\n", " Full cell voltage (V)\n", " current_density : float\n", " Current density (mA/cm²)\n", " custom_params : dict, optional\n", " Custom parameters for voltage conversion\n", " \n", " Returns:\n", " --------\n", " ushe : float\n", " Half-cell potential vs SHE (V)\n", " urhe : float\n", " Half-cell potential vs RHE (V)\n", " \"\"\"\n", " # Use custom parameters if provided, otherwise use defaults\n", " if custom_params:\n", " params = {\n", " 'ref_pot': custom_params.get('ref_pot', ref_pot),\n", " 'cathode_pH': custom_params.get('cathode_pH', cathode_pH),\n", " 'anode_pH': custom_params.get('anode_pH', anode_pH),\n", " 'membrane_loss': custom_params.get('membrane_loss', membrane_loss),\n", " 'geo_area': custom_params.get('geo_area', geo_area),\n", " }\n", " else:\n", " params = {\n", " 'ref_pot': ref_pot,\n", " 'cathode_pH': cathode_pH,\n", " 'anode_pH': anode_pH,\n", " 'membrane_loss': membrane_loss,\n", " 'geo_area': geo_area,\n", " }\n", " \n", " # Calculate Nern_pH_loss\n", " Nern_pH_loss = (params['cathode_pH'] - params['anode_pH']) * 0.059\n", " \n", " # Convert to RHE\n", " urhe = cell2rhe(vcell, \n", " params['ref_pot'],\n", " params['anode_pH'],\n", " params['membrane_loss'],\n", " Nern_pH_loss,\n", " current_density,\n", " params['geo_area'])\n", " \n", " # Convert RHE to SHE\n", " ushe = rhe2she(urhe, params['cathode_pH'], params['ref_pot'])\n", " \n", " return ushe, urhe\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Working Example: Convert Sample Voltages\n" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Voltage Conversion Example:\n", "================================================================================\n", "Full Cell (V) Current (mA/cm²) vs SHE (V) vs RHE (V) \n", "--------------------------------------------------------------------------------\n", "2.8 50 -1.263 -0.525 \n", "3.2 100 -1.669 -0.932 \n", "3.5 150 -1.983 -1.246 \n", "3.8 200 -2.310 -1.573 \n", "4.0 250 -2.570 -1.832 \n" ] } ], "source": [ "# Example: Convert some sample full cell voltages\n", "# Note: Now requires current density as well\n", "sample_voltages = [2.8, 3.2, 3.5, 3.8, 4.0]\n", "sample_current_densities = [50, 100, 150, 200, 250] # mA/cm²\n", "\n", "print(\"Voltage Conversion Example:\")\n", "print(\"=\" * 80)\n", "print(f\"{'Full Cell (V)':<12} {'Current (mA/cm²)':<18} {'vs SHE (V)':<12} {'vs RHE (V)':<12}\")\n", "print(\"-\" * 80)\n", "\n", "for vcell, j in zip(sample_voltages, sample_current_densities):\n", " ushe, urhe = fullcell2halfcell(vcell, j)\n", " print(f\"{vcell:<12.1f} {j:<18.0f} {ushe:<12.3f} {urhe:<12.3f}\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Usage Instructions\n", "\n", "To use these functions in your own analysis:\n", "\n", "1. **Import the functions** from this notebook\n", "2. **Call `fullcell2halfcell(vcell, current_density)`** with your full cell voltage and current density\n", "3. **The function returns** `(ushe, urhe)` - half-cell potentials vs SHE and RHE\n", "\n", "### Example:\n", "```python\n", "# Convert a full cell voltage of 3.5 V at 100 mA/cm²\n", "ushe, urhe = fullcell2halfcell(3.5, 100)\n", "print(f\"Half-cell potential vs SHE: {ushe:.3f} V\")\n", "print(f\"Half-cell potential vs RHE: {urhe:.3f} V\")\n", "```\n", "\n", "### For batch conversion:\n", "```python\n", "# Convert arrays of voltages and current densities\n", "voltages = [2.8, 3.2, 3.5, 3.8, 4.0]\n", "current_densities = [50, 100, 150, 200, 250] # mA/cm²\n", "she_values = []\n", "rhe_values = []\n", "\n", "for v, j in zip(voltages, current_densities):\n", " ushe, urhe = fullcell2halfcell(v, j)\n", " she_values.append(ushe)\n", " rhe_values.append(urhe)\n", "\n", "print(f\"SHE values: {she_values}\")\n", "print(f\"RHE values: {rhe_values}\")\n", "```\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Complete Calculation Example: Pt Catalyst\n", "\n", "Let's walk through a complete calculation step-by-step for debugging purposes.\n" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "================================================================================\n", "COMPLETE CALCULATION EXAMPLE: Pt Catalyst\n", "================================================================================\n", "\n", "Input Parameters:\n", " Full cell voltage (V_cell): 2.59 V\n", " Current density (j): 50 mA/cm²\n", "\n", "Experimental Conditions:\n", " Reference electrode potential: 0.23 V vs SHE\n", " Anode pH: 3\n", " Cathode pH: 12.5\n", " Anode measured potential vs reference: (interpolated from calibration data)\n", " Membrane loss: 0.1 V\n", " Geometric area: 4 cm²\n", " Nernst pH loss: 0.560 V\n", "\n", "================================================================================\n", "STEP-BY-STEP CALCULATION:\n", "================================================================================\n", "\n", "Step 1: Interpolate anode measured potential vs reference\n", " Calibration data: j = [50, 100, 200] mA/cm², anode_pot = [1.3, 1.35, 1.4] V\n", " For j = 50 mA/cm²:\n", " Interpolated anode_measured_potential_vs_ref = 1.3000 V\n", "\n", "Step 2: Convert anode measured potential to RHE\n", " V_anode_RHE = anode_measured_potential_vs_ref + ref_pot + 0.059 * anode_pH\n", " V_anode_RHE = 1.3000 + 0.23 + 0.059 * 3\n", " V_anode_RHE = 1.7070 V\n", "\n", "Step 3: Calculate cathode RHE (before IR correction)\n", " V_cathode_RHE = (V_anode_RHE + membrane_loss + Nern_pH_loss) - V_cell\n", " V_cathode_RHE = (1.7070 + 0.1 + 0.5605) - 2.59\n", " V_cathode_RHE = -0.2225 V\n", "\n", "Step 4: Interpolate cathode resistance\n", " Calibration data: j = [50, 100, 200] mA/cm², R = [0.48, 0.34, 0.3] Ω\n", " For j = 50 mA/cm²:\n", " Interpolated R = 0.4633 Ω\n", "\n", "Step 5: Apply IR correction\n", " IR_drop = (j / 1000) * R * A\n", " IR_drop = (50 / 1000) * 0.4633 * 4\n", " IR_drop = 0.0927 V\n", " V_cathode_RHE (final) = -0.2225 - 0.0927\n", " V_cathode_RHE (final) = -0.3152 V\n", "\n", "Step 6: Convert RHE to SHE\n", " V_cathode_SHE = V_cathode_RHE - (0.059 * cathode_pH)\n", " V_cathode_SHE = -0.3152 - (0.059 * 12.5)\n", " V_cathode_SHE = -1.0527 V\n", "\n", "================================================================================\n", "FINAL RESULTS:\n", "================================================================================\n", " Half-cell potential vs RHE: -0.3152 V\n", " Half-cell potential vs SHE: -1.0527 V\n", "\n", "Verification using fullcell2halfcell():\n", " vs RHE: -0.3152 V\n", " vs SHE: -1.0527 V\n", " ✓ Results match!\n", "================================================================================\n" ] } ], "source": [ "# Example: Pt catalyst\n", "# Full cell voltage: 2.59 V\n", "# Current density: 50 mA/cm²\n", "\n", "vcell = 2.59 # V\n", "current_density = 50 # mA/cm²\n", "\n", "print(\"=\" * 80)\n", "print(\"COMPLETE CALCULATION EXAMPLE: Pt Catalyst\")\n", "print(\"=\" * 80)\n", "print(f\"\\nInput Parameters:\")\n", "print(f\" Full cell voltage (V_cell): {vcell} V\")\n", "print(f\" Current density (j): {current_density} mA/cm²\")\n", "print(f\"\\nExperimental Conditions:\")\n", "print(f\" Reference electrode potential: {ref_pot} V vs SHE\")\n", "print(f\" Anode pH: {anode_pH}\")\n", "print(f\" Cathode pH: {cathode_pH}\")\n", "print(f\" Anode measured potential vs reference: (interpolated from calibration data)\")\n", "print(f\" Membrane loss: {membrane_loss} V\")\n", "print(f\" Geometric area: {geo_area} cm²\")\n", "print(f\" Nernst pH loss: {Nern_pH_loss:.3f} V\")\n", "\n", "print(f\"\\n\" + \"=\" * 80)\n", "print(\"STEP-BY-STEP CALCULATION:\")\n", "print(\"=\" * 80)\n", "\n", "# Step 1: Interpolate anode measured potential vs reference\n", "anode_measured_potential_vs_ref = interpolate_anode_potential_vs_ref(current_density)\n", "print(f\"\\nStep 1: Interpolate anode measured potential vs reference\")\n", "print(f\" Calibration data: j = [50, 100, 200] mA/cm², anode_pot = [1.3, 1.35, 1.4] V\")\n", "print(f\" For j = {current_density} mA/cm²:\")\n", "print(f\" Interpolated anode_measured_potential_vs_ref = {anode_measured_potential_vs_ref:.4f} V\")\n", "\n", "# Step 2: Convert anode measured potential to RHE\n", "v_anode_rhe = anode_measured_potential_vs_ref + ref_pot + 0.059 * anode_pH\n", "print(f\"\\nStep 2: Convert anode measured potential to RHE\")\n", "print(f\" V_anode_RHE = anode_measured_potential_vs_ref + ref_pot + 0.059 * anode_pH\")\n", "print(f\" V_anode_RHE = {anode_measured_potential_vs_ref:.4f} + {ref_pot} + 0.059 * {anode_pH}\")\n", "print(f\" V_anode_RHE = {v_anode_rhe:.4f} V\")\n", "\n", "# Step 3: Calculate cathode RHE with membrane and Nernst pH losses\n", "v_cathode_rhe_step2 = (v_anode_rhe + membrane_loss + Nern_pH_loss) - vcell\n", "print(f\"\\nStep 3: Calculate cathode RHE (before IR correction)\")\n", "print(f\" V_cathode_RHE = (V_anode_RHE + membrane_loss + Nern_pH_loss) - V_cell\")\n", "print(f\" V_cathode_RHE = ({v_anode_rhe:.4f} + {membrane_loss} + {Nern_pH_loss:.4f}) - {vcell}\")\n", "print(f\" V_cathode_RHE = {v_cathode_rhe_step2:.4f} V\")\n", "\n", "# Step 4: Interpolate resistance\n", "R = interpolate_cathode_R(current_density)\n", "print(f\"\\nStep 4: Interpolate cathode resistance\")\n", "print(f\" Calibration data: j = [50, 100, 200] mA/cm², R = [0.48, 0.34, 0.3] Ω\")\n", "print(f\" For j = {current_density} mA/cm²:\")\n", "print(f\" Interpolated R = {R:.4f} Ω\")\n", "\n", "# Step 5: Apply IR correction\n", "IR_drop = (current_density / 1000.0) * R * geo_area\n", "v_cathode_rhe = v_cathode_rhe_step2 - IR_drop\n", "print(f\"\\nStep 5: Apply IR correction\")\n", "print(f\" IR_drop = (j / 1000) * R * A\")\n", "print(f\" IR_drop = ({current_density} / 1000) * {R:.4f} * {geo_area}\")\n", "print(f\" IR_drop = {IR_drop:.4f} V\")\n", "print(f\" V_cathode_RHE (final) = {v_cathode_rhe_step2:.4f} - {IR_drop:.4f}\")\n", "print(f\" V_cathode_RHE (final) = {v_cathode_rhe:.4f} V\")\n", "\n", "# Step 6: Convert RHE to SHE\n", "v_cathode_she = rhe2she(v_cathode_rhe, cathode_pH, ref_pot)\n", "print(f\"\\nStep 6: Convert RHE to SHE\")\n", "print(f\" V_cathode_SHE = V_cathode_RHE - (0.059 * cathode_pH)\")\n", "print(f\" V_cathode_SHE = {v_cathode_rhe:.4f} - (0.059 * {cathode_pH})\")\n", "print(f\" V_cathode_SHE = {v_cathode_she:.4f} V\")\n", "\n", "print(f\"\\n\" + \"=\" * 80)\n", "print(\"FINAL RESULTS:\")\n", "print(\"=\" * 80)\n", "print(f\" Half-cell potential vs RHE: {v_cathode_rhe:.4f} V\")\n", "print(f\" Half-cell potential vs SHE: {v_cathode_she:.4f} V\")\n", "\n", "# Verify using the function\n", "ushe_func, urhe_func = fullcell2halfcell(vcell, current_density)\n", "print(f\"\\nVerification using fullcell2halfcell():\")\n", "print(f\" vs RHE: {urhe_func:.4f} V\")\n", "print(f\" vs SHE: {ushe_func:.4f} V\")\n", "print(f\" ✓ Results match!\")\n", "print(\"=\" * 80)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## References\n", "\n", "1. Arabyarmohammadi, F. et al. Voltage distribution within carbon dioxide reduction electrolysers. *Nature Sustainability* (2025) - https://www.nature.com/articles/s41893-025-01643-4\n", "\n", "2. This methodology follows established protocols for accurate half-cell potential determination in CO₂ reduction electrolyzers.\n" ] } ], "metadata": { "kernelspec": { "display_name": "base", "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.7" } }, "nbformat": 4, "nbformat_minor": 2 }