{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "359edeb3",
   "metadata": {},
   "source": [
    "# DandeLiion API - Example Notebook"
   ]
  },
  {
   "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",
    "\n",
    "# Optional\n",
    "import pybamm\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": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "DadneLiion Client version: 1.1\n"
     ]
    }
   ],
   "source": [
    "print(\"DadneLiion Client version:\", 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": null,
   "id": "424cfefd-19b2-4e2a-83dd-a306ead4ff98",
   "metadata": {},
   "outputs": [],
   "source": [
    "api_url = \"https://api.dandeliion.com/v1\"\n",
    "api_key = \"API KEY\"  # Replace with your actual API key\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": 5,
   "id": "8fe2f5ef-1c2d-4259-8e45-ce592a122b91",
   "metadata": {},
   "outputs": [],
   "source": [
    "params = 'AE_gen1_BPX.json'"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "251b4538-abf6-438b-b2f4-ae0b6b433378",
   "metadata": {},
   "source": [
    "### Experiment\n",
    "PyBaMM or DandeLiion `Experiment` object, for example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "041b9b60-c797-47c8-982c-6f78b3526929",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Drive cycle data\n",
    "import pandas as pd\n",
    "drive_cycle_data = pd.read_csv(\"US06.csv\", comment=\"#\", header=None).to_numpy()\n",
    "drive_cycle = np.column_stack([drive_cycle_data[:, 0], -drive_cycle_data[:, 1]])  # Negative current for discharge\n",
    "\n",
    "# Use dandeliion.Experiment if PyBaMM is not installed\n",
    "# experiment = dandeliion.Experiment(\n",
    "experiment = pybamm.Experiment(\n",
    "  [\n",
    "    (\n",
    "      \"Discharge at 1C until 3.8 V\",\n",
    "      \"Hold at 3.8 V for 10 minutes (5 second period)\",\n",
    "      \"Rest for 300 seconds\",\n",
    "      \"Charge at 2000 mA for 1 hour or until 4.0 V\",\n",
    "      \"Rest for 5 min\",\n",
    "      \"Discharge at 20W for 20 minutes or until 2.5V\",\n",
    "      \"Rest for 300 s (1 second period)\",\n",
    "      \"Charge at C/2 until 3.8 V (0.5 min period)\",\n",
    "      \"Hold at 3.8V for 0.1 hr (1s period)\",\n",
    "    ),\n",
    "    (\n",
    "      pybamm.step.current(drive_cycle),\n",
    "    ) * 2,\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": 25,
   "id": "55c1d753-029a-44e6-8a6c-5fdf34a2a1b1",
   "metadata": {},
   "outputs": [],
   "source": [
    "extra_params = {}\n",
    "\n",
    "# Number of mesh points in PyBaMM format (default is 16 mesh points everywhere):\n",
    "extra_params['Mesh'] = {\"x_n\": 16, \"x_s\": 16, \"x_p\": 16, \"r_n\": 16, \"r_p\": 16}\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": 26,
   "id": "d22442eb-c0bf-41bc-b9ee-25d19800d1d0",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/schewtsj/Documents/dandeliion/dandeliion-client/.venv/lib/python3.12/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] | Your simulation is now running on AWS Lambda.\n",
      "INFO:dandeliion.client.simulator:[running] | Your simulation is now running on AWS Lambda.\n",
      "INFO:dandeliion.client.simulator:[success] | [2025-07-11 10:45:24.838] [info] Total time: 6.383 s\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",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8c4b5e07-7068-4b99-b898-c1402024bc66",
   "metadata": {},
   "source": [
    "If solve is run in non-blocking mode, you can still force the program to wait for the simulation to finish by calling `join()` on the solution returned by it (when used for an already finished simulation, it simply returns instantly)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "987aa8e4-98c2-4b1e-8e16-b38f49a31241",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Status: success\n",
      "Status: success\n"
     ]
    }
   ],
   "source": [
    "print(\"Status:\", solution.status) \n",
    "solution.join()\n",
    "print(\"Status:\", solution.status) "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e975c59e-c18a-4bc7-85cf-833a6c7fa5d1",
   "metadata": {},
   "source": [
    "### Accessing the log file "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "93d6aefb-4148-493b-8d23-26c3552a19fa",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Your simulation is now running on AWS Lambda. \n",
      "[2025-07-11 10:45:18.455] [info] Starting dandeliion-models v0.2.0...\n",
      "[2025-07-11 10:45:18.455] [info] Parsing input JSON file parameters.json...\n",
      "[2025-07-11 10:45:18.464] [info] Input JSON file parsing time: 8.468 ms\n",
      "[2025-07-11 10:45:18.464] [info] Initialising simulation parameters...\n",
      "[2025-07-11 10:45:18.464] [info] Title: About:Energy Gen1 Demo 21700 NMC811|graphite 5 Ah cell\n",
      "[2025-07-11 10:45:18.464] [info] Model: DFN\n",
      "[2025-07-11 10:45:18.470] [info] Thermal model: Isothermal\n",
      "[2025-07-11 10:45:18.470] [info] Found time series input...\n",
      "[2025-07-11 10:45:18.470] [info] Time series input: Current [A]\n",
      "[2025-07-11 10:45:18.470] [info] Found DandeLiion Experiment...\n",
      "[2025-07-11 10:45:18.470] [info] Number of instructions to parse: 11\n",
      "[2025-07-11 10:45:18.470] [info] Solver mode: Fast (default)\n",
      "[2025-07-11 10:45:18.470] [info] Simulation parameters initialisation time: 6.223 ms\n",
      "[2025-07-11 10:45:18.470] [info] Starting the simulation...\n",
      "[2025-07-11 10:45:18.470] [info] Computational mesh: 15x15 + 15 + 15x15 finite volumes\n",
      "[2025-07-11 10:45:18.470] [info] Total number of equations: 604\n",
      "[2025-07-11 10:45:18.470] [info] Updating inconsistent initial conditions...\n",
      "[2025-07-11 10:45:18.476] [info] Initial conditions settled in: 5.994 ms\n",
      "[2025-07-11 10:45:18.476] [info] Instruction #1 of 11: Discharge at 1 [C] until 3.8 [V]...\n",
      "[2025-07-11 10:45:18.655] [info] - Voltage event at t = 762.827 s: V = 3.800 V (tolerance = 1e-06 V)\n",
      "[2025-07-11 10:45:18.655] [info] - Completed in 179.043 ms. Charge/discharge time: 762.827 s\n",
      "[2025-07-11 10:45:18.655] [info] Instruction #2 of 11: Hold at 3.8 [V] for 600 [s] with 5 [s] period...\n",
      "[2025-07-11 10:45:18.866] [info] - Completed in 210.610 ms. Charge/discharge time: 1362.827 s\n",
      "[2025-07-11 10:45:18.866] [info] Instruction #3 of 11: Rest for 300 [s]...\n",
      "[2025-07-11 10:45:18.931] [info] - Completed in 65.874 ms. Charge/discharge time: 1662.827 s\n",
      "[2025-07-11 10:45:18.931] [info] Instruction #4 of 11: Charge at 2 [A] for 3600 [s] or until 4 [V]...\n",
      "[2025-07-11 10:45:19.035] [info] - Voltage event at t = 2093.730 s: V = 4.000 V (tolerance = 1e-06 V)\n",
      "[2025-07-11 10:45:19.035] [info] - Completed in 103.548 ms. Charge/discharge time: 2093.730 s\n",
      "[2025-07-11 10:45:19.035] [info] Instruction #5 of 11: Rest for 300 [s]...\n",
      "[2025-07-11 10:45:19.094] [info] - Completed in 59.202 ms. Charge/discharge time: 2393.730 s\n",
      "[2025-07-11 10:45:19.094] [info] Instruction #6 of 11: Discharge at 20 [W] for 1200 [s] or until 2.5 [V]...\n",
      "[2025-07-11 10:45:19.351] [info] - Completed in 256.806 ms. Charge/discharge time: 3593.730 s\n",
      "[2025-07-11 10:45:19.351] [info] Instruction #7 of 11: Rest for 300 [s] with 1 [s] period...\n",
      "[2025-07-11 10:45:19.735] [info] - Completed in 383.411 ms. Charge/discharge time: 3893.730 s\n",
      "[2025-07-11 10:45:19.735] [info] Instruction #8 of 11: Charge at 0.5 [C] until 3.8 [V] with 30 [s] period...\n",
      "[2025-07-11 10:45:19.841] [info] - Voltage event at t = 5036.036 s: V = 3.800 V (tolerance = 1e-06 V)\n",
      "[2025-07-11 10:45:19.841] [info] - Completed in 106.072 ms. Charge/discharge time: 5036.036 s\n",
      "[2025-07-11 10:45:19.841] [info] Instruction #9 of 11: Hold at 3.8 [V] for 360 [s] with 1 [s] period...\n",
      "[2025-07-11 10:45:20.228] [info] - Completed in 387.327 ms. Charge/discharge time: 5396.036 s\n",
      "[2025-07-11 10:45:20.228] [info] Instruction #10 of 11: Time series input...\n",
      "[2025-07-11 10:45:22.423] [info] - Completed in 2.195 s. Charge/discharge time: 5996.036 s\n",
      "[2025-07-11 10:45:22.423] [info] Instruction #11 of 11: Time series input...\n",
      "[2025-07-11 10:45:24.589] [info] - Completed in 2.166 s. Charge/discharge time: 6596.036 s\n",
      "[2025-07-11 10:45:24.589] [info] Simulation completed successfully\n",
      "[2025-07-11 10:45:24.589] [info] Simulation time: 6.119 s\n",
      "[2025-07-11 10:45:24.589] [info] Writing the output to output.json...\n",
      "[2025-07-11 10:45:24.831] [info] Output JSON file writing time: 241.692 ms\n",
      "[2025-07-11 10:45:24.838] [info] Total time: 6.383 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": 29,
   "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": 30,
   "id": "84abbcab-168c-4175-8f3e-9a9555857f30",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:dandeliion.client.solution:Fetching 'Time [s]' column from simulator\n",
      "Final time [s]: 6596.0361328125\n",
      "INFO:dandeliion.client.solution:Fetching 'Voltage [V]' column from simulator\n",
      "Final voltage [V]: 3.6802161457188616\n",
      "INFO:dandeliion.client.solution:Fetching 'Temperature [K]' column from simulator\n",
      "Final temperature [K]: 298.15\n"
     ]
    }
   ],
   "source": [
    "# Print the final values of time, voltage, and temperature\n",
    "print(f\"Final time [s]: {solution['Time [s]'][-1]}\")\n",
    "print(f\"Final voltage [V]: {solution['Voltage [V]'][-1]}\")\n",
    "print(f\"Final temperature [K]: {solution['Temperature [K]'][-1]}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "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, 93088 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": 32,
   "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, 38256 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": 33,
   "id": "816148d3-13c6-468d-a118-3fb9a273ee81",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Time [s]\tVoltage [V]\n",
      "0\t4.080929637658874\n",
      "1\t4.07556477797119\n",
      "2\t4.0706389644456165\n",
      "3\t4.065947609011764\n",
      "4\t4.06198428762323\n",
      "5\t4.058101858906398\n",
      "6\t4.0542194301895655\n",
      "7\t4.0511337764210245\n",
      "8\t4.0483895976303215\n",
      "9\t4.0456454188396185\n",
      "10\t4.042901240048916\n",
      "11\t4.040157061258213\n",
      "12\t4.03741288246751\n",
      "13\t4.034999605785829\n",
      "14\t4.033358434025193\n",
      "15\t4.031717262264559\n",
      "16\t4.030076090503924\n",
      "17\t4.028434918743289\n",
      "18\t4.026793746982654\n",
      "19\t4.02515257522202\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": "markdown",
   "id": "46974494-6635-4519-ae56-9a8ceeb4b7fc",
   "metadata": {},
   "source": [
    "### Storing and retrieving simulation and its solutions"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6d423d66-4881-49b4-b919-a9f33034e54a",
   "metadata": {},
   "source": [
    "You can export simulation output at any time. It will try to fetch whatever solutions & logs are available at that time and dump it into an output file. You can use that file externally or to create a new solution object. If the simulation was still running at the time of export, you can provide the API key used to start the simulation to reconnect that solution object to the original simulation to retrieve the latest updates."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "id": "c6477864-d6c6-457b-8523-cb60f4f32d4e",
   "metadata": {},
   "outputs": [],
   "source": [
    "solution_file = 'test_solution.json'\n",
    "\n",
    "solution.dump(solution_file)\n",
    "restored_solution = dandeliion.Simulator.restore(solution_file, api_key=api_key)"
   ]
  }
 ],
 "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.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
