{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "359edeb3",
   "metadata": {},
   "source": [
    "# DandeLiion API - PyBaMM comparison"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "62b21eed-fcab-4ad8-be9a-438dde08af5d",
   "metadata": {},
   "outputs": [],
   "source": [
    "import dandeliion.client as dandeliion\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "import pybamm\n",
    "from pybamm import Experiment\n",
    "\n",
    "# The following lines enable logging output in this notebook\n",
    "import sys\n",
    "import logging\n",
    "logging.basicConfig(stream=sys.stdout, level=logging.INFO)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "8501c809-cd90-42cc-9917-8068dea62404",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'1.0.0rc2'"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dandeliion.__version__"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6b38282b-c158-4def-bc9a-f5d565e82299",
   "metadata": {},
   "source": [
    "### Authentication\n",
    "Define the simulator object by providing the API URL and API token:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "424cfefd-19b2-4e2a-83dd-a306ead4ff98",
   "metadata": {},
   "outputs": [],
   "source": [
    "api_url = \"https://api.dandeliion.com/v1\"\n",
    "api_key = \"f1543b9ff2001d04bd2c002d9375c8a28ad0bcac9914ce5d8f0e0b98ef4404cf\"\n",
    "simulator = dandeliion.Simulator(api_url, api_key)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ec0b6b20-bf68-4e00-bcb9-49f6987b47bd",
   "metadata": {},
   "source": [
    "### Battery parameters\n",
    "Can be a valid BPX file name/path or already read-in valid BPX as a `dict` or `BPX` object itself:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "8fe2f5ef-1c2d-4259-8e45-ce592a122b91",
   "metadata": {},
   "outputs": [],
   "source": [
    "# params = '2025-04-10-parameters_Dandeliion_example.json'\n",
    "params = 'AE_gen1_BPX.json'"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "251b4538-abf6-438b-b2f4-ae0b6b433378",
   "metadata": {},
   "source": [
    "### Experiment\n",
    "PyBaMM `Experiment` object, for example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "041b9b60-c797-47c8-982c-6f78b3526929",
   "metadata": {},
   "outputs": [],
   "source": [
    "experiment = Experiment(\n",
    "  [\n",
    "      \"Discharge at C/2 for 10 hours or until 2.5 V\"  # Discharging at 1C crashes PyBaMM\n",
    "  ],\n",
    "  period = \"10 s\",\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "abb95e28-8293-49b4-87ed-6739f39ca65a",
   "metadata": {},
   "source": [
    "### Additional model/backend-specific parameters\n",
    "Define a dictionary of extra parameters outside the BPX standard, such as mesh, initial state of charge or voltage, etc.:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "55c1d753-029a-44e6-8a6c-5fdf34a2a1b1",
   "metadata": {},
   "outputs": [],
   "source": [
    "extra_params = {}\n",
    "\n",
    "is_isothermal = True\n",
    "\n",
    "# Number of mesh points in PyBaMM format (default is 16 mesh points everywhere):\n",
    "extra_params['Mesh'] = {\"x_n\": 32, \"x_s\": 16, \"x_p\": 32, \"r_n\": 32, \"r_p\": 32}\n",
    "\n",
    "# Initial state of charge (default is 1):\n",
    "extra_params['Initial SOC'] = 1.0"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "709bd5a4-f0b0-40c5-95d9-88d3aa35438a",
   "metadata": {},
   "source": [
    "### Start the simulation in the cloud"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "d22442eb-c0bf-41bc-b9ee-25d19800d1d0",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/ivan/workspace/dandeliion-client/.venv-pip/lib/python3.10/site-packages/pydantic/_internal/_decorators_v1.py:171: UserWarning: The minimum voltage computed from the STO limits (2.4988861558265327 V) is less than the lower voltage cut-off (2.5 V) with the absolute tolerance v_tol = 0.001 V\n",
      "  model_dict_new = validator(model_dict)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:dandeliion.client.simulator:[running] | \n",
      "INFO:dandeliion.client.simulator:[success] | [2025-05-09 17:53:46.007] [info] Total time: 3.904 s\n",
      "Solution status: success\n"
     ]
    }
   ],
   "source": [
    "solution = dandeliion.solve(\n",
    "    simulator=simulator,\n",
    "    params=params,\n",
    "    experiment=experiment,      # optional (1C discharge for 1 hour or until the `Lower voltage cut-off [V]` from BPX will be used by default)\n",
    "    extra_params=extra_params,  # optional (initial conditions from the BPX, fully charged state, and single cell Newman model with constant temperature will be used by default)\n",
    "    is_blocking=True            # optional (`True` by default)\n",
    ")\n",
    "\n",
    "print(\"Solution status:\", solution.status)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9c3f60ea",
   "metadata": {},
   "source": [
    "### Start PyBaMM simulation with the same parameters"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "f39c43eb",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "PyBaMM 25.1.1 simulation...\n",
      "\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/ivan/workspace/dandeliion-client/.venv-pip/lib/python3.10/site-packages/pybamm/parameters/parameter_values.py:163: UserWarning: 'Open-circuit voltage at 0% SOC [V]' not found in BPX file. Using 'Lower voltage cut-off [V]'.\n",
      "  return ParameterValues._create_from_bpx(bpx, target_soc)\n",
      "/home/ivan/workspace/dandeliion-client/.venv-pip/lib/python3.10/site-packages/pybamm/parameters/parameter_values.py:163: UserWarning: 'Open-circuit voltage at 100% SOC [V]' not found in BPX file. Using 'Upper voltage cut-off [V]'.\n",
      "  return ParameterValues._create_from_bpx(bpx, target_soc)\n"
     ]
    },
    {
     "ename": "RuntimeError",
     "evalue": ".../casadi/core/serializing_stream.cpp:381: Assertion \"load_version==v\" failed:\nDeSerialization of SXFunction failed. Object written in version 2 but can only read in version 1.",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mRuntimeError\u001b[0m                              Traceback (most recent call last)",
      "Cell \u001b[0;32mIn[8], line 33\u001b[0m\n\u001b[1;32m     30\u001b[0m t_eval \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39marange(\u001b[38;5;241m0\u001b[39m, \u001b[38;5;241m10\u001b[39m \u001b[38;5;241m*\u001b[39m \u001b[38;5;241m3600\u001b[39m, \u001b[38;5;241m10\u001b[39m)\n\u001b[1;32m     32\u001b[0m \u001b[38;5;66;03m# PyBaMM outputs\u001b[39;00m\n\u001b[0;32m---> 33\u001b[0m t \u001b[38;5;241m=\u001b[39m \u001b[43msol\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mTime [s]\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m(\u001b[49m\u001b[43mt\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mt_eval\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m     34\u001b[0m I \u001b[38;5;241m=\u001b[39m sol[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mCurrent [A]\u001b[39m\u001b[38;5;124m\"\u001b[39m](t\u001b[38;5;241m=\u001b[39mt_eval)\n\u001b[1;32m     35\u001b[0m V \u001b[38;5;241m=\u001b[39m sol[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mVoltage [V]\u001b[39m\u001b[38;5;124m\"\u001b[39m](t\u001b[38;5;241m=\u001b[39mt_eval)\n",
      "File \u001b[0;32m~/workspace/dandeliion-client/.venv-pip/lib/python3.10/site-packages/pybamm/solvers/processed_variable.py:257\u001b[0m, in \u001b[0;36mProcessedVariable.__call__\u001b[0;34m(self, t, x, r, y, z, R, fill_value)\u001b[0m\n\u001b[1;32m    255\u001b[0m     entries_for_interp, coords \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_interp_setup(entries, t_observe)\n\u001b[1;32m    256\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m--> 257\u001b[0m     \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43minitialise\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m    258\u001b[0m     entries_for_interp, coords \u001b[38;5;241m=\u001b[39m (\n\u001b[1;32m    259\u001b[0m         \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_entries_for_interp_raw,\n\u001b[1;32m    260\u001b[0m         \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_coords_raw,\n\u001b[1;32m    261\u001b[0m     )\n\u001b[1;32m    263\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mtime_integral \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n",
      "File \u001b[0;32m~/workspace/dandeliion-client/.venv-pip/lib/python3.10/site-packages/pybamm/solvers/processed_variable.py:85\u001b[0m, in \u001b[0;36mProcessedVariable.initialise\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m     82\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mentries_raw_initialized:\n\u001b[1;32m     83\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m\n\u001b[0;32m---> 85\u001b[0m entries \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mobserve_raw\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m     87\u001b[0m t \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mt_pts\n\u001b[1;32m     88\u001b[0m entries_for_interp, coords \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_interp_setup(entries, t)\n",
      "File \u001b[0;32m~/workspace/dandeliion-client/.venv-pip/lib/python3.10/site-packages/pybamm/solvers/processed_variable.py:116\u001b[0m, in \u001b[0;36mProcessedVariable.observe_raw\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m    112\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m    113\u001b[0m \u001b[38;5;124;03mEvaluate the base variable at the given time points and y values.\u001b[39;00m\n\u001b[1;32m    114\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m    115\u001b[0m t \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mt_pts\n\u001b[0;32m--> 116\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_observe_postfix(\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_observe_raw_cpp\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m, t)\n",
      "File \u001b[0;32m~/workspace/dandeliion-client/.venv-pip/lib/python3.10/site-packages/pybamm/solvers/processed_variable.py:178\u001b[0m, in \u001b[0;36mProcessedVariable._observe_raw_cpp\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m    173\u001b[0m ts, ys, _, funcs, inputs, is_f_contiguous \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_setup_cpp_inputs(\n\u001b[1;32m    174\u001b[0m     t, full_range\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m\n\u001b[1;32m    175\u001b[0m )\n\u001b[1;32m    176\u001b[0m shapes \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_shape(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mt_pts)\n\u001b[0;32m--> 178\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43midaklu\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mobserve\u001b[49m\u001b[43m(\u001b[49m\u001b[43mts\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mys\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43minputs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mfuncs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mis_f_contiguous\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mshapes\u001b[49m\u001b[43m)\u001b[49m\n",
      "\u001b[0;31mRuntimeError\u001b[0m: .../casadi/core/serializing_stream.cpp:381: Assertion \"load_version==v\" failed:\nDeSerialization of SXFunction failed. Object written in version 2 but can only read in version 1."
     ]
    }
   ],
   "source": [
    "print(f\"\\nPyBaMM {pybamm.__version__} simulation...\\n\")\n",
    "\n",
    "pybamm_params = pybamm.ParameterValues.create_from_bpx(params)\n",
    "\n",
    "if is_isothermal:\n",
    "    model = pybamm.lithium_ion.DFN()\n",
    "\n",
    "else:\n",
    "    pybamm_params.update(\n",
    "        {\n",
    "            \"Total heat transfer coefficient [W.m-2.K-1]\": 0.0,\n",
    "        },\n",
    "        check_already_exists=False,\n",
    "    )\n",
    "\n",
    "    model_opts = {\"thermal\": \"lumped\", \"cell geometry\": \"arbitrary\"}\n",
    "\n",
    "    model = pybamm.lithium_ion.DFN(options=model_opts)\n",
    "\n",
    "# Sim object\n",
    "sim = pybamm.Simulation(\n",
    "    model,\n",
    "    parameter_values=pybamm_params,\n",
    "    experiment=experiment,\n",
    "    var_pts=extra_params['Mesh'],\n",
    ")\n",
    "\n",
    "sol = sim.solve()\n",
    "\n",
    "t_eval = np.arange(0, 10 * 3600, 10)\n",
    "\n",
    "# PyBaMM outputs\n",
    "t = sol[\"Time [s]\"](t=t_eval)\n",
    "I = sol[\"Current [A]\"](t=t_eval)\n",
    "V = sol[\"Voltage [V]\"](t=t_eval)\n",
    "T = sol[\"Volume-averaged cell temperature [K]\"](t=t_eval)\n",
    "x = sol[\"x [m]\"](t=t_eval)\n",
    "ce = sol[\"Electrolyte concentration [mol.m-3]\"](t=t_eval)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "987aa8e4-98c2-4b1e-8e16-b38f49a31241",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<pybamm.solvers.processed_variable.ProcessedVariable0D object at 0x7fdd14ecdde0>\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'success'"
      ]
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "t_eval = np.arange(0, 10 * 3600, 10)\n",
    "\n",
    "# PyBaMM outputs\n",
    "print(sol[\"Time [s]\"](t=t_eval))\n",
    "\n",
    "\n",
    "solution.status"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e975c59e-c18a-4bc7-85cf-833a6c7fa5d1",
   "metadata": {},
   "source": [
    "### Accessing the log file "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "93d6aefb-4148-493b-8d23-26c3552a19fa",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[2025-05-09 17:13:09.249] [info] Starting dandeliion-models v0.1.1...\n",
      "[2025-05-09 17:13:09.249] [info] Parsing input JSON file parameters.json...\n",
      "[2025-05-09 17:13:09.249] [info] Input JSON file parsing time: 0.091 ms\n",
      "[2025-05-09 17:13:09.249] [info] Initialising simulation parameters...\n",
      "[2025-05-09 17:13:09.249] [info] Title: Parameterisation of our example cell\n",
      "[2025-05-09 17:13:09.249] [info] Model: DFN\n",
      "[2025-05-09 17:13:09.256] [info] Thermal model: Isothermal\n",
      "[2025-05-09 17:13:09.256] [info] Found DandeLiion Experiment...\n",
      "[2025-05-09 17:13:09.256] [info] Number of instructions to parse: 1\n",
      "[2025-05-09 17:13:09.256] [info] Solver mode: Fast (default)\n",
      "[2025-05-09 17:13:09.256] [info] Simulation parameters initialisation time: 6.705 ms\n",
      "[2025-05-09 17:13:09.256] [info] Starting the simulation...\n",
      "[2025-05-09 17:13:09.256] [info] Computational mesh: 31x31 + 15 + 31x31 finite volumes\n",
      "[2025-05-09 17:13:09.256] [info] Total number of equations: 2204\n",
      "[2025-05-09 17:13:09.256] [info] Updating inconsistent initial conditions...\n",
      "[2025-05-09 17:13:09.271] [info] Initial conditions settled in: 15.091 ms\n",
      "[2025-05-09 17:13:09.271] [info] Instruction #1 of 1: Discharge at 1 [C] until 2.5 [V]...\n",
      "[2025-05-09 17:13:15.467] [warning] DAE solver diverged at time t = 10012.737004947663\n",
      "[2025-05-09 17:13:15.467] [info] Simulation time: 6.211 s\n",
      "[2025-05-09 17:13:15.467] [info] Writing the output to output.json...\n",
      "[2025-05-09 17:13:15.493] [info] Output JSON file writing time: 25.931 ms\n",
      "[2025-05-09 17:13:15.497] [info] Total time: 6.248 s\n",
      "\n"
     ]
    }
   ],
   "source": [
    "print(solution.log)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e3c7b5e7-e0e4-44d9-ba44-bd978e8974fb",
   "metadata": {},
   "source": [
    "### Results\n",
    "Print all available keys in the solution object.\n",
    "Here method `keys()` is used to get all available keys in the solution object."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "bf329a1e-fd4f-47a8-81f3-4e3130804616",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Current [A]\n",
      "Electrolyte concentration [mol.m-3]\n",
      "Electrolyte potential [V]\n",
      "Electrolyte x-coordinate [m]\n",
      "Temperature [K]\n",
      "Time [s]\n",
      "Voltage [V]\n",
      "X-averaged negative electrode exchange current density [A.m-2]\n",
      "X-averaged negative electrode potential [V]\n",
      "X-averaged negative electrode surface concentration [mol.m-3]\n",
      "X-averaged positive electrode exchange current density [A.m-2]\n",
      "X-averaged positive electrode potential [V]\n",
      "X-averaged positive electrode surface concentration [mol.m-3]\n"
     ]
    }
   ],
   "source": [
    "for key in sorted(solution.keys()):\n",
    "    print(key)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "id": "84abbcab-168c-4175-8f3e-9a9555857f30",
   "metadata": {},
   "outputs": [
    {
     "ename": "IndexError",
     "evalue": "invalid index to scalar variable.",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mIndexError\u001b[0m                                Traceback (most recent call last)",
      "Cell \u001b[0;32mIn[37], line 10\u001b[0m\n\u001b[1;32m      7\u001b[0m             \u001b[38;5;28;01mreturn\u001b[39;00m element, \u001b[38;5;28mlen\u001b[39m(arr) \u001b[38;5;241m-\u001b[39m \u001b[38;5;241m1\u001b[39m \u001b[38;5;241m-\u001b[39m idx\n\u001b[1;32m      8\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m, \u001b[38;5;28;01mNone\u001b[39;00m  \u001b[38;5;66;03m# Return None, None if all elements are NaN\u001b[39;00m\n\u001b[0;32m---> 10\u001b[0m t_last, i_last \u001b[38;5;241m=\u001b[39m \u001b[43mlast_non_nan\u001b[49m\u001b[43m(\u001b[49m\u001b[43mt\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m     12\u001b[0m \u001b[38;5;66;03m# Print the final values of time, voltage, and temperature\u001b[39;00m\n\u001b[1;32m     13\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mFinal PyBaMM | DandeLiion time [s]: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mt_last\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m | \u001b[39m\u001b[38;5;132;01m{\u001b[39;00msolution[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mTime [s]\u001b[39m\u001b[38;5;124m'\u001b[39m][\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m]\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n",
      "Cell \u001b[0;32mIn[37], line 5\u001b[0m, in \u001b[0;36mlast_non_nan\u001b[0;34m(arr)\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21mlast_non_nan\u001b[39m(arr):\n\u001b[1;32m      2\u001b[0m \u001b[38;5;250m    \u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m      3\u001b[0m \u001b[38;5;124;03m    A helper function to get the last non-NaN element in an array and its index.\u001b[39;00m\n\u001b[1;32m      4\u001b[0m \u001b[38;5;124;03m    \"\"\"\u001b[39;00m\n\u001b[0;32m----> 5\u001b[0m     \u001b[38;5;28;01mfor\u001b[39;00m idx, element \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(\u001b[43marr\u001b[49m\u001b[43m[\u001b[49m\u001b[43m:\u001b[49m\u001b[43m:\u001b[49m\u001b[38;5;241;43m-\u001b[39;49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m]\u001b[49m):\n\u001b[1;32m      6\u001b[0m         \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m np\u001b[38;5;241m.\u001b[39misnan(element):\n\u001b[1;32m      7\u001b[0m             \u001b[38;5;28;01mreturn\u001b[39;00m element, \u001b[38;5;28mlen\u001b[39m(arr) \u001b[38;5;241m-\u001b[39m \u001b[38;5;241m1\u001b[39m \u001b[38;5;241m-\u001b[39m idx\n",
      "\u001b[0;31mIndexError\u001b[0m: invalid index to scalar variable."
     ]
    }
   ],
   "source": [
    "def last_non_nan(arr):\n",
    "    \"\"\"\n",
    "    A helper function to get the last non-NaN element in an array and its index.\n",
    "    \"\"\"\n",
    "    for idx, element in enumerate(arr[::-1]):\n",
    "        if not np.isnan(element):\n",
    "            return element, len(arr) - 1 - idx\n",
    "    return None, None  # Return None, None if all elements are NaN\n",
    "\n",
    "t_last, i_last = last_non_nan(t)\n",
    "\n",
    "# Print the final values of time, voltage, and temperature\n",
    "print(f\"Final PyBaMM | DandeLiion time [s]: {t_last} | {solution['Time [s]'][-1]}\")\n",
    "print(f\"Final PyBaMM | DandeLiion voltage [V]: {V[i_last]} | {solution['Voltage [V]'][-1]}\")\n",
    "print(f\"Final PyBaMM | DandeLiion temperature [K]: {T[i_last]} | {solution['Temperature [K]'][-1]}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "0ac018b7-b201-466c-adef-0aff6e624ab1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:dandeliion.client.solution:Fetching 'Current [A]' column from simulator\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 60428 characters]",
      "text/plain": [
       "<Figure size 1000x800 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Plot current and voltage vs time.\n",
    "# Here we access scalar values vs time.\n",
    "fig, axs = plt.subplots(2, 1, figsize=(10, 8))\n",
    "axs[0].plot(solution[\"Time [s]\"], solution[\"Current [A]\"], label=\"Dandeliion\")\n",
    "axs[0].set_xlabel(\"time [s]\")\n",
    "axs[0].set_title(\"Current [A]\")\n",
    "axs[0].legend()\n",
    "axs[0].grid()\n",
    "axs[1].plot(solution[\"Time [s]\"], solution[\"Voltage [V]\"], label=\"Dandeliion\")\n",
    "axs[1].set_xlabel(\"time [s]\")\n",
    "axs[1].set_title(\"Voltage [V]\")\n",
    "axs[1].legend()\n",
    "axs[1].grid()\n",
    "plt.tight_layout()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "f9d0531c-8c29-442d-9890-fcf73fa2f238",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:dandeliion.client.solution:Fetching 'Electrolyte x-coordinate [m]' column from simulator\n",
      "INFO:dandeliion.client.solution:Fetching 'Electrolyte concentration [mol.m-3]' column from simulator\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 41372 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Concentration in the electrolyte vs `x` at the last time step.\n",
    "# Here we access spatially dependent values vs time.\n",
    "plt.plot(\n",
    "    solution[\"Electrolyte x-coordinate [m]\"] * 1e6,\n",
    "    solution[\"Electrolyte concentration [mol.m-3]\"][-1],\n",
    "    label=\"Dandeliion\",\n",
    ")\n",
    "plt.xlabel(r\"x [$\\mu$m]\")\n",
    "plt.title(\"Electrolyte conc. (end of experiment) [mol.m-3]\")\n",
    "plt.legend()\n",
    "plt.grid()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "816148d3-13c6-468d-a118-3fb9a273ee81",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Time [s]\tVoltage [V]\n",
      "0\t4.105739335391049\n",
      "1\t4.101427662296605\n",
      "2\t4.098642664465888\n",
      "3\t4.096238093482471\n",
      "4\t4.094757223776048\n",
      "5\t4.093378987544845\n",
      "6\t4.092000751313644\n",
      "7\t4.09082470025466\n",
      "8\t4.0897352999837695\n",
      "9\t4.088645899712879\n",
      "10\t4.0875564994419875\n",
      "11\t4.086467099171097\n",
      "12\t4.085377698900206\n",
      "13\t4.084390779669351\n",
      "14\t4.083642982865247\n",
      "15\t4.082895186061143\n",
      "16\t4.082147389257038\n",
      "17\t4.081399592452934\n",
      "18\t4.080651795648829\n",
      "19\t4.079903998844725\n"
     ]
    }
   ],
   "source": [
    "# A list of output times (optional)\n",
    "t_eval = np.arange(0, 20, 1)\n",
    "\n",
    "# If the user needs the solution at the `t_eval` times, the following code can be used (works only correctly on columns with timeline data).\n",
    "# This is a linear interpolation with constant extrapolation:\n",
    "print(\"Time [s]\\tVoltage [V]\")\n",
    "for t, voltage in zip(t_eval, solution[\"Voltage [V]\"](t=t_eval)):\n",
    "    print(f\"{t}\\t{voltage}\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "51d45698-5342-4cbc-9e07-e85931d5b6e7",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv-pip",
   "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.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
