{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "a39d7464-a111-4ac6-80b0-bcaa20aa81fa",
   "metadata": {},
   "source": [
    "## Imports"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 385,
   "id": "0dbb86a3-8915-43d6-b821-379ab76b2849",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "import matplotlib.pyplot as plt\n",
    "import scipy.optimize\n",
    "from scipy.signal import medfilt\n",
    "from scipy.optimize import curve_fit\n",
    "\n",
    "import warnings"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "79273c57-b22e-4b9b-ad97-8b788969a4a6",
   "metadata": {},
   "source": [
    "## Data Loading"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 386,
   "id": "9a43783f-d1bb-4c79-ae93-843086a00a26",
   "metadata": {},
   "outputs": [],
   "source": [
    "# --- Data Loading ---\n",
    "\n",
    "def lpr_cor_import(filename):\n",
    "    \"\"\" Import cor file as pandas dataframe.\"\"\"\n",
    "\n",
    "    try: \n",
    "        df = pd.read_csv(\n",
    "            filename,\n",
    "            skiprows=26,\n",
    "            sep='\\s+',\n",
    "            header=None,\n",
    "            names=[\"E\", \"i\", \"T\"],\n",
    "        ) \n",
    "        df.drop(columns=[\"T\"], inplace=True)\n",
    "        df.drop(df.head(150).index, inplace=True)\n",
    "        df[\"E\"] = df[\"E\"]*1e3 # Convert it to mV\n",
    "        \n",
    "    except FileNotFoundError as e:\n",
    "        print(f\"Error: File was not found.\")\n",
    "        print(e.message)\n",
    "        print(e.args)\n",
    "        return None\n",
    "    except Exception as e:\n",
    "        print(f\"Error reading the CSV file: {e}\")\n",
    "        return None   \n",
    "    else:\n",
    "        return df"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 387,
   "id": "6a2888b4-e151-4fae-97d5-7de521fb7b48",
   "metadata": {},
   "outputs": [],
   "source": [
    "HS1_1 = lpr_cor_import(\"HIPed_Stellite1_LPR/LPR_1.cor\")\n",
    "HS1_2 = lpr_cor_import(\"HIPed_Stellite1_LPR/LPR_2.cor\")\n",
    "HS1_3 = lpr_cor_import(\"HIPed_Stellite1_LPR/LPR_3.cor\")\n",
    "HS1_4 = lpr_cor_import(\"HIPed_Stellite1_LPR/LPR_4.cor\")\n",
    "HS1_5 = lpr_cor_import(\"HIPed_Stellite1_LPR/LPR_5.cor\")\n",
    "HS1_6 = lpr_cor_import(\"HIPed_Stellite1_LPR/LPR_6.cor\")\n",
    "\n",
    "# Keep it in the same cell to keep df reproducible, even if Vi clicks it multiple times\n",
    "area = 2 #cm^2\n",
    "\n",
    "for df in [HS1_1, HS1_2, HS1_3, HS1_4, HS1_5, HS1_6]:\n",
    "    df[\"i\"] = np.abs(df[\"i\"]/area) # Current density\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 388,
   "id": "0ad5c189-9bd6-4291-bdee-8b75fdd5a3cd",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>E</th>\n",
       "      <th>i</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>150</th>\n",
       "      <td>-205.357</td>\n",
       "      <td>4.661220e-11</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>151</th>\n",
       "      <td>-205.347</td>\n",
       "      <td>4.660095e-11</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>152</th>\n",
       "      <td>-205.337</td>\n",
       "      <td>4.661040e-11</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>153</th>\n",
       "      <td>-205.327</td>\n",
       "      <td>4.662150e-11</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>154</th>\n",
       "      <td>-205.317</td>\n",
       "      <td>4.661900e-11</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>...</th>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3999</th>\n",
       "      <td>-166.870</td>\n",
       "      <td>7.752900e-11</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4000</th>\n",
       "      <td>-166.854</td>\n",
       "      <td>7.931000e-11</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4001</th>\n",
       "      <td>-166.857</td>\n",
       "      <td>7.781400e-11</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4002</th>\n",
       "      <td>-166.850</td>\n",
       "      <td>7.296700e-11</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4003</th>\n",
       "      <td>-166.846</td>\n",
       "      <td>6.987300e-11</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>3854 rows × 2 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "            E             i\n",
       "150  -205.357  4.661220e-11\n",
       "151  -205.347  4.660095e-11\n",
       "152  -205.337  4.661040e-11\n",
       "153  -205.327  4.662150e-11\n",
       "154  -205.317  4.661900e-11\n",
       "...       ...           ...\n",
       "3999 -166.870  7.752900e-11\n",
       "4000 -166.854  7.931000e-11\n",
       "4001 -166.857  7.781400e-11\n",
       "4002 -166.850  7.296700e-11\n",
       "4003 -166.846  6.987300e-11\n",
       "\n",
       "[3854 rows x 2 columns]"
      ]
     },
     "execution_count": 388,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "HS1_1 "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ee328bd6-4817-41a9-8831-12cda31cb8f1",
   "metadata": {},
   "source": [
    "# Coding part!\n",
    "\n",
    "Separate code into analyze and plotting, with all answers in a dict.\n",
    "Can't believe it took me this long to really do it this way :|"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bec9f9bc-b531-470e-9c27-a89b512e97ba",
   "metadata": {},
   "source": [
    "## Analyze Tafel Data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 389,
   "id": "d87b07b6-f893-4d5f-b06a-09643d170939",
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "\n",
    "def analyze_tafel_data(\n",
    "    data: pd.DataFrame\n",
    ") -> dict | None:\n",
    "\n",
    "    INITIAL_KERNEL = 29\n",
    "    FIT_KERNEL = 99\n",
    "    \n",
    "    # 1. Estimate Corrosion Potential (Ecorr) using a median filter\n",
    "    ecorr_guess = data['E'].iloc[np.argmin(medfilt(data['i'], kernel_size=INITIAL_KERNEL))]\n",
    "\n",
    "    # 2. Split data into anodic and cathodic regions based on Ecorr\n",
    "    anodic_data = data[data['E'] > ecorr_guess]\n",
    "    cathodic_data = data[data['E'] < ecorr_guess]\n",
    "    if anodic_data.empty or cathodic_data.empty:\n",
    "        warnings.warn(\"No data in anodic or cathodic regions?. Ecorr guess may be incorrect.\")    \n",
    "    \n",
    "    # 3. Perform linear fit on E vs. i for each branch\n",
    "    medfilt_anodic_i = medfilt(anodic_data['i'].iloc[30:-90], kernel_size=FIT_KERNEL)\n",
    "    p_anodic = np.poly1d(np.polyfit(medfilt_anodic_i, anodic_data['E'].iloc[30:-90], 1))\n",
    "    medfilt_cathodic_i = medfilt(cathodic_data['i'].iloc[30:-90], kernel_size=FIT_KERNEL)\n",
    "    p_cathodic = np.poly1d(np.polyfit(medfilt_cathodic_i, cathodic_data['E'].iloc[30:-90], 1))\n",
    "    \n",
    "    # 4. Calculate the tangent at the extreme ends of the filtered data\n",
    "    i_extreme_anodic = medfilt_anodic_i.max()\n",
    "    e_extreme_anodic = p_anodic(i_extreme_anodic)\n",
    "    tangent_slope_anodic = p_anodic.coeffs[0] * (i_extreme_anodic * np.log(10))\n",
    "\n",
    "    i_extreme_cathodic = medfilt_cathodic_i.max()\n",
    "    e_extreme_cathodic = p_cathodic(i_extreme_cathodic)\n",
    "    tangent_slope_cathodic = p_cathodic.coeffs[0] * (i_extreme_cathodic * np.log(10))\n",
    "\n",
    "    print(f\"tangent_slope_anodic   {tangent_slope_anodic}\")\n",
    "    print(f\"tangent_slope_cathodic {tangent_slope_cathodic}\")\n",
    "    print(f\"p_anodic   {p_anodic}\")\n",
    "    print(f\"p_cathodic {p_cathodic}\")\n",
    "\n",
    "          \n",
    "    # 5. Calculate tangent lines    \n",
    "    log_i_extreme_anodic = np.log10(i_extreme_anodic)\n",
    "    log_i_tangent_anodic = np.linspace(log_i_extreme_anodic - 0.5, log_i_extreme_anodic, 100)\n",
    "    e_tangent_anodic = (\n",
    "        tangent_slope_anodic * (log_i_tangent_anodic - log_i_extreme_anodic)\n",
    "        + e_extreme_anodic\n",
    "    )\n",
    "\n",
    "    log_i_extreme_cathodic = np.log10(i_extreme_cathodic)\n",
    "    log_i_tangent_cathodic = np.linspace(log_i_extreme_cathodic - 0.5, log_i_extreme_cathodic, 100)\n",
    "    e_tangent_cathodic = (\n",
    "        tangent_slope_cathodic * (log_i_tangent_cathodic - log_i_extreme_cathodic)\n",
    "        + e_extreme_cathodic\n",
    "    )\n",
    "\n",
    "\n",
    "    # --- 3. Calculations ---\n",
    "    temp_kelvin = 25.0 + 273.15\n",
    "    R = 8.314  # Ideal Gas Constant (J/(mol·K))\n",
    "    F = 96485  # Faraday Constant (C/mol)\n",
    "    n = 2 # Number of electrons transferred in your electrochemical reaction.    \n",
    "    \n",
    "    # Calculate the anodic charge transfer coefficient (alpha_a)\n",
    "    # This value represents the fraction of the potential that assists the oxidation reaction.\n",
    "    try:\n",
    "        alpha_a = (2.303 * R * temp_kelvin) / (1e-3 * tangent_slope_anodic * n * F)\n",
    "        alpha_c = -(2.303 * R * temp_kelvin) / (1e-3 * tangent_slope_cathodic * n * F)    \n",
    "    except ZeroDivisionError as e:\n",
    "        print(\"Slope is zero, what happened?\")\n",
    "        print(e)\n",
    "  \n",
    "    \n",
    "    # 6. Package results into a dictionary\n",
    "    results = {\n",
    "        \"ecorr_guess\": ecorr_guess,\n",
    "        \"beta_a\": tangent_slope_anodic,        \n",
    "        \"beta_c\": tangent_slope_cathodic,\n",
    "        \"alpha_a\": alpha_a,        \n",
    "        \"alpha_c\": alpha_c,        \n",
    "        \"anodic_tangent_i\": 10**log_i_tangent_anodic,\n",
    "        \"anodic_tangent_E\": e_tangent_anodic,\n",
    "        \"cathodic_tangent_i\": 10**log_i_tangent_cathodic,\n",
    "        \"cathodic_tangent_E\": e_tangent_cathodic,   \n",
    "    }\n",
    "    return results"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "967c80c8-6271-405d-98e6-f2699a657fc5",
   "metadata": {},
   "source": [
    "## Plotting code\n",
    "\n",
    "Plot the additional stuff only if you have results.\n",
    "Use the same function for everything\n",
    "Again, why didn't I do this earlier :|\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 390,
   "id": "a49005dc-a2b9-4cf1-9ad8-e4f549bda1fd",
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_tafel(data: pd.DataFrame, results: dict):\n",
    "    \"\"\"\n",
    "    Generates and displays a Tafel plot from the data and analysis results.\n",
    "\n",
    "    Args:\n",
    "        data (pd.DataFrame): The original experimental data.\n",
    "        results (dict): The dictionary returned by analyze_tafel_data.\n",
    "    \"\"\"\n",
    "    fig, ax = plt.subplots(figsize=(8, 4), dpi=150)\n",
    "\n",
    "    # Plot the raw experimental data\n",
    "    ax.semilogx(\n",
    "        np.abs(data[\"i\"].to_numpy()), data[\"E\"].to_numpy(),\n",
    "        marker='o', linestyle='', markersize=3, alpha=0.6, label=\"Experimental Data\"\n",
    "    )\n",
    "\n",
    "    # Plot the analysis results if available\n",
    "    if results:\n",
    "        \n",
    "        ax.axhline(  # Ecorr line\n",
    "            y=results[\"ecorr_guess\"],\n",
    "            color='green', linestyle='--', linewidth=2,\n",
    "            label=f'$E_{{corr}}$ = {results[\"ecorr_guess\"]:.2f} mV'\n",
    "        )\n",
    "        \n",
    "        ax.plot(     # Anodic tangent line\n",
    "            results[\"anodic_tangent_i\"], results[\"anodic_tangent_E\"],\n",
    "            color='red', linestyle='-.', linewidth=2,\n",
    "            label=f'Anodic Tangent {results[\"beta_a\"]:.2f}'\n",
    "        )\n",
    "        \n",
    "        ax.plot(     # Cathodic tangent line\n",
    "            results[\"cathodic_tangent_i\"], results[\"cathodic_tangent_E\"],\n",
    "            color='blue', linestyle='-.', linewidth=2,\n",
    "            label=f'Cathodic Tangent {results[\"beta_c\"]:.2f}'\n",
    "        )\n",
    "\n",
    "        bx = ax.twinx()  \n",
    "        ymin, ymax = ax.get_ylim()\n",
    "        bx.set_ylim(ymin-results[\"ecorr_guess\"], ymax-results[\"ecorr_guess\"])\n",
    "        bx.set_ylabel(\"Overpotential (mV)\")\n",
    "    \n",
    "    # Configure plot aesthetics\n",
    "    ax.set_xlabel(\"Current (A)\")\n",
    "    ax.set_ylabel(\"Potential (mV)\")\n",
    "    ax.legend()\n",
    "    ax.grid(True, which=\"both\", ls=\"--\", linewidth=0.5)\n",
    "    plt.tight_layout()\n",
    "    plt.show()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d83b0f6a-2961-4381-941f-2eda09ab713c",
   "metadata": {},
   "source": [
    "## Funspace"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 391,
   "id": "2f0f4d93-90bd-4ea0-b481-5d2d0c0d35c5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tangent_slope_anodic   36.935982391954994\n",
      "tangent_slope_cathodic -51.83557582444677\n",
      "p_anodic    \n",
      "2.397e+11 x - 184\n",
      "p_cathodic  \n",
      "-5.013e+11 x - 182\n",
      "alpha_a 0.8009387773094865\n",
      "alpha_c 0.5707173134514494\n",
      "1.3716560907609359\n",
      "\n",
      "tangent_slope_anodic   32.33609922748971\n",
      "tangent_slope_cathodic -54.09410507385739\n",
      "p_anodic    \n",
      "2.421e+11 x - 182.5\n",
      "p_cathodic  \n",
      "-5.052e+11 x - 181.2\n",
      "alpha_a 0.9148741277546404\n",
      "alpha_c 0.5468888067441983\n",
      "1.4617629344988385\n",
      "\n",
      "tangent_slope_anodic   33.64593158958795\n",
      "tangent_slope_cathodic -52.384158766821365\n",
      "p_anodic    \n",
      "2.375e+11 x - 182.9\n",
      "p_cathodic  \n",
      "-5.358e+11 x - 181.2\n",
      "alpha_a 0.8792581800556247\n",
      "alpha_c 0.5647405870813464\n",
      "1.4439987671369712\n",
      "\n",
      "tangent_slope_anodic   36.99409502392016\n",
      "tangent_slope_cathodic -48.72756466526758\n",
      "p_anodic    \n",
      "2.566e+11 x - 184.8\n",
      "p_cathodic  \n",
      "-5.508e+11 x - 182.9\n",
      "alpha_a 0.7996806127196425\n",
      "alpha_c 0.6071196206697334\n",
      "1.4068002333893759\n",
      "\n",
      "tangent_slope_anodic   39.037818290543065\n",
      "tangent_slope_cathodic -48.123338398882495\n",
      "p_anodic    \n",
      "2.673e+11 x - 185.4\n",
      "p_cathodic  \n",
      "-5.483e+11 x - 183.3\n",
      "alpha_a 0.7578154177459187\n",
      "alpha_c 0.6147424837929392\n",
      "1.3725579015388578\n",
      "\n",
      "tangent_slope_anodic   41.082268537703634\n",
      "tangent_slope_cathodic -45.85765496987607\n",
      "p_anodic    \n",
      "2.856e+11 x - 185.9\n",
      "p_cathodic  \n",
      "-5.625e+11 x - 184\n",
      "alpha_a 0.7201028966690741\n",
      "alpha_c 0.6451149888752609\n",
      "1.365217885544335\n",
      "\n",
      "tangent_slope_anodic   36.935982391954994\n",
      "tangent_slope_cathodic -51.83557582444677\n",
      "p_anodic    \n",
      "2.397e+11 x - 184\n",
      "p_cathodic  \n",
      "-5.013e+11 x - 182\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 157796 characters]",
      "text/plain": [
       "<Figure size 1200x600 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# =============================================================================\n",
    "# --- Main Execution Block ---\n",
    "# =============================================================================\n",
    "\n",
    "# 1. Analyze the dataset\n",
    "for HS1_df in [HS1_1, HS1_2, HS1_3, HS1_4, HS1_5, HS1_6]:\n",
    "    analysis_results = analyze_tafel_data(HS1_df)\n",
    "    print(f\"alpha_a {analysis_results['alpha_a']}\")\n",
    "    print(f\"alpha_c {analysis_results['alpha_c']}\")\n",
    "    print(analysis_results['alpha_a'] + analysis_results['alpha_c'])\n",
    "    print()\n",
    "\n",
    "analysis_results = analyze_tafel_data(HS1_1)\n",
    "\n",
    "# 2. Plot the results\n",
    "if analysis_results:\n",
    "    plot_tafel(HS1_1, analysis_results)\n",
    "else:\n",
    "    print(\"Analysis could not be completed.\")\n",
    "\n",
    "#print(analysis_results)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7a6fb1f0-efb2-4c23-83d7-1079955624a7",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cf05eadf-1b38-403c-854f-59cd2a3a8fde",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dab79e32-56d9-41b1-a328-c734f66c1696",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b712f813-bc16-4453-8899-bcc00a70b575",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b1ec6441-2655-49e7-bc9d-89caabd23ab0",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "14fc70e9-8a28-4226-9c09-0a75e069e425",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "313e9a4f-434b-4925-adcb-582819569a3d",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f6c4f357-a19b-4f35-93d5-7cd3dd9d9635",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "989d4d9d-0c51-471f-98d5-473beba53f82",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "d882a7cb-c89d-4bc8-a041-ad1defd2d03b",
   "metadata": {},
   "source": [
    "# Test space"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 426,
   "id": "b0371e43-b1ce-4181-a08e-93626934a938",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "14.303656914399205 15.62901295498541\n",
      "alpha_a 0.52\n",
      "alpha_c 0.47\n",
      "0.9902749736726397\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 140716 characters]",
      "text/plain": [
       "<Figure size 1200x600 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def plot_tafel(data: pd.DataFrame):\n",
    "\n",
    "    \n",
    "    fig, ax = plt.subplots(figsize=(8, 4), dpi=150)\n",
    "\n",
    "    # Plot the raw experimental data\n",
    "    ax.semilogx(\n",
    "        np.abs(data[\"i\"].to_numpy()), data[\"E\"].to_numpy(),\n",
    "        marker='o', linestyle='', markersize=3, alpha=0.6, label=\"Experimental Data\"\n",
    "    )\n",
    "\n",
    "    INITIAL_KERNEL = 29\n",
    "    FIT_KERNEL = 99\n",
    "\n",
    "    # 1. Estimate Corrosion Potential (Ecorr) using a median filter\n",
    "    ecorr_guess = data['E'].iloc[np.argmin(medfilt(data['i'], kernel_size=INITIAL_KERNEL))]\n",
    "\n",
    "    ax.axhline(  # Ecorr line\n",
    "        y=ecorr_guess,\n",
    "        color='green', linestyle='--', linewidth=1,\n",
    "        label=f'$E_{{corr}}$ = {results[\"ecorr_guess\"]:.2f} mV'\n",
    "    )\n",
    "    ax.set_xlabel(\"Current (A)\")\n",
    "    ax.set_ylabel(\"Potential (mV)\")\n",
    "\n",
    "    bx = ax.twinx()  \n",
    "    ymin, ymax = ax.get_ylim()\n",
    "    bx.set_ylim(ymin-results[\"ecorr_guess\"], ymax-results[\"ecorr_guess\"])\n",
    "    bx.set_ylabel(\"Overpotential (mV)\")    \n",
    "\n",
    "    # 2. Split data into anodic and cathodic regions based on Ecorr\n",
    "    anodic_data = data[data['E'] > ecorr_guess + 10].copy()\n",
    "    cathodic_data = data[data['E'] < ecorr_guess - 10].copy()\n",
    "    #anodic_data = anodic_data\n",
    "    \n",
    "    anodic_data[\"i\"] = medfilt(anodic_data['i'], kernel_size=FIT_KERNEL)\n",
    "    cathodic_data[\"i\"] = medfilt(cathodic_data['i'], kernel_size=FIT_KERNEL)\n",
    "    \n",
    "    if anodic_data.empty or cathodic_data.empty:\n",
    "        warnings.warn(\"No data in anodic or cathodic regions?. Ecorr guess may be incorrect.\")    \n",
    "\n",
    "    p_anodic = np.poly1d(np.polyfit(np.log(anodic_data[\"i\"]), anodic_data['E'], 1))\n",
    "    p_cathodic = np.poly1d(np.polyfit(np.log(cathodic_data[\"i\"]), cathodic_data['E'], 1))\n",
    "    \n",
    "    anodic_x = np.linspace(anodic_data[\"i\"].max(), 10**-11, 100)\n",
    "    cathodic_x = np.linspace(cathodic_data[\"i\"].max(), 10**-11, 100)\n",
    "    \n",
    "    beta_a = p_anodic[1]\n",
    "    beta_c = -p_cathodic[1]\n",
    "\n",
    "    print(f\"{beta_a} {beta_c}\")\n",
    "    \n",
    "    ax.plot(anodic_x, p_anodic(np.log(anodic_x)), '-', markersize=1)\n",
    "    ax.plot(cathodic_x, p_cathodic(np.log(cathodic_x)), '-', markersize=1)\n",
    "\n",
    "    # --- 3. Calculations ---\n",
    "    temp_kelvin = 25.0 + 273.15\n",
    "    R = 8.314  # Ideal Gas Constant (J/(mol·K))\n",
    "    F = 96485  # Faraday Constant (C/mol)\n",
    "    n = 8 # Number of electrons transferred in your electrochemical reaction.    \n",
    "    \n",
    "    # Calculate the anodic charge transfer coefficient (alpha_a)\n",
    "    # This value represents the fraction of the potential that assists the oxidation reaction.\n",
    "    try:\n",
    "        alpha_a = (2.303 * R * temp_kelvin) / (1e-3 * beta_a * n * F)\n",
    "        alpha_c = (2.303 * R * temp_kelvin) / (1e-3 * beta_c * n * F)    \n",
    "    except ZeroDivisionError as e:\n",
    "        print(\"Slope is zero, what happened?\")\n",
    "        print(e)\n",
    "      \n",
    "    print(f\"alpha_a {alpha_a:.2f}\")\n",
    "    print(f\"alpha_c {alpha_c:.2f}\")\n",
    "    print(alpha_a + alpha_c)\n",
    "    \n",
    "    # 3. Perform linear fit on E vs. i for each branch\n",
    "    #medfilt_anodic_i = medfilt(anodic_data['i'].iloc[30:-90], kernel_size=FIT_KERNEL)\n",
    "    #medfilt_cathodic_i = medfilt(cathodic_data['i'].iloc[30:-90], kernel_size=FIT_KERNEL)\n",
    "    #i_extreme_anodic = medfilt_anodic_i.max()\n",
    "    #i_extreme_cathodic = medfilt_cathodic_i.max()\n",
    "\n",
    "\n",
    "    \n",
    "    \n",
    "    # Configure plot aesthetics\n",
    "\n",
    "    ax.legend()\n",
    "    ax.grid(True, which=\"both\", ls=\"--\", linewidth=0.5)\n",
    "    plt.tight_layout()\n",
    "    plt.show()\n",
    "\n",
    "plot_tafel(HS1_6)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e564beda-3ec1-4613-8dde-50d3986f5cfd",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b25ef827-dfb6-44d0-8633-e4255ba4f047",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b38675d9-8a13-47b0-87f9-f8732612a096",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 427,
   "id": "110a6031-ea23-46cf-8f1a-7c39ec6d88b9",
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "from scipy.signal import medfilt\n",
    "import warnings\n",
    "\n",
    "def analyze_tafel(data: pd.DataFrame) -> dict:\n",
    "    \"\"\"\n",
    "    Analyzes Tafel data to determine electrochemical parameters.\n",
    "\n",
    "    Args:\n",
    "        data: DataFrame with 'E' (potential) and 'i' (current) columns.\n",
    "\n",
    "    Returns:\n",
    "        A dictionary containing the analysis results.\n",
    "    \"\"\"\n",
    "    INITIAL_KERNEL = 29\n",
    "    FIT_KERNEL = 99\n",
    "\n",
    "    # 1. Estimate Corrosion Potential (Ecorr) using a median filter\n",
    "    ecorr_guess = data['E'].iloc[np.argmin(medfilt(data['i'], kernel_size=INITIAL_KERNEL))]\n",
    "\n",
    "    # 2. Split data into anodic and cathodic regions based on Ecorr\n",
    "    anodic_data = data[data['E'] > ecorr_guess + 10].copy()\n",
    "    cathodic_data = data[data['E'] < ecorr_guess - 10].copy()\n",
    "\n",
    "    if anodic_data.empty or cathodic_data.empty:\n",
    "        warnings.warn(\"No data in anodic or cathodic regions. Ecorr guess may be incorrect.\")\n",
    "        return {}\n",
    "\n",
    "    anodic_data[\"i\"] = medfilt(anodic_data['i'], kernel_size=FIT_KERNEL)\n",
    "    cathodic_data[\"i\"] = medfilt(cathodic_data['i'], kernel_size=FIT_KERNEL)\n",
    "\n",
    "    # 3. Perform linear fit on E vs. log(i) for each branch\n",
    "    p_anodic = np.poly1d(np.polyfit(np.log(anodic_data[\"i\"]), anodic_data['E'], 1))\n",
    "    p_cathodic = np.poly1d(np.polyfit(np.log(cathodic_data[\"i\"]), cathodic_data['E'], 1))\n",
    "\n",
    "    beta_a = p_anodic[1]\n",
    "    beta_c = -p_cathodic[1]\n",
    "\n",
    "    # --- 4. Calculations ---\n",
    "    temp_kelvin = 25.0 + 273.15\n",
    "    R = 8.314  # Ideal Gas Constant (J/(mol·K))\n",
    "    F = 96485  # Faraday Constant (C/mol)\n",
    "    n = 8      # Number of electrons transferred\n",
    "\n",
    "    alpha_a, alpha_c = None, None\n",
    "    try:\n",
    "        # Calculate charge transfer coefficients\n",
    "        alpha_a = (2.303 * R * temp_kelvin) / (1e-3 * beta_a * n * F)\n",
    "        alpha_c = (2.303 * R * temp_kelvin) / (1e-3 * beta_c * n * F)\n",
    "    except ZeroDivisionError as e:\n",
    "        print(f\"Error during calculation: {e}. A slope was zero.\")\n",
    "\n",
    "    return {\n",
    "        \"ecorr_guess\": ecorr_guess,\n",
    "        \"p_anodic\": p_anodic,\n",
    "        \"p_cathodic\": p_cathodic,\n",
    "        \"anodic_data\": anodic_data,\n",
    "        \"cathodic_data\": cathodic_data,\n",
    "        \"beta_a\": beta_a,\n",
    "        \"beta_c\": beta_c,\n",
    "        \"alpha_a\": alpha_a,\n",
    "        \"alpha_c\": alpha_c\n",
    "    }"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 428,
   "id": "56eba50b-b152-4033-af25-5ec5f2bbb80c",
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "def plot_tafel(data: pd.DataFrame, results: dict):\n",
    "    \"\"\"\n",
    "    Generates a Tafel plot from experimental data and analysis results.\n",
    "\n",
    "    Args:\n",
    "        data: The original DataFrame with 'E' and 'i' columns.\n",
    "        results: A dictionary from the analyze_tafel function.\n",
    "    \"\"\"\n",
    "    if not results:\n",
    "        print(\"Analysis results are empty. Cannot generate plot.\")\n",
    "        return\n",
    "\n",
    "    fig, ax = plt.subplots(figsize=(8, 4), dpi=150)\n",
    "\n",
    "    # Plot the raw experimental data\n",
    "    ax.semilogx(\n",
    "        np.abs(data[\"i\"].to_numpy()), data[\"E\"].to_numpy(),\n",
    "        marker='o', linestyle='', markersize=3, alpha=0.6, label=\"Experimental Data\"\n",
    "    )\n",
    "\n",
    "    # Ecorr line\n",
    "    ax.axhline(\n",
    "        y=results[\"ecorr_guess\"],\n",
    "        color='green', linestyle='--', linewidth=1,\n",
    "        label=f'$E_{{corr}}$ = {results[\"ecorr_guess\"]:.2f} mV'\n",
    "    )\n",
    "\n",
    "    # Plot Tafel fits\n",
    "    anodic_x = np.linspace(results[\"anodic_data\"][\"i\"].min(), results[\"anodic_data\"][\"i\"].max(), 100)\n",
    "    cathodic_x = np.linspace(results[\"cathodic_data\"][\"i\"].min(), results[\"cathodic_data\"][\"i\"].max(), 100)\n",
    "\n",
    "    ax.plot(anodic_x, results[\"p_anodic\"](np.log(anodic_x)), '-', markersize=1, label=f'Anodic Fit (βa = {results[\"beta_a\"]:.2f})')\n",
    "    ax.plot(cathodic_x, results[\"p_cathodic\"](np.log(cathodic_x)), '-', markersize=1, label=f'Cathodic Fit (βc = {results[\"beta_c\"]:.2f})')\n",
    "\n",
    "    # Configure plot aesthetics\n",
    "    ax.set_xlabel(\"Current (A)\")\n",
    "    ax.set_ylabel(\"Potential (mV)\")\n",
    "    ax.legend()\n",
    "    ax.grid(True, which=\"both\", ls=\"--\", linewidth=0.5)\n",
    "\n",
    "    # Create a twin axis for overpotential\n",
    "    bx = ax.twinx()\n",
    "    ymin, ymax = ax.get_ylim()\n",
    "    bx.set_ylim(ymin - results[\"ecorr_guess\"], ymax - results[\"ecorr_guess\"])\n",
    "    bx.set_ylabel(\"Overpotential (mV)\")\n",
    "\n",
    "    plt.tight_layout()\n",
    "    plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 429,
   "id": "0651c767-a244-4649-8a82-fcda97ade5b0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "alpha_a: 0.52\n",
      "alpha_c: 0.47\n",
      "Sum of alphas: 0.99\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 146936 characters]",
      "text/plain": [
       "<Figure size 1200x600 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Assuming 'HS1_6' is your DataFrame loaded with the experimental data\n",
    "# For example:\n",
    "# data = {'E': np.linspace(0, 1, 100), 'i': np.logspace(-6, -2, 100)}\n",
    "# HS1_6 = pd.DataFrame(data)\n",
    "\n",
    "# 1. Analyze the data\n",
    "analysis_results = analyze_tafel(HS1_6)\n",
    "\n",
    "# Print the calculated alpha values\n",
    "if analysis_results:\n",
    "    print(f\"alpha_a: {analysis_results['alpha_a']:.2f}\")\n",
    "    print(f\"alpha_c: {analysis_results['alpha_c']:.2f}\")\n",
    "    if analysis_results['alpha_a'] is not None and analysis_results['alpha_c'] is not None:\n",
    "        print(f\"Sum of alphas: {analysis_results['alpha_a'] + analysis_results['alpha_c']:.2f}\")\n",
    "\n",
    "\n",
    "# 2. Plot the data and the analysis\n",
    "plot_tafel(HS1_6, analysis_results)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2f13865b-557a-4fdf-b3d4-a8ffa5c2c8e7",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "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.10.16"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
