{ "cells": [ { "cell_type": "markdown", "id": "2ec166b9", "metadata": {}, "source": [ "# KPI Decomposition Deep Dive\n", "\n", "Break down production, mobilisation, sequencing, and downtime metrics for the tiny7 scenario." ] }, { "cell_type": "code", "execution_count": 1, "id": "5f2971e1", "metadata": { "execution": { "iopub.execute_input": "2026-10-05T12:57:12.650851Z", "iopub.status.busy": "2026-10-05T12:57:12.650745Z", "iopub.status.idle": "2026-10-05T12:57:15.331066Z", "shell.execute_reply": "2026-10-05T12:57:15.329925Z" } }, "outputs": [], "source": [ "import sys\n", "from pathlib import Path\n", "\n", "PROJECT_ROOT = Path.cwd().resolve()\n", "while PROJECT_ROOT != PROJECT_ROOT.parent and not (PROJECT_ROOT / \"pyproject.toml\").exists():\n", " PROJECT_ROOT = PROJECT_ROOT.parent\n", "if not (PROJECT_ROOT / \"pyproject.toml\").exists():\n", " raise RuntimeError(\n", " \"Notebook must be executed within a FHOPS checkout (pyproject.toml not found).\"\n", " )\n", "if str(PROJECT_ROOT) not in sys.path:\n", " sys.path.insert(0, str(PROJECT_ROOT))\n", "\n", "import pandas as pd\n", "\n", "from docs.examples.analytics import utils\n", "from fhops.evaluation import playback_summary_metrics\n", "from fhops.scenario.io import load_scenario\n", "\n", "SCENARIO = PROJECT_ROOT / \"examples/tiny7/scenario.yaml\"\n", "ASSIGNMENTS = PROJECT_ROOT / \"tests/fixtures/playback/tiny7_assignments.csv\"\n", "playback_tables = utils.load_deterministic_playback(SCENARIO, ASSIGNMENTS)" ] }, { "cell_type": "markdown", "id": "b7f495ea", "metadata": {}, "source": [ "## Scenario Overview" ] }, { "cell_type": "code", "execution_count": 2, "id": "d3062e03", "metadata": { "execution": { "iopub.execute_input": "2026-10-05T12:57:15.332743Z", "iopub.status.busy": "2026-10-05T12:57:15.332586Z", "iopub.status.idle": "2026-10-05T12:57:15.353754Z", "shell.execute_reply": "2026-10-05T12:57:15.353281Z" } }, "outputs": [ { "data": { "text/plain": [ "samples 1.000000\n", "total_production 17658.811008\n", "total_hours 576.000000\n", "mobilisation_cost 479.160000\n", "average_utilisation 0.380952\n", "dtype: float64" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "scenario = load_scenario(SCENARIO)\n", "summary_metrics = playback_summary_metrics(playback_tables.shift, playback_tables.day)\n", "pd.Series(summary_metrics)" ] }, { "cell_type": "markdown", "id": "f1a4b6ec", "metadata": {}, "source": [ "## Production vs Mobilisation" ] }, { "cell_type": "code", "execution_count": 3, "id": "fdf3a10c", "metadata": { "execution": { "iopub.execute_input": "2026-10-05T12:57:15.355379Z", "iopub.status.busy": "2026-10-05T12:57:15.355241Z", "iopub.status.idle": "2026-10-05T12:57:15.361536Z", "shell.execute_reply": "2026-10-05T12:57:15.360852Z" } }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
dayproduction_unitsmobilisation_cost
012007.9127550.00
121438.0865290.00
233193.79581053.24
344047.344153106.48
452969.193965106.48
561595.68779953.24
672406.789997159.72
\n", "
" ], "text/plain": [ " day production_units mobilisation_cost\n", "0 1 2007.912755 0.00\n", "1 2 1438.086529 0.00\n", "2 3 3193.795810 53.24\n", "3 4 4047.344153 106.48\n", "4 5 2969.193965 106.48\n", "5 6 1595.687799 53.24\n", "6 7 2406.789997 159.72" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "prod_metrics = playback_tables.day[[\"day\", \"production_units\", \"mobilisation_cost\"]]\n", "prod_metrics" ] }, { "cell_type": "markdown", "id": "98676f1a", "metadata": {}, "source": [ "## Sequencing & Downtime" ] }, { "cell_type": "code", "execution_count": 4, "id": "045fb3b6", "metadata": { "execution": { "iopub.execute_input": "2026-10-05T12:57:15.362884Z", "iopub.status.busy": "2026-10-05T12:57:15.362762Z", "iopub.status.idle": "2026-10-05T12:57:15.368138Z", "shell.execute_reply": "2026-10-05T12:57:15.367427Z" } }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
daysequencing_violationsdowntime_hoursdowntime_events
0100.00
1200.00
2300.00
3400.00
4500.00
5600.00
6700.00
\n", "
" ], "text/plain": [ " day sequencing_violations downtime_hours downtime_events\n", "0 1 0 0.0 0\n", "1 2 0 0.0 0\n", "2 3 0 0.0 0\n", "3 4 0 0.0 0\n", "4 5 0 0.0 0\n", "5 6 0 0.0 0\n", "6 7 0 0.0 0" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "seq_metrics = playback_tables.day[\n", " [\"day\", \"sequencing_violations\", \"downtime_hours\", \"downtime_events\"]\n", "]\n", "seq_metrics" ] }, { "cell_type": "markdown", "id": "9d222853", "metadata": {}, "source": [ "## Visualise Utilisation" ] }, { "cell_type": "code", "execution_count": 5, "id": "1566b1e1", "metadata": { "execution": { "iopub.execute_input": "2026-10-05T12:57:15.369590Z", "iopub.status.busy": "2026-10-05T12:57:15.369442Z", "iopub.status.idle": "2026-10-05T12:57:15.392115Z", "shell.execute_reply": "2026-10-05T12:57:15.391443Z" } }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "
\n", "" ], "text/plain": [ "alt.Chart(...)" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "chart = utils.plot_utilisation_heatmap(playback_tables.shift)\n", "chart" ] } ], "metadata": { "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.11.16" } }, "nbformat": 4, "nbformat_minor": 5 }