├── LICENSE
├── README.md
├── digitalTwin_Li_ION.ipynb
└── discharge.csv
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 JAVIER MARIN
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # Digital-Twin-in-python
4 | In this repo we will show how to build a simple but useful Digital Twin using python. Our asset will be a Li-ion battery. This Digital Twin will allow us to model and predict batteries behavior and can be included in any virtual asset management process.
5 | https://medium.com/towards-data-science/how-to-build-a-digital-twin-b31058fd5d3e
6 |
--------------------------------------------------------------------------------
/digitalTwin_Li_ION.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "nbformat": 4,
3 | "nbformat_minor": 0,
4 | "metadata": {
5 | "kernelspec": {
6 | "display_name": "Python 3",
7 | "language": "python",
8 | "name": "python3"
9 | },
10 | "language_info": {
11 | "codemirror_mode": {
12 | "name": "ipython",
13 | "version": 3
14 | },
15 | "file_extension": ".py",
16 | "mimetype": "text/x-python",
17 | "name": "python",
18 | "nbconvert_exporter": "python",
19 | "pygments_lexer": "ipython3",
20 | "version": "3.7.1"
21 | },
22 | "colab": {
23 | "name": "digitalTwin_Li_ION_.ipynb",
24 | "provenance": [],
25 | "collapsed_sections": []
26 | }
27 | },
28 | "cells": [
29 | {
30 | "cell_type": "markdown",
31 | "metadata": {
32 | "id": "AEil20eqS2So"
33 | },
34 | "source": [
35 | "# Hybrid digital twin of a Li-ion battery\n"
36 | ]
37 | },
38 | {
39 | "cell_type": "code",
40 | "metadata": {
41 | "id": "ZYaB7HSTS2Ss"
42 | },
43 | "source": [
44 | "import pandas as pd\n",
45 | "import numpy as np\n",
46 | "from sklearn.model_selection import train_test_split\n",
47 | "import keras\n",
48 | "import tensorflow as tf\n",
49 | "from keras.models import Sequential\n",
50 | "from keras.layers import LSTM, Dense\n",
51 | "from keras.optimizers import SGD\n",
52 | "from tensorflow.keras.layers import Dropout\n",
53 | "from matplotlib import pyplot as plt\n",
54 | "import plotly.express as px\n",
55 | "from plotly.subplots import make_subplots\n",
56 | "import plotly.graph_objects as go\n",
57 | "import plotly.figure_factory as ff"
58 | ],
59 | "execution_count": null,
60 | "outputs": []
61 | },
62 | {
63 | "cell_type": "markdown",
64 | "metadata": {
65 | "id": "NIhtsIQHS2St"
66 | },
67 | "source": [
68 | "### 1. Load experimental data"
69 | ]
70 | },
71 | {
72 | "cell_type": "code",
73 | "metadata": {
74 | "id": "UrmwSwuMS_iY"
75 | },
76 | "source": [
77 | "df = pd.read_csv('discharge.csv')"
78 | ],
79 | "execution_count": null,
80 | "outputs": []
81 | },
82 | {
83 | "cell_type": "code",
84 | "metadata": {
85 | "id": "rWmpbfS7W9zV"
86 | },
87 | "source": [
88 | "df = df[df['Battery'] == 'B0005']\n",
89 | "df = df[df['Temperature_measured'] > 36] #choose battery B0005\n",
90 | "#df['Time'] =pd.to_datetime(df['Time'], unit='s')\n",
91 | "dfb = df.groupby(['id_cycle']).max()\n",
92 | "dfb['Cumulated_T'] = dfb['Time'].cumsum()"
93 | ],
94 | "execution_count": null,
95 | "outputs": []
96 | },
97 | {
98 | "cell_type": "code",
99 | "metadata": {
100 | "colab": {
101 | "base_uri": "https://localhost:8080/",
102 | "height": 1000
103 | },
104 | "id": "CcBEYPoQC9SE",
105 | "outputId": "7e16313e-8ccf-4422-a787-a8dba8e29a22"
106 | },
107 | "source": [
108 | "import plotly.express as px\n",
109 | "fig = px.scatter_matrix(dfb.drop(columns=['Time','type', 'ambient_temperature', \n",
110 | " 'time', 'Battery']), \n",
111 | " )\n",
112 | "fig.update_traces(marker=dict(size=2,\n",
113 | " color='crimson',\n",
114 | " symbol='square')),\n",
115 | "fig.update_traces(diagonal_visible=False)\n",
116 | "fig.update_layout(\n",
117 | " title='Battery dataset',\n",
118 | " width=900,\n",
119 | " height=1200,\n",
120 | ")\n",
121 | "fig.update_layout({'plot_bgcolor': '#f2f8fd',\n",
122 | " 'paper_bgcolor': 'white',}, \n",
123 | " template='plotly_white',\n",
124 | " font=dict(size=7)\n",
125 | " )\n",
126 | "\n",
127 | "fig.show()"
128 | ],
129 | "execution_count": null,
130 | "outputs": [
131 | {
132 | "output_type": "display_data",
133 | "data": {
134 | "text/html": [
135 | "\n",
136 | "
\n",
137 | "\n",
138 | " \n",
139 | " \n",
140 | " \n",
141 | " \n",
142 | "
\n",
143 | " \n",
181 | "
\n",
182 | "\n",
183 | ""
184 | ]
185 | },
186 | "metadata": {
187 | "tags": []
188 | }
189 | }
190 | ]
191 | },
192 | {
193 | "cell_type": "code",
194 | "metadata": {
195 | "colab": {
196 | "base_uri": "https://localhost:8080/",
197 | "height": 542
198 | },
199 | "id": "bmLhWPg6Dnzk",
200 | "outputId": "1a350f6d-dd02-4b61-fbb8-404ba9510ecc"
201 | },
202 | "source": [
203 | "fig = go.Figure()\n",
204 | "\n",
205 | "fig.add_trace(go.Scatter(x=dfb['Cumulated_T']/3600, \n",
206 | " y=dfb['Capacity'],\n",
207 | " mode='lines',\n",
208 | " name='Capacity',\n",
209 | " marker_size=3, \n",
210 | " line=dict(color='crimson', width=3) \n",
211 | " ))\n",
212 | "fig.update_layout(\n",
213 | " title=\"Battery discharge capacity\",\n",
214 | " xaxis_title=\"Working time [hours]\",\n",
215 | " yaxis_title=f\"Battery capacity in Ahr\"\n",
216 | " )\n",
217 | "fig.update_layout({'plot_bgcolor': '#f2f8fd',\n",
218 | " 'paper_bgcolor': 'white',}, \n",
219 | " template='plotly_white')"
220 | ],
221 | "execution_count": null,
222 | "outputs": [
223 | {
224 | "output_type": "display_data",
225 | "data": {
226 | "text/html": [
227 | "\n",
228 | "\n",
229 | "\n",
230 | " \n",
231 | " \n",
232 | " \n",
233 | " \n",
234 | "
\n",
235 | " \n",
273 | "
\n",
274 | "\n",
275 | ""
276 | ]
277 | },
278 | "metadata": {
279 | "tags": []
280 | }
281 | }
282 | ]
283 | },
284 | {
285 | "cell_type": "markdown",
286 | "metadata": {
287 | "id": "z4khX4N5wys7"
288 | },
289 | "source": [
290 | "### 2. Define a physical model"
291 | ]
292 | },
293 | {
294 | "cell_type": "markdown",
295 | "metadata": {
296 | "id": "NhiMrtrkcpiR"
297 | },
298 | "source": [
299 | "Physical model according [1]. The basic equation is:
\n",
300 | " $L = 1 − (1 − L' )e^{-f_d}$
\n",
301 | "\n",
302 | "Where $L$ is the battery lifetime and $L'$ the initial battery lifetime. $f_d$ is a Linearized degradation rate per unit time and per cycle. It can be described as:
\n",
303 | " $f_d = f_d(t, δ, σ, T_c)$
\n",
304 | "\n",
305 | "where $t$ is charging time, δ is the cycle depth of discharge, σ is the cycle average state of charge, and $T_c$ is cell temperature. The equation for baatery capacity could be written as follows:
\n",
306 | " $C = C_0e^{f_d}$
\n",
307 | "\n",
308 | "We have empirically found that $f_d$ aproximates to:\n",
309 | " $f_d = \\frac{kT_Ci}{t}$
\n",
310 | "\n",
311 | "where $k= $ 0.13, $i$ the cycle number and $t$ the charge time for every cycle.\n",
312 | "\n",
313 | "- [1] *Xu, Bolun & Oudalov, Alexandre & Ulbig, Andreas & Andersson, Göran & Kirschen, D.s. (2016). Modeling of Lithium-Ion Battery Degradation for Cell Life Assessment. IEEE Transactions on Smart Grid. 99. 1-1. 10.1109/TSG.2016.2578950.* "
314 | ]
315 | },
316 | {
317 | "cell_type": "code",
318 | "metadata": {
319 | "id": "rTU_ImZ1rPLf"
320 | },
321 | "source": [
322 | "from math import e\n",
323 | "L = (dfb['Capacity']-dfb['Capacity'].iloc[0:1].values[0])/-dfb['Capacity'].iloc[0:1].values[0]\n",
324 | "K = 0.13\n",
325 | "L_1 = 1-e**(-K*dfb.index*dfb['Temperature_measured']/(dfb['Time']))\n",
326 | "dfb['C. Capacity'] = -(L_1*dfb['Capacity'].iloc[0:1].values[0]) + dfb['Capacity'].iloc[0:1].values[0]"
327 | ],
328 | "execution_count": null,
329 | "outputs": []
330 | },
331 | {
332 | "cell_type": "code",
333 | "metadata": {
334 | "colab": {
335 | "base_uri": "https://localhost:8080/",
336 | "height": 542
337 | },
338 | "id": "l8bmHjJQCBEe",
339 | "outputId": "0030b53e-9dde-449a-9f29-ba6606820c2e"
340 | },
341 | "source": [
342 | "fig = go.Figure()\n",
343 | "\n",
344 | "fig.add_trace(go.Scatter(x=dfb.index, \n",
345 | " y=dfb['C. Capacity'],\n",
346 | " mode='lines',\n",
347 | " name='Physical model',\n",
348 | " line=dict(color='navy', \n",
349 | " width=2.5,\n",
350 | " )))\n",
351 | "\n",
352 | "fig.add_trace(go.Scatter(x=dfb.index, \n",
353 | " y=dfb['Capacity'],\n",
354 | " mode='markers',\n",
355 | " marker=dict(\n",
356 | " size=4,\n",
357 | " color='grey',\n",
358 | " symbol='cross'\n",
359 | " ),\n",
360 | " name='NASA dataset',\n",
361 | " line_color='navy'))\n",
362 | "fig.update_layout(\n",
363 | " title=\"Physical model comparison \",\n",
364 | " xaxis_title=\"Cycles\",\n",
365 | " yaxis_title=\"𝐶, Capacity [Ahr]\")\n",
366 | "\n",
367 | "fig.update_layout(legend=dict(\n",
368 | " yanchor=\"top\",\n",
369 | " y=0.9,\n",
370 | " xanchor=\"left\",\n",
371 | " x=0.8\n",
372 | "))\n",
373 | "\n",
374 | "fig.update_layout({'plot_bgcolor': '#f2f8fd',\n",
375 | " 'paper_bgcolor': 'white',}, \n",
376 | " template='plotly_white')"
377 | ],
378 | "execution_count": null,
379 | "outputs": [
380 | {
381 | "output_type": "display_data",
382 | "data": {
383 | "text/html": [
384 | "\n",
385 | "\n",
386 | "\n",
387 | " \n",
388 | " \n",
389 | " \n",
390 | " \n",
391 | "
\n",
392 | " \n",
430 | "
\n",
431 | "\n",
432 | ""
433 | ]
434 | },
435 | "metadata": {
436 | "tags": []
437 | }
438 | }
439 | ]
440 | },
441 | {
442 | "cell_type": "markdown",
443 | "metadata": {
444 | "id": "GaZ8opJqS2Sx"
445 | },
446 | "source": [
447 | "### 3. Compare experimental data with physical model"
448 | ]
449 | },
450 | {
451 | "cell_type": "code",
452 | "metadata": {
453 | "colab": {
454 | "base_uri": "https://localhost:8080/"
455 | },
456 | "id": "0jRN6RyZxMvG",
457 | "outputId": "22590dfb-0d36-453b-a065-9117fe84aa81"
458 | },
459 | "source": [
460 | "# Mean Absolute Error\n",
461 | "M = pd.DataFrame()\n",
462 | "S = pd.DataFrame()\n",
463 | "def MAE(M,S): \n",
464 | " return np.sum(S-M)/len(S)\n",
465 | "\n",
466 | "print(f'Mean Absolute Error =', round(MAE(dfb['Capacity'], dfb['C. Capacity']), 3))"
467 | ],
468 | "execution_count": null,
469 | "outputs": [
470 | {
471 | "output_type": "stream",
472 | "text": [
473 | "Mean Absolute Error = 0.004\n"
474 | ],
475 | "name": "stdout"
476 | }
477 | ]
478 | },
479 | {
480 | "cell_type": "markdown",
481 | "metadata": {
482 | "id": "MQj67klQS2S0"
483 | },
484 | "source": [
485 | "### 4. Hybrid digital twin "
486 | ]
487 | },
488 | {
489 | "cell_type": "code",
490 | "metadata": {
491 | "id": "P4tOwrSYS2S0"
492 | },
493 | "source": [
494 | "#Define inputs and outputs\n",
495 | "X_in = dfb['C. Capacity'] # input: the simulation time series\n",
496 | "X_out = dfb['Capacity'] - dfb['C. Capacity'] # output: difference between measurement and simulation\n",
497 | "\n",
498 | "X_in_train, X_in_test, X_out_train, X_out_test = train_test_split(X_in, X_out, test_size=0.33)"
499 | ],
500 | "execution_count": null,
501 | "outputs": []
502 | },
503 | {
504 | "cell_type": "code",
505 | "metadata": {
506 | "colab": {
507 | "base_uri": "https://localhost:8080/"
508 | },
509 | "id": "r4RzAol2J8or",
510 | "outputId": "d096e003-658c-4c95-c759-3e315ade8d63"
511 | },
512 | "source": [
513 | "X_in_train.shape"
514 | ],
515 | "execution_count": null,
516 | "outputs": [
517 | {
518 | "output_type": "execute_result",
519 | "data": {
520 | "text/plain": [
521 | "(112,)"
522 | ]
523 | },
524 | "metadata": {
525 | "tags": []
526 | },
527 | "execution_count": 10
528 | }
529 | ]
530 | },
531 | {
532 | "cell_type": "code",
533 | "metadata": {
534 | "id": "dnfxtbRqF1V6"
535 | },
536 | "source": [
537 | "#The Dense function in Keras constructs a fully connected neural network layer, automatically initializing the weights as biases.\n",
538 | "#First hidden layer\n",
539 | "model = Sequential()\n",
540 | "model.add(Dense(64, activation='relu'))\n",
541 | "model.add(Dense(64, activation='relu'))\n",
542 | "model.add(Dense(1))"
543 | ],
544 | "execution_count": null,
545 | "outputs": []
546 | },
547 | {
548 | "cell_type": "code",
549 | "metadata": {
550 | "colab": {
551 | "base_uri": "https://localhost:8080/"
552 | },
553 | "id": "xpWJebynF6uZ",
554 | "outputId": "bcc5ab35-7be1-491c-a89f-db29f12638e5"
555 | },
556 | "source": [
557 | "epochs = 100\n",
558 | "loss = \"mse\"\n",
559 | "model.compile(optimizer='adam',\n",
560 | " loss=loss,\n",
561 | " metrics=['mae'], #Mean Absolute Error\n",
562 | " )\n",
563 | "history = model.fit(X_in_train, X_out_train, \n",
564 | " shuffle=True, \n",
565 | " epochs=epochs,\n",
566 | " batch_size=20,\n",
567 | " validation_data=(X_in_test, X_out_test), \n",
568 | " verbose=1)"
569 | ],
570 | "execution_count": null,
571 | "outputs": [
572 | {
573 | "output_type": "stream",
574 | "text": [
575 | "Epoch 1/100\n",
576 | "6/6 [==============================] - 22s 167ms/step - loss: 0.0073 - mae: 0.0724 - val_loss: 0.0029 - val_mae: 0.0441\n",
577 | "Epoch 2/100\n",
578 | "6/6 [==============================] - 0s 7ms/step - loss: 0.0020 - mae: 0.0367 - val_loss: 0.0030 - val_mae: 0.0478\n",
579 | "Epoch 3/100\n",
580 | "6/6 [==============================] - 0s 7ms/step - loss: 0.0019 - mae: 0.0379 - val_loss: 0.0021 - val_mae: 0.0354\n",
581 | "Epoch 4/100\n",
582 | "6/6 [==============================] - 0s 6ms/step - loss: 0.0016 - mae: 0.0319 - val_loss: 0.0011 - val_mae: 0.0289\n",
583 | "Epoch 5/100\n",
584 | "6/6 [==============================] - 0s 7ms/step - loss: 0.0011 - mae: 0.0288 - val_loss: 8.8750e-04 - val_mae: 0.0258\n",
585 | "Epoch 6/100\n",
586 | "6/6 [==============================] - 0s 7ms/step - loss: 8.6720e-04 - mae: 0.0241 - val_loss: 9.3450e-04 - val_mae: 0.0255\n",
587 | "Epoch 7/100\n",
588 | "6/6 [==============================] - 0s 6ms/step - loss: 7.7068e-04 - mae: 0.0238 - val_loss: 9.3880e-04 - val_mae: 0.0270\n",
589 | "Epoch 8/100\n",
590 | "6/6 [==============================] - 0s 10ms/step - loss: 7.6818e-04 - mae: 0.0235 - val_loss: 9.1910e-04 - val_mae: 0.0254\n",
591 | "Epoch 9/100\n",
592 | "6/6 [==============================] - 0s 6ms/step - loss: 7.8089e-04 - mae: 0.0221 - val_loss: 9.1008e-04 - val_mae: 0.0266\n",
593 | "Epoch 10/100\n",
594 | "6/6 [==============================] - 0s 6ms/step - loss: 7.3556e-04 - mae: 0.0235 - val_loss: 8.7510e-04 - val_mae: 0.0254\n",
595 | "Epoch 11/100\n",
596 | "6/6 [==============================] - 0s 6ms/step - loss: 7.5666e-04 - mae: 0.0227 - val_loss: 8.7445e-04 - val_mae: 0.0258\n",
597 | "Epoch 12/100\n",
598 | "6/6 [==============================] - 0s 7ms/step - loss: 7.2720e-04 - mae: 0.0231 - val_loss: 8.7741e-04 - val_mae: 0.0260\n",
599 | "Epoch 13/100\n",
600 | "6/6 [==============================] - 0s 6ms/step - loss: 8.0716e-04 - mae: 0.0245 - val_loss: 8.7129e-04 - val_mae: 0.0253\n",
601 | "Epoch 14/100\n",
602 | "6/6 [==============================] - 0s 7ms/step - loss: 8.9908e-04 - mae: 0.0232 - val_loss: 8.6374e-04 - val_mae: 0.0255\n",
603 | "Epoch 15/100\n",
604 | "6/6 [==============================] - 0s 10ms/step - loss: 8.0920e-04 - mae: 0.0242 - val_loss: 8.6269e-04 - val_mae: 0.0253\n",
605 | "Epoch 16/100\n",
606 | "6/6 [==============================] - 0s 6ms/step - loss: 7.7771e-04 - mae: 0.0231 - val_loss: 8.6120e-04 - val_mae: 0.0257\n",
607 | "Epoch 17/100\n",
608 | "6/6 [==============================] - 0s 7ms/step - loss: 7.4492e-04 - mae: 0.0228 - val_loss: 8.5888e-04 - val_mae: 0.0257\n",
609 | "Epoch 18/100\n",
610 | "6/6 [==============================] - 0s 7ms/step - loss: 7.1863e-04 - mae: 0.0222 - val_loss: 8.5531e-04 - val_mae: 0.0254\n",
611 | "Epoch 19/100\n",
612 | "6/6 [==============================] - 0s 6ms/step - loss: 7.0377e-04 - mae: 0.0223 - val_loss: 8.5408e-04 - val_mae: 0.0253\n",
613 | "Epoch 20/100\n",
614 | "6/6 [==============================] - 0s 8ms/step - loss: 7.0186e-04 - mae: 0.0217 - val_loss: 8.7235e-04 - val_mae: 0.0264\n",
615 | "Epoch 21/100\n",
616 | "6/6 [==============================] - 0s 10ms/step - loss: 7.9355e-04 - mae: 0.0241 - val_loss: 8.7012e-04 - val_mae: 0.0250\n",
617 | "Epoch 22/100\n",
618 | "6/6 [==============================] - 0s 7ms/step - loss: 6.9302e-04 - mae: 0.0206 - val_loss: 9.7644e-04 - val_mae: 0.0278\n",
619 | "Epoch 23/100\n",
620 | "6/6 [==============================] - 0s 10ms/step - loss: 8.7732e-04 - mae: 0.0255 - val_loss: 9.7150e-04 - val_mae: 0.0252\n",
621 | "Epoch 24/100\n",
622 | "6/6 [==============================] - 0s 7ms/step - loss: 7.9624e-04 - mae: 0.0228 - val_loss: 9.6106e-04 - val_mae: 0.0277\n",
623 | "Epoch 25/100\n",
624 | "6/6 [==============================] - 0s 10ms/step - loss: 8.3275e-04 - mae: 0.0243 - val_loss: 9.0669e-04 - val_mae: 0.0250\n",
625 | "Epoch 26/100\n",
626 | "6/6 [==============================] - 0s 7ms/step - loss: 7.3055e-04 - mae: 0.0209 - val_loss: 9.4628e-04 - val_mae: 0.0275\n",
627 | "Epoch 27/100\n",
628 | "6/6 [==============================] - 0s 6ms/step - loss: 8.2731e-04 - mae: 0.0250 - val_loss: 9.1348e-04 - val_mae: 0.0249\n",
629 | "Epoch 28/100\n",
630 | "6/6 [==============================] - 0s 10ms/step - loss: 8.7785e-04 - mae: 0.0229 - val_loss: 8.5502e-04 - val_mae: 0.0262\n",
631 | "Epoch 29/100\n",
632 | "6/6 [==============================] - 0s 7ms/step - loss: 7.4989e-04 - mae: 0.0229 - val_loss: 8.3938e-04 - val_mae: 0.0255\n",
633 | "Epoch 30/100\n",
634 | "6/6 [==============================] - 0s 12ms/step - loss: 7.9248e-04 - mae: 0.0237 - val_loss: 8.5845e-04 - val_mae: 0.0249\n",
635 | "Epoch 31/100\n",
636 | "6/6 [==============================] - 0s 8ms/step - loss: 7.7327e-04 - mae: 0.0213 - val_loss: 8.9804e-04 - val_mae: 0.0270\n",
637 | "Epoch 32/100\n",
638 | "6/6 [==============================] - 0s 7ms/step - loss: 7.2960e-04 - mae: 0.0235 - val_loss: 0.0010 - val_mae: 0.0254\n",
639 | "Epoch 33/100\n",
640 | "6/6 [==============================] - 0s 7ms/step - loss: 8.1861e-04 - mae: 0.0218 - val_loss: 0.0011 - val_mae: 0.0288\n",
641 | "Epoch 34/100\n",
642 | "6/6 [==============================] - 0s 7ms/step - loss: 8.8335e-04 - mae: 0.0247 - val_loss: 0.0011 - val_mae: 0.0258\n",
643 | "Epoch 35/100\n",
644 | "6/6 [==============================] - 0s 7ms/step - loss: 9.8814e-04 - mae: 0.0242 - val_loss: 9.0522e-04 - val_mae: 0.0271\n",
645 | "Epoch 36/100\n",
646 | "6/6 [==============================] - 0s 6ms/step - loss: 7.0212e-04 - mae: 0.0230 - val_loss: 9.1783e-04 - val_mae: 0.0250\n",
647 | "Epoch 37/100\n",
648 | "6/6 [==============================] - 0s 10ms/step - loss: 8.6581e-04 - mae: 0.0241 - val_loss: 8.5024e-04 - val_mae: 0.0250\n",
649 | "Epoch 38/100\n",
650 | "6/6 [==============================] - 0s 6ms/step - loss: 7.5783e-04 - mae: 0.0221 - val_loss: 9.0157e-04 - val_mae: 0.0271\n",
651 | "Epoch 39/100\n",
652 | "6/6 [==============================] - 0s 6ms/step - loss: 0.0010 - mae: 0.0265 - val_loss: 0.0014 - val_mae: 0.0287\n",
653 | "Epoch 40/100\n",
654 | "6/6 [==============================] - 0s 6ms/step - loss: 9.4764e-04 - mae: 0.0240 - val_loss: 0.0013 - val_mae: 0.0303\n",
655 | "Epoch 41/100\n",
656 | "6/6 [==============================] - 0s 6ms/step - loss: 0.0011 - mae: 0.0281 - val_loss: 8.8451e-04 - val_mae: 0.0249\n",
657 | "Epoch 42/100\n",
658 | "6/6 [==============================] - 0s 6ms/step - loss: 7.4749e-04 - mae: 0.0230 - val_loss: 8.4974e-04 - val_mae: 0.0251\n",
659 | "Epoch 43/100\n",
660 | "6/6 [==============================] - 0s 6ms/step - loss: 7.4331e-04 - mae: 0.0215 - val_loss: 8.5526e-04 - val_mae: 0.0264\n",
661 | "Epoch 44/100\n",
662 | "6/6 [==============================] - 0s 6ms/step - loss: 6.6416e-04 - mae: 0.0221 - val_loss: 8.4034e-04 - val_mae: 0.0260\n",
663 | "Epoch 45/100\n",
664 | "6/6 [==============================] - 0s 7ms/step - loss: 7.7864e-04 - mae: 0.0242 - val_loss: 9.1566e-04 - val_mae: 0.0250\n",
665 | "Epoch 46/100\n",
666 | "6/6 [==============================] - 0s 6ms/step - loss: 6.8302e-04 - mae: 0.0207 - val_loss: 0.0011 - val_mae: 0.0290\n",
667 | "Epoch 47/100\n",
668 | "6/6 [==============================] - 0s 7ms/step - loss: 8.8531e-04 - mae: 0.0255 - val_loss: 0.0012 - val_mae: 0.0261\n",
669 | "Epoch 48/100\n",
670 | "6/6 [==============================] - 0s 6ms/step - loss: 0.0010 - mae: 0.0242 - val_loss: 8.5470e-04 - val_mae: 0.0264\n",
671 | "Epoch 49/100\n",
672 | "6/6 [==============================] - 0s 6ms/step - loss: 8.5538e-04 - mae: 0.0235 - val_loss: 9.0461e-04 - val_mae: 0.0271\n",
673 | "Epoch 50/100\n",
674 | "6/6 [==============================] - 0s 6ms/step - loss: 8.3663e-04 - mae: 0.0245 - val_loss: 0.0010 - val_mae: 0.0254\n",
675 | "Epoch 51/100\n",
676 | "6/6 [==============================] - 0s 6ms/step - loss: 7.0976e-04 - mae: 0.0218 - val_loss: 9.6546e-04 - val_mae: 0.0277\n",
677 | "Epoch 52/100\n",
678 | "6/6 [==============================] - 0s 9ms/step - loss: 9.2072e-04 - mae: 0.0252 - val_loss: 8.7586e-04 - val_mae: 0.0267\n",
679 | "Epoch 53/100\n",
680 | "6/6 [==============================] - 0s 6ms/step - loss: 7.7637e-04 - mae: 0.0235 - val_loss: 8.4499e-04 - val_mae: 0.0252\n",
681 | "Epoch 54/100\n",
682 | "6/6 [==============================] - 0s 6ms/step - loss: 6.8052e-04 - mae: 0.0226 - val_loss: 8.4043e-04 - val_mae: 0.0253\n",
683 | "Epoch 55/100\n",
684 | "6/6 [==============================] - 0s 7ms/step - loss: 7.3551e-04 - mae: 0.0226 - val_loss: 8.3528e-04 - val_mae: 0.0256\n",
685 | "Epoch 56/100\n",
686 | "6/6 [==============================] - 0s 6ms/step - loss: 6.7654e-04 - mae: 0.0219 - val_loss: 8.8698e-04 - val_mae: 0.0269\n",
687 | "Epoch 57/100\n",
688 | "6/6 [==============================] - 0s 6ms/step - loss: 7.3546e-04 - mae: 0.0232 - val_loss: 9.2346e-04 - val_mae: 0.0250\n",
689 | "Epoch 58/100\n",
690 | "6/6 [==============================] - 0s 6ms/step - loss: 8.0102e-04 - mae: 0.0220 - val_loss: 0.0013 - val_mae: 0.0306\n",
691 | "Epoch 59/100\n",
692 | "6/6 [==============================] - 0s 9ms/step - loss: 0.0011 - mae: 0.0280 - val_loss: 8.3792e-04 - val_mae: 0.0254\n",
693 | "Epoch 60/100\n",
694 | "6/6 [==============================] - 0s 6ms/step - loss: 9.1964e-04 - mae: 0.0252 - val_loss: 9.7688e-04 - val_mae: 0.0251\n",
695 | "Epoch 61/100\n",
696 | "6/6 [==============================] - 0s 7ms/step - loss: 8.3015e-04 - mae: 0.0230 - val_loss: 8.5337e-04 - val_mae: 0.0263\n",
697 | "Epoch 62/100\n",
698 | "6/6 [==============================] - 0s 8ms/step - loss: 8.5996e-04 - mae: 0.0243 - val_loss: 8.3637e-04 - val_mae: 0.0257\n",
699 | "Epoch 63/100\n",
700 | "6/6 [==============================] - 0s 7ms/step - loss: 7.5642e-04 - mae: 0.0237 - val_loss: 9.3817e-04 - val_mae: 0.0250\n",
701 | "Epoch 64/100\n",
702 | "6/6 [==============================] - 0s 8ms/step - loss: 8.8207e-04 - mae: 0.0244 - val_loss: 8.6595e-04 - val_mae: 0.0266\n",
703 | "Epoch 65/100\n",
704 | "6/6 [==============================] - 0s 8ms/step - loss: 7.5487e-04 - mae: 0.0231 - val_loss: 8.5121e-04 - val_mae: 0.0263\n",
705 | "Epoch 66/100\n",
706 | "6/6 [==============================] - 0s 7ms/step - loss: 6.9196e-04 - mae: 0.0222 - val_loss: 0.0011 - val_mae: 0.0253\n",
707 | "Epoch 67/100\n",
708 | "6/6 [==============================] - 0s 8ms/step - loss: 8.5080e-04 - mae: 0.0220 - val_loss: 0.0013 - val_mae: 0.0304\n",
709 | "Epoch 68/100\n",
710 | "6/6 [==============================] - 0s 7ms/step - loss: 9.3323e-04 - mae: 0.0249 - val_loss: 9.2514e-04 - val_mae: 0.0250\n",
711 | "Epoch 69/100\n",
712 | "6/6 [==============================] - 0s 7ms/step - loss: 6.4373e-04 - mae: 0.0214 - val_loss: 9.2058e-04 - val_mae: 0.0273\n",
713 | "Epoch 70/100\n",
714 | "6/6 [==============================] - 0s 6ms/step - loss: 7.4666e-04 - mae: 0.0227 - val_loss: 8.3819e-04 - val_mae: 0.0256\n",
715 | "Epoch 71/100\n",
716 | "6/6 [==============================] - 0s 7ms/step - loss: 7.0207e-04 - mae: 0.0218 - val_loss: 9.1534e-04 - val_mae: 0.0272\n",
717 | "Epoch 72/100\n",
718 | "6/6 [==============================] - 0s 8ms/step - loss: 8.1527e-04 - mae: 0.0244 - val_loss: 0.0010 - val_mae: 0.0252\n",
719 | "Epoch 73/100\n",
720 | "6/6 [==============================] - 0s 8ms/step - loss: 9.5500e-04 - mae: 0.0238 - val_loss: 8.4162e-04 - val_mae: 0.0260\n",
721 | "Epoch 74/100\n",
722 | "6/6 [==============================] - 0s 7ms/step - loss: 7.5374e-04 - mae: 0.0227 - val_loss: 8.4190e-04 - val_mae: 0.0255\n",
723 | "Epoch 75/100\n",
724 | "6/6 [==============================] - 0s 10ms/step - loss: 6.5247e-04 - mae: 0.0211 - val_loss: 8.4693e-04 - val_mae: 0.0254\n",
725 | "Epoch 76/100\n",
726 | "6/6 [==============================] - 0s 10ms/step - loss: 7.3209e-04 - mae: 0.0220 - val_loss: 9.1979e-04 - val_mae: 0.0272\n",
727 | "Epoch 77/100\n",
728 | "6/6 [==============================] - 0s 8ms/step - loss: 8.1132e-04 - mae: 0.0243 - val_loss: 9.6925e-04 - val_mae: 0.0251\n",
729 | "Epoch 78/100\n",
730 | "6/6 [==============================] - 0s 7ms/step - loss: 7.9817e-04 - mae: 0.0236 - val_loss: 9.6579e-04 - val_mae: 0.0277\n",
731 | "Epoch 79/100\n",
732 | "6/6 [==============================] - 0s 6ms/step - loss: 8.7666e-04 - mae: 0.0242 - val_loss: 9.4684e-04 - val_mae: 0.0275\n",
733 | "Epoch 80/100\n",
734 | "6/6 [==============================] - 0s 6ms/step - loss: 7.8953e-04 - mae: 0.0235 - val_loss: 9.6187e-04 - val_mae: 0.0251\n",
735 | "Epoch 81/100\n",
736 | "6/6 [==============================] - 0s 9ms/step - loss: 7.9530e-04 - mae: 0.0222 - val_loss: 9.0846e-04 - val_mae: 0.0271\n",
737 | "Epoch 82/100\n",
738 | "6/6 [==============================] - 0s 7ms/step - loss: 8.5763e-04 - mae: 0.0248 - val_loss: 8.3873e-04 - val_mae: 0.0257\n",
739 | "Epoch 83/100\n",
740 | "6/6 [==============================] - 0s 8ms/step - loss: 6.6067e-04 - mae: 0.0221 - val_loss: 0.0010 - val_mae: 0.0281\n",
741 | "Epoch 84/100\n",
742 | "6/6 [==============================] - 0s 7ms/step - loss: 8.8731e-04 - mae: 0.0254 - val_loss: 8.9134e-04 - val_mae: 0.0251\n",
743 | "Epoch 85/100\n",
744 | "6/6 [==============================] - 0s 7ms/step - loss: 8.1048e-04 - mae: 0.0233 - val_loss: 8.5842e-04 - val_mae: 0.0253\n",
745 | "Epoch 86/100\n",
746 | "6/6 [==============================] - 0s 7ms/step - loss: 6.8501e-04 - mae: 0.0225 - val_loss: 8.4013e-04 - val_mae: 0.0256\n",
747 | "Epoch 87/100\n",
748 | "6/6 [==============================] - 0s 6ms/step - loss: 7.3615e-04 - mae: 0.0218 - val_loss: 0.0012 - val_mae: 0.0295\n",
749 | "Epoch 88/100\n",
750 | "6/6 [==============================] - 0s 8ms/step - loss: 0.0010 - mae: 0.0273 - val_loss: 8.5857e-04 - val_mae: 0.0253\n",
751 | "Epoch 89/100\n",
752 | "6/6 [==============================] - 0s 6ms/step - loss: 8.3378e-04 - mae: 0.0241 - val_loss: 0.0010 - val_mae: 0.0251\n",
753 | "Epoch 90/100\n",
754 | "6/6 [==============================] - 0s 7ms/step - loss: 0.0010 - mae: 0.0247 - val_loss: 8.7960e-04 - val_mae: 0.0268\n",
755 | "Epoch 91/100\n",
756 | "6/6 [==============================] - 0s 7ms/step - loss: 7.5908e-04 - mae: 0.0228 - val_loss: 8.4580e-04 - val_mae: 0.0255\n",
757 | "Epoch 92/100\n",
758 | "6/6 [==============================] - 0s 13ms/step - loss: 6.4425e-04 - mae: 0.0220 - val_loss: 8.4094e-04 - val_mae: 0.0259\n",
759 | "Epoch 93/100\n",
760 | "6/6 [==============================] - 0s 7ms/step - loss: 7.8918e-04 - mae: 0.0237 - val_loss: 0.0010 - val_mae: 0.0251\n",
761 | "Epoch 94/100\n",
762 | "6/6 [==============================] - 0s 6ms/step - loss: 8.5163e-04 - mae: 0.0235 - val_loss: 8.7668e-04 - val_mae: 0.0267\n",
763 | "Epoch 95/100\n",
764 | "6/6 [==============================] - 0s 6ms/step - loss: 7.2157e-04 - mae: 0.0218 - val_loss: 8.6276e-04 - val_mae: 0.0265\n",
765 | "Epoch 96/100\n",
766 | "6/6 [==============================] - 0s 7ms/step - loss: 7.7666e-04 - mae: 0.0237 - val_loss: 9.2775e-04 - val_mae: 0.0251\n",
767 | "Epoch 97/100\n",
768 | "6/6 [==============================] - 0s 6ms/step - loss: 7.9270e-04 - mae: 0.0228 - val_loss: 8.6700e-04 - val_mae: 0.0266\n",
769 | "Epoch 98/100\n",
770 | "6/6 [==============================] - 0s 6ms/step - loss: 7.2839e-04 - mae: 0.0229 - val_loss: 8.3969e-04 - val_mae: 0.0257\n",
771 | "Epoch 99/100\n",
772 | "6/6 [==============================] - 0s 8ms/step - loss: 7.2456e-04 - mae: 0.0230 - val_loss: 8.8624e-04 - val_mae: 0.0252\n",
773 | "Epoch 100/100\n",
774 | "6/6 [==============================] - 0s 8ms/step - loss: 7.3666e-04 - mae: 0.0223 - val_loss: 9.1588e-04 - val_mae: 0.0272\n"
775 | ],
776 | "name": "stdout"
777 | }
778 | ]
779 | },
780 | {
781 | "cell_type": "code",
782 | "metadata": {
783 | "colab": {
784 | "base_uri": "https://localhost:8080/",
785 | "height": 542
786 | },
787 | "id": "m40rR1lA8COE",
788 | "outputId": "865a83fe-c2fd-4cc0-fa6e-553f23e1fca6"
789 | },
790 | "source": [
791 | "fig = go.Figure()\n",
792 | "\n",
793 | "fig.add_trace(go.Scatter(x=np.arange(0, epochs, 1),\n",
794 | " y=history.history['mae'],\n",
795 | " mode='lines',\n",
796 | " name=f'Training MAE',\n",
797 | " marker_size=3, \n",
798 | " line_color='orange'))\n",
799 | "fig.add_trace(go.Scatter(x=np.arange(0, epochs, 1),\n",
800 | " y=history.history['val_mae'],\n",
801 | " mode='lines',\n",
802 | " name=f'Validation MAE',\n",
803 | " line_color='grey'))\n",
804 | "\n",
805 | "fig.update_layout(\n",
806 | " title=\"Network training\",\n",
807 | " xaxis_title=\"Epochs\",\n",
808 | " yaxis_title=f\"Mean Absolute Error\")\n",
809 | "fig.update_layout({'plot_bgcolor': '#f2f8fd' , \n",
810 | " 'paper_bgcolor': 'white',}, \n",
811 | " template='plotly_white')"
812 | ],
813 | "execution_count": null,
814 | "outputs": [
815 | {
816 | "output_type": "display_data",
817 | "data": {
818 | "text/html": [
819 | "\n",
820 | "\n",
821 | "\n",
822 | " \n",
823 | " \n",
824 | " \n",
825 | " \n",
826 | "
\n",
827 | " \n",
865 | "
\n",
866 | "\n",
867 | ""
868 | ]
869 | },
870 | "metadata": {
871 | "tags": []
872 | }
873 | }
874 | ]
875 | },
876 | {
877 | "cell_type": "markdown",
878 | "metadata": {
879 | "id": "gXIxRV1HS2S3"
880 | },
881 | "source": [
882 | "### 4. Compile the hybrid digital twin"
883 | ]
884 | },
885 | {
886 | "cell_type": "code",
887 | "metadata": {
888 | "colab": {
889 | "base_uri": "https://localhost:8080/",
890 | "height": 542
891 | },
892 | "id": "0PjNqTBjAEEi",
893 | "outputId": "ff5267eb-f824-4909-a738-18f3c83b375a"
894 | },
895 | "source": [
896 | "fig = go.Figure()\n",
897 | "fig.add_trace(go.Scatter(x=X_in_train, \n",
898 | " y=X_out_train,\n",
899 | " mode='markers',\n",
900 | " name=f'Modelled Capacity',\n",
901 | " marker=dict(\n",
902 | " size=4,\n",
903 | " color='grey',\n",
904 | " symbol='cross'\n",
905 | " ), \n",
906 | " line_color='crimson'))\n",
907 | "fig.add_trace(go.Scatter(x = X_in_train, \n",
908 | " y=model.predict(X_in_train).reshape(-1),\n",
909 | " mode='lines',\n",
910 | " name=f'Trained Capacity',\n",
911 | " line=dict(color='navy', width=3)))\n",
912 | "fig.update_layout(\n",
913 | " title=\"Network training\",\n",
914 | " xaxis_title=\"Modelled capacity\",\n",
915 | " yaxis_title=\"Δ (Mod. Capacity - Measured Cap.)\")\n",
916 | "\n",
917 | "fig.update_layout(legend=dict(\n",
918 | " yanchor=\"top\",\n",
919 | " y=0.95,\n",
920 | " xanchor=\"left\",\n",
921 | " x=0.45\n",
922 | "))\n",
923 | "fig.update_layout({'plot_bgcolor': '#f2f8fd' , #or azure\n",
924 | "'paper_bgcolor': 'white',}, template='plotly_white')"
925 | ],
926 | "execution_count": null,
927 | "outputs": [
928 | {
929 | "output_type": "display_data",
930 | "data": {
931 | "text/html": [
932 | "\n",
933 | "\n",
934 | "\n",
935 | " \n",
936 | " \n",
937 | " \n",
938 | " \n",
939 | "
\n",
940 | " \n",
978 | "
\n",
979 | "\n",
980 | ""
981 | ]
982 | },
983 | "metadata": {
984 | "tags": []
985 | }
986 | }
987 | ]
988 | },
989 | {
990 | "cell_type": "code",
991 | "metadata": {
992 | "colab": {
993 | "base_uri": "https://localhost:8080/",
994 | "height": 542
995 | },
996 | "id": "5U6sfANYBjxd",
997 | "outputId": "52c95c9b-d4b2-4103-fe3b-85fcce2f607c"
998 | },
999 | "source": [
1000 | "X_twin = X_in + model.predict(X_in).reshape(-1)\n",
1001 | "\n",
1002 | "fig = go.Figure()\n",
1003 | "\n",
1004 | "fig.add_trace(go.Scatter(x=dfb.index, \n",
1005 | " y=X_twin,\n",
1006 | " mode='lines',\n",
1007 | " name=f'Hybrid digial twin',\n",
1008 | " line=dict(color='firebrick', width=3)))\n",
1009 | "fig.add_trace(go.Scatter(x=dfb.index, \n",
1010 | " y=dfb['C. Capacity'],\n",
1011 | " mode='lines',\n",
1012 | " name=f'Modelled capacity',\n",
1013 | " line=dict(color='navy', \n",
1014 | " width=3,\n",
1015 | " dash='dash')))\n",
1016 | "\n",
1017 | "fig.add_trace(go.Scatter(x=dfb.index, \n",
1018 | " y=dfb['Capacity'],\n",
1019 | " mode='markers',\n",
1020 | " marker=dict(\n",
1021 | " size=4,\n",
1022 | " color='grey',\n",
1023 | " symbol='cross'\n",
1024 | " ),\n",
1025 | " name=f'Observed capacity',\n",
1026 | " line_color='navy'))\n",
1027 | "fig.update_layout(\n",
1028 | " title=\"Comparison of hybrid twin with other models\",\n",
1029 | " xaxis_title=\"Cycles\",\n",
1030 | " yaxis_title=\"Capacity in Ahr\")\n",
1031 | "fig.update_layout(legend=dict(\n",
1032 | " yanchor=\"top\",\n",
1033 | " y=0.95,\n",
1034 | " xanchor=\"left\",\n",
1035 | " x=0.77\n",
1036 | "))\n",
1037 | "fig.update_layout({'plot_bgcolor': '#f2f8fd',\n",
1038 | " 'paper_bgcolor': 'white',}, \n",
1039 | " template='plotly_white')"
1040 | ],
1041 | "execution_count": null,
1042 | "outputs": [
1043 | {
1044 | "output_type": "display_data",
1045 | "data": {
1046 | "text/html": [
1047 | "\n",
1048 | "\n",
1049 | "\n",
1050 | " \n",
1051 | " \n",
1052 | " \n",
1053 | " \n",
1054 | "
\n",
1055 | " \n",
1093 | "
\n",
1094 | "\n",
1095 | ""
1096 | ]
1097 | },
1098 | "metadata": {
1099 | "tags": []
1100 | }
1101 | }
1102 | ]
1103 | },
1104 | {
1105 | "cell_type": "markdown",
1106 | "metadata": {
1107 | "id": "lt2L-FqWS2S3"
1108 | },
1109 | "source": [
1110 | "## 5. Prediction whit hybrid twin model\n"
1111 | ]
1112 | },
1113 | {
1114 | "cell_type": "code",
1115 | "metadata": {
1116 | "id": "pf-mRrfdJ5ek"
1117 | },
1118 | "source": [
1119 | "cycles = np.arange(168,500,1)\n",
1120 | "temperature = dfb['Temperature_measured'].iloc[167]\n",
1121 | "time = dfb['Time'].iloc[167]\n",
1122 | "K = 0.13\n",
1123 | "L_e = 1-e**(-K*cycles*temperature/time)\n",
1124 | "X_in_e = -(L_e*dfb['Capacity'].iloc[0:1].values[0]) + dfb['Capacity'].iloc[0:1].values[0]\n",
1125 | "C_twin_e = X_in_e + model.predict(X_in_e).reshape(-1)"
1126 | ],
1127 | "execution_count": null,
1128 | "outputs": []
1129 | },
1130 | {
1131 | "cell_type": "code",
1132 | "metadata": {
1133 | "colab": {
1134 | "base_uri": "https://localhost:8080/",
1135 | "height": 542
1136 | },
1137 | "id": "4WJpp8TzMvPq",
1138 | "outputId": "0840db8c-cb67-4a77-cb1e-dedfb990b2d8"
1139 | },
1140 | "source": [
1141 | "X_twin = X_in + model.predict(X_in).reshape(-1)\n",
1142 | "\n",
1143 | "fig = go.Figure()\n",
1144 | "\n",
1145 | "fig.add_trace(go.Scatter(x=cycles, \n",
1146 | " y=X_in_e,\n",
1147 | " mode='lines',\n",
1148 | " name=f'C modelled (predicted)',\n",
1149 | " line=dict(color='navy', \n",
1150 | " width=3,\n",
1151 | " dash='dash')))\n",
1152 | "fig.add_trace(go.Scatter(x=cycles, \n",
1153 | " y=C_twin_e,\n",
1154 | " mode='lines',\n",
1155 | " name=f'C Digital twin (predicted)',\n",
1156 | " line=dict(color='crimson', \n",
1157 | " width=3,\n",
1158 | " dash='dash'\n",
1159 | " )))\n",
1160 | "\n",
1161 | "fig.add_trace(go.Scatter(x=dfb.index, \n",
1162 | " y=X_twin,\n",
1163 | " mode='lines',\n",
1164 | " name=f'C Digital twin',\n",
1165 | " line=dict(color='crimson',\n",
1166 | " width=2)))\n",
1167 | "fig.add_trace(go.Scatter(x=dfb.index, \n",
1168 | " y=dfb['C. Capacity'],\n",
1169 | " mode='lines',\n",
1170 | " name=f'C modelled',\n",
1171 | " line=dict(color='navy', \n",
1172 | " width=2)))\n",
1173 | "\n",
1174 | "fig.update_layout(\n",
1175 | " title=\"Battery capacity prediction\",\n",
1176 | " xaxis_title=\"Cycles\",\n",
1177 | " yaxis_title=\"Battery capacity [Ahr]\")\n",
1178 | "fig.update_layout(legend=dict(\n",
1179 | " yanchor=\"top\",\n",
1180 | " y=0.95,\n",
1181 | " xanchor=\"left\",\n",
1182 | " x=0.72\n",
1183 | "))\n",
1184 | "fig.update_layout({'plot_bgcolor': '#f2f8fd',\n",
1185 | " 'paper_bgcolor': 'white',}, \n",
1186 | " template='plotly_white')"
1187 | ],
1188 | "execution_count": null,
1189 | "outputs": [
1190 | {
1191 | "output_type": "display_data",
1192 | "data": {
1193 | "text/html": [
1194 | "\n",
1195 | "\n",
1196 | "\n",
1197 | " \n",
1198 | " \n",
1199 | " \n",
1200 | " \n",
1201 | "
\n",
1202 | " \n",
1240 | "
\n",
1241 | "\n",
1242 | ""
1243 | ]
1244 | },
1245 | "metadata": {
1246 | "tags": []
1247 | }
1248 | }
1249 | ]
1250 | }
1251 | ]
1252 | }
--------------------------------------------------------------------------------