From 4e02ecb0684409dabcd4000695a211121e2d31d0 Mon Sep 17 00:00:00 2001 From: Fridolin Glatter Date: Thu, 29 Aug 2024 11:29:26 +0200 Subject: [PATCH] Add second part of transport tutorial --- .../transport/linopy_transport_scenario.ipynb | 386 ++++++++++++++++++ 1 file changed, 386 insertions(+) create mode 100644 tutorial/transport/linopy_transport_scenario.ipynb diff --git a/tutorial/transport/linopy_transport_scenario.ipynb b/tutorial/transport/linopy_transport_scenario.ipynb new file mode 100644 index 00000000..895cd396 --- /dev/null +++ b/tutorial/transport/linopy_transport_scenario.ipynb @@ -0,0 +1,386 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Tutorial 2 for Python\n", + "\n", + "## Make a scenario of Dantzig's Transport Problem using the *ix modeling platform* (ixmp4)\n", + "\n", + "\n", + "\n", + "### Aim and scope of the tutorial\n", + "\n", + "This tutorial uses the transport problem scenario developed in the first tutorial and illustrates how the ixmp4 framework can be applied for scenario analysis in the sense often used in economic or environmental modeling: develop a baseline, create a clone from the baseline with altered parameters or assumptions, and solve the new model. Then, compare the results from the original and altered scenario versions.\n", + "\n", + "In particular, this tutorial will take you through the following steps:\n", + "\n", + "0. Launch an `ixmp4.Platform` instance and retrieve the `ixmp4.Run` instance of Dantzig's transport problem\n", + "0. Retrieve some data from the `Run` for illustration of filters\n", + "0. Make a clone of the baseline scenario, then check out the clone and make changes: \n", + " in this case, add a new demand location and transport costs to that city\n", + "0. Solve the new scenario\n", + "0. Display the solution of both the baseline and the new scenario" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Launching the `platform` and loading a `Run` from the `ixmp4` database instance\n", + "\n", + "We launch a platform instance and display all models/scenarios currently stored in the connected database instance. We use the same local database we created in [part 1](linopy_transport.ipynb), so please see there for how to create such a local database. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import ixmp4\n", + "\n", + "platform = ixmp4.Platform(\"tutorial-test\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Tabulate all Runs in the database\n", + "# Per default, only \"default\" Runs are tabulated\n", + "platform.runs.tabulate(default_only=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Model and scenario name we used for Dantzig's transport problem in part 1\n", + "model = \"transport problem\"\n", + "scenario = \"standard\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If you have run the first part of tutorial before, the existing `Run` should have appeared, and we can load it.\n", + "Uncomment and run the following lines as appropriate:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Load the default version of the Run created in the first tutorial\n", + "# run = platform.runs.get(model=model, scenario=scenario)\n", + "\n", + "# If you already solved this Run, remember to remove its solution!\n", + "# run.optimization.remove_solution()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If the `Run` did not appear (e.g. because you are starting with this tutorial), we can use a function that creates the `Run` from scratch in one step and solve it as usual:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from tutorial.transport.utils import create_default_dantzig_run\n", + "\n", + "run = create_default_dantzig_run(platform=platform)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from tutorial.transport.dantzig_model_linopy import (\n", + " create_dantzig_model,\n", + " read_dantzig_solution,\n", + ")\n", + "\n", + "linopy_model = create_dantzig_model(run=run)\n", + "linopy_model.solve(\"highs\")\n", + "\n", + "read_dantzig_solution(model=linopy_model, run=run)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Retrieve some data from the run for illustration of filters\n", + "\n", + "Before cloning a run and editing data, this section illustrates two-and-a-half methods to retrieve data for a parameter from a run." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Load the distance Parameter\n", + "d = run.optimization.parameters.get(\"d\")\n", + "d" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This is an `ixmp4.Parameter` object! It's useful to interact with if you want to edit the information stored in the database about it. You could e.g. add `.docs` to it so that you can always look up in the database what this object does.\n", + "For interacting with the modeling data, it's more useful to interact with the `.data` attribute, which stores the actual data: " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "d.data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This is a dictionary because that's easier to store in a database. For ease of access, you may want to convert it to a `pandas.DataFrame`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "\n", + "d_data = pd.DataFrame(d.data)\n", + "\n", + "# Show only the distances for connections from Seattle by filtering the pandas.DataFrame\n", + "d_data[d_data[\"i\"] == \"seattle\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# NOTE We currently don't support loading only specific parameter elements.\n", + "# We always load the whole parameter and can then select from the data as usual with\n", + "# e.g. pandas." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For faster access or more complex filtering, you can then use all familiar `pandas` features. For example, list only distances from Seattle to specific other cities:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "d_data.loc[(d_data[\"i\"] == \"seattle\") & (d_data[\"j\"].isin([\"chicago\", \"topeka\"]))]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Please note that `pandas` recommends to use [advanced indexing](https://pandas.pydata.org/pandas-docs/stable/user_guide/advanced.html#advanced-advanced-hierarchical) if you find yourself using more than three conditionals in `.loc[]`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Make a clone of the baseline scenario, then check out the clone and edit the scenario\n", + "\n", + "For illustration of a scenario analysis workflow, we add a new demand location ``detroit`` and add a demand level and transport costs to that city.\n", + "Because the production capacity does not allow much slack for increased production, we also reduce the demand level in ``chicago``." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Create a new Run by cloning the existing one (without keeping the solution)\n", + "run_detroit = platform.runs.clone(\n", + " run_id=run.id, model=model, scenario=\"detroit\", keep_solution=False\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Reduce demand in chicago\n", + "run_detroit.optimization.parameters.get(\"b\").add(\n", + " {\"j\": [\"chicago\"], \"values\": [200], \"units\": [\"cases\"]}\n", + ")\n", + "\n", + "# Add a new city with demand and distances\n", + "run_detroit.optimization.indexsets.get(\"j\").add(\"detroit\")\n", + "run_detroit.optimization.parameters.get(\"b\").add(\n", + " {\"j\": [\"detroit\"], \"values\": [150], \"units\": [\"cases\"]}\n", + ")\n", + "run_detroit.optimization.parameters.get(\"d\").add(\n", + " {\n", + " \"i\": [\"seattle\", \"san-diego\"],\n", + " \"j\": [\"detroit\", \"detroit\"],\n", + " \"values\": [1.7, 1.9],\n", + " \"units\": [\"km\", \"km\"],\n", + " }\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Solve the new scenario" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "linopy_model_detroit = create_dantzig_model(run=run_detroit)\n", + "linopy_model_detroit.solve(\"highs\")\n", + "\n", + "read_dantzig_solution(model=linopy_model_detroit, run=run_detroit)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Display and analyze the results\n", + "\n", + "For comparison between the baseline `Run`, i.e., the original transport problem, and the \"detroit\" `Run`, we show the solution for both cases." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "jupyter": { + "name": "scen-z" + } + }, + "outputs": [], + "source": [ + "# Display the objective value of the solution in the baseline Run\n", + "run.optimization.variables.get(\"z\").levels" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "jupyter": { + "name": "scen-detroit-z" + } + }, + "outputs": [], + "source": [ + "# Display the objective value of the solution in the \"detroit\" Run\n", + "run_detroit.optimization.variables.get(\"z\").levels" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Display quantities transported from canning plants to demand locations in \"baseline\"\n", + "pd.DataFrame(run.optimization.variables.get(\"x\").data)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Display quantities transported from canning plants to demand locations in \"detroit\"\n", + "pd.DataFrame(run_detroit.optimization.variables.get(\"x\").data)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Display quantities and marginals (=shadow prices) of the demand balance constraints\n", + "# in \"baseline\"\n", + "run.optimization.equations.get(\"demand\").data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Display quantities and marginals (=shadow prices) of the demand balance constraints\n", + "# in \"detroit\"\n", + "run_detroit.optimization.equations.get(\"demand\").data" + ] + } + ], + "metadata": { + "anaconda-cloud": {}, + "kernelspec": { + "display_name": "Python 3", + "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.5" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +}