{ "cells": [ { "cell_type": "markdown", "id": "d3889873-304a-4138-8d26-71cd9df08281", "metadata": {}, "source": [ "## 🚀 Quick Start" ] }, { "cell_type": "markdown", "id": "58b8d4b8", "metadata": {}, "source": [ "**Concepts:** [Problem](../problem.md) · [Optimization approaches](../routers.md#optimization-approaches) **Advanced API:** [🚀 Quick Start](lo00_quickstart.ipynb)" ] }, { "cell_type": "markdown", "id": "b613f213-609c-4fab-b6e2-6da86ea72cb6", "metadata": {}, "source": [ "This notebook shows the _minimum steps_ needed to optimize an electrical network for a wind farm with OptiWindNet." ] }, { "cell_type": "markdown", "id": "56bae6b4-9cf4-4e01-83ce-e17d4b788b18", "metadata": {}, "source": [ "### Steps" ] }, { "cell_type": "markdown", "id": "77861cab-daa3-4bf5-98a9-27d52ad7cecb", "metadata": {}, "source": [ "An OptiWindNet network optimization involves two components:\n", "\n", "- A [WindFarmNetwork](../autoapi/optiwindnet/api/index.rst#optiwindnet.api.WindFarmNetwork) containing the problem data and options.\n", "- A [Router](../autoapi/optiwindnet/api/index.rst#optiwindnet.api.Router) defining the algorithm used to solve it.\n", "\n", "The network optimization can be performed in the following steps:\n", "\n", "1. Create a [WindFarmNetwork](../autoapi/optiwindnet/api/index.rst#optiwindnet.api.WindFarmNetwork) instance\n", "2. Define a router\n", "3. Run the optimization and get the results" ] }, { "cell_type": "markdown", "id": "48145102", "metadata": {}, "source": [ "#### 1. Create a `WindFarmNetwork` instance" ] }, { "cell_type": "markdown", "id": "fac8dccf", "metadata": {}, "source": [ "The [WindFarmNetwork](../autoapi/optiwindnet/api/index.rst#optiwindnet.api.WindFarmNetwork) component stores the problem data, invokes its [Router](../autoapi/optiwindnet/api/index.rst#optiwindnet.api.Router) during optimization, and stores the resulting topology and routed network. See [📦 WindFarmNetwork](hi10_windfarmnetwork.ipynb) for its key methods." ] }, { "cell_type": "code", "execution_count": 1, "id": "2f1ecb89-84e4-4316-9db9-dbaf5b282bf0", "metadata": {}, "outputs": [], "source": [ "from optiwindnet.api import WindFarmNetwork" ] }, { "cell_type": "markdown", "id": "e0765d5c", "metadata": {}, "source": [ "##### Load location data" ] }, { "cell_type": "markdown", "id": "2b278cc9", "metadata": {}, "source": [ "> Note: For details about _OptiWindNet_'s [load_repository()](../autoapi/optiwindnet/importer/index.rst#optiwindnet.importer.load_repository), check [🗂️ Locations](hi12_locations.ipynb)." ] }, { "cell_type": "code", "execution_count": 2, "id": "1f2d7a49-6c57-49e9-bb85-cad2eb08d350", "metadata": {}, "outputs": [], "source": [ "from optiwindnet.api import load_repository\n", "\n", "locations = load_repository()\n", "L = locations.doggerA" ] }, { "cell_type": "markdown", "id": "082fda01", "metadata": {}, "source": [ "Initialize a [WindFarmNetwork](../autoapi/optiwindnet/api/index.rst#optiwindnet.api.WindFarmNetwork) instance with a prebuilt `L` and a desired maximum cable capacity _(See [Data Input](hi11_data_input.ipynb) for various input formats.)_" ] }, { "cell_type": "code", "execution_count": 3, "id": "1cefe795", "metadata": {}, "outputs": [], "source": [ "wfn = WindFarmNetwork(L=L, cables=8)\n", "results = [] # (router, topology, length) of each example" ] }, { "cell_type": "markdown", "id": "a8fd46b9", "metadata": {}, "source": [ "Plot the location to confirm that `wfn` contains the intended data." ] }, { "cell_type": "markdown", "id": "753feb91", "metadata": {}, "source": [ "> **Tip.** In a notebook, put `wfn` as the last line of a cell to plot its current state.\n", ">\n", "> - Before optimization (no `G` yet), it renders the location geometry `L`.\n", "> - If `G` exists (for example, after a call to [optimize()](../autoapi/optiwindnet/api/index.rst#optiwindnet.api.WindFarmNetwork.optimize)), it automatically renders `G`.\n", ">\n", "> Call [wfn.plot_location()](../autoapi/optiwindnet/api/index.rst#optiwindnet.api.WindFarmNetwork.plot_location) explicitly to plot only the location geometry. See [📊 Plotting](hi14_plotting.ipynb) for the available plotting methods." ] }, { "cell_type": "code", "execution_count": 4, "id": "e0e31dfa", "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\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", " \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" ], "text/plain": [ "" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "wfn" ] }, { "cell_type": "markdown", "id": "64a931d6-a64a-4e6b-a36d-6b103a8216dc", "metadata": {}, "source": [ "#### 2. Define a `Router`" ] }, { "cell_type": "markdown", "id": "100bfc85-3785-435e-839e-1a6adef38c23", "metadata": {}, "source": [ "Three built-in routers are available in _OptiWindNet_, one per optimization approach:\n", "\n", "| Router | Approach |\n", "| --- | --- |\n", "| [EWRouter](../autoapi/optiwindnet/api/index.rst#optiwindnet.api.EWRouter) | constructive heuristic — sub-second |\n", "| [HGSRouter](../autoapi/optiwindnet/api/index.rst#optiwindnet.api.HGSRouter) | meta-heuristic — configurable time budget |\n", "| [MILPRouter](../autoapi/optiwindnet/api/index.rst#optiwindnet.api.MILPRouter) | exact optimization — reports an optimality gap |\n", "\n", "Their trade-offs are compared in [Optimization approaches](../routers.md#optimization-approaches). See [📦 WindFarmNetwork](hi10_windfarmnetwork.ipynb) for details about routers." ] }, { "cell_type": "markdown", "id": "d949bb52-5c2b-467b-954b-1ff0be0f717c", "metadata": {}, "source": [ "##### EWRouter" ] }, { "cell_type": "markdown", "id": "90750db9-b0e4-48cf-ade0-339d2ef14d84", "metadata": {}, "source": [ "Create an [EWRouter](../autoapi/optiwindnet/api/index.rst#optiwindnet.api.EWRouter) instance with no arguments to use its defaults. Every argument is optional." ] }, { "cell_type": "code", "execution_count": 5, "id": "b6a65515-8573-4fb7-9f14-f741d78eb361", "metadata": {}, "outputs": [], "source": [ "from optiwindnet.api import EWRouter" ] }, { "cell_type": "code", "execution_count": 6, "id": "e4218728-24ee-4e12-a597-0aff167ce84d", "metadata": {}, "outputs": [], "source": [ "ew_router = EWRouter()" ] }, { "cell_type": "markdown", "id": "e52ecc51", "metadata": {}, "source": [ "##### HGSRouter" ] }, { "cell_type": "markdown", "id": "25b453c9", "metadata": {}, "source": [ "To use this router, create an instance of the [HGSRouter](../autoapi/optiwindnet/api/index.rst#optiwindnet.api.HGSRouter) class. The _only required argument_ is [time_limit](../autoapi/optiwindnet/api/index.rst#optiwindnet.api.HGSRouter.time_limit), which defines how long the optimization is allowed to run (in seconds)." ] }, { "cell_type": "code", "execution_count": 7, "id": "3c842d29", "metadata": {}, "outputs": [], "source": [ "from optiwindnet.api import HGSRouter" ] }, { "cell_type": "code", "execution_count": 8, "id": "346b0030", "metadata": {}, "outputs": [], "source": [ "hgs_router = HGSRouter(time_limit=1, seed=1234567)" ] }, { "cell_type": "markdown", "id": "8694bc98", "metadata": {}, "source": [ "##### MILPRouter" ] }, { "cell_type": "markdown", "id": "1873d050", "metadata": {}, "source": [ "Create a [MILPRouter](../autoapi/optiwindnet/api/index.rst#optiwindnet.api.MILPRouter) instance with the following required arguments:\n", "\n", "- [solver_name](../autoapi/optiwindnet/api/index.rst#optiwindnet.api.MILPRouter.solver_name): the MILP solver to use (e.g., `'ortools.cp_sat'`, `'gurobi'`, `'ortools.cbcbox'`)\n", "- [time_limit](../autoapi/optiwindnet/api/index.rst#optiwindnet.api.MILPRouter.time_limit): maximum time allowed for solving (in seconds)\n", "- [mip_gap](../autoapi/optiwindnet/api/index.rst#optiwindnet.api.MILPRouter.mip_gap): acceptable optimality gap (e.g., `0.005` = 0.5%)" ] }, { "cell_type": "markdown", "id": "cc4966be", "metadata": {}, "source": [ "Optional arguments:\n", "\n", "- [verbose](../autoapi/optiwindnet/api/index.rst#optiwindnet.api.MILPRouter.verbose) _(default: False)_: set to `True` to display solver progress and detailed logs\n", "- [solver_options](../autoapi/optiwindnet/api/index.rst#optiwindnet.api.MILPRouter.solver_options): dictionary of solver-specific parameters\n", "\n", "The requested solution properties (e.g., topology, feeder counts) are not router arguments: they are the network's [constraints](../autoapi/optiwindnet/api/index.rst#optiwindnet.api.WindFarmNetwork.constraints) ([NetworkConstraints](../autoapi/optiwindnet/constraints/index.rst#optiwindnet.constraints.NetworkConstraints)), which every router enforces." ] }, { "cell_type": "code", "execution_count": 9, "id": "9362998f", "metadata": {}, "outputs": [], "source": [ "from optiwindnet.api import MILPRouter" ] }, { "cell_type": "code", "execution_count": 10, "id": "052b93cb", "metadata": {}, "outputs": [], "source": [ "milp_router = MILPRouter(\n", " solver_name='ortools.cp_sat',\n", " time_limit=20,\n", " mip_gap=0.005,\n", " solver_options={'random_seed': 1234567},\n", ")" ] }, { "cell_type": "markdown", "id": "77b319ef", "metadata": {}, "source": [ "#### 3. Run the optimization" ] }, { "cell_type": "markdown", "id": "a1993761", "metadata": {}, "source": [ "##### EWRouter (sub-second)" ] }, { "cell_type": "code", "execution_count": 11, "id": "db91b101-c598-4a53-859d-d2bd540c1bef", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "19.8 ms ± 609 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" ] } ], "source": [ "%%timeit\n", "res_ew = wfn.optimize(router=ew_router)" ] }, { "cell_type": "code", "execution_count": 12, "id": "d534a69f-957b-4966-9d32-b326e24ab888", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'router': 'EWRouter', 'capacity': 8, 'method': 'rootlust', 'iterations': 83}" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "wfn.solution_info()" ] }, { "cell_type": "code", "execution_count": 13, "id": "0d340d87-76a2-4537-8267-a5ae267b3ac2", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "('EWRouter', 'branched', '256617.7')" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "results.append(\n", " (\n", " wfn.solution_info()['router'],\n", " wfn.G.graph['topology'].value,\n", " f'{wfn.length():.1f}',\n", " )\n", ")\n", "results[-1]" ] }, { "cell_type": "markdown", "id": "bc45a08e", "metadata": {}, "source": [ "Plot the optimized network.\n", "\n", "> **Note:** Call [wfn.plot()](../autoapi/optiwindnet/api/index.rst#optiwindnet.api.WindFarmNetwork.plot) explicitly to plot the optimized network. See [📊 Plotting](hi14_plotting.ipynb) for the available plotting methods." ] }, { "cell_type": "code", "execution_count": 14, "id": "842e6996-49a7-4354-b28a-c3c042b5a6a3", "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\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", " \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", " \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", " \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", " Σλ = 256 618 m(+1) SS1: 13κ = 8, T = 95\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "wfn" ] }, { "cell_type": "markdown", "id": "95ce79c5", "metadata": {}, "source": [ "> **Note:** Each call to [.optimize()](../autoapi/optiwindnet/api/index.rst#optiwindnet.api.WindFarmNetwork.optimize) on the same [WindFarmNetwork](../autoapi/optiwindnet/api/index.rst#optiwindnet.api.WindFarmNetwork) instance overwrites its stored solution and related information, including the network graph, cost, and length. Compare the updated length or plot with the preceding result to observe the replacement." ] }, { "cell_type": "markdown", "id": "0d00cd7e", "metadata": {}, "source": [ "##### HGSRouter (fast — radial or ringed topology)" ] }, { "cell_type": "code", "execution_count": 15, "id": "181abc1b", "metadata": {}, "outputs": [], "source": [ "res_hgs = wfn.optimize(router=hgs_router)" ] }, { "cell_type": "code", "execution_count": 16, "id": "92dfde90", "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "{'router': 'HGSRouter',\n", " 'capacity': 8,\n", " 'solver_name': 'HGS-CVRP',\n", " 'complete': False,\n", " 'feeders_above_min': None,\n", " 'feeders_exact': False,\n", " 'balanced': False,\n", " 'ringed': False,\n", " 'repair': True,\n", " 'max_retries': 10,\n", " 'nbGranular': 20,\n", " 'mu': 25,\n", " 'lambda_': 40,\n", " 'nbElite': 4,\n", " 'nbClose': 5,\n", " 'nbIterPenaltyManagement': 100,\n", " 'targetFeasible': 0.2,\n", " 'penaltyDecrease': 0.85,\n", " 'penaltyIncrease': 1.2,\n", " 'seed': 1234567,\n", " 'nbIter': 20000,\n", " 'nbIterTraces': 500,\n", " 'timeLimit': 1,\n", " 'useSwapStar': True,\n", " 'runtime': 1.001226878}" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "wfn.solution_info()" ] }, { "cell_type": "code", "execution_count": 17, "id": "ecc63aca", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "('HGSRouter', 'radial', '243214.0')" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "results.append(\n", " (\n", " wfn.solution_info()['router'],\n", " wfn.G.graph['topology'].value,\n", " f'{wfn.length():.1f}',\n", " )\n", ")\n", "results[-1]" ] }, { "cell_type": "code", "execution_count": 18, "id": "02a617ed", "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\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", " \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", " \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", " \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", " Σλ = 243 214 m(+0) SS1: 12κ = 8, T = 95\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "wfn" ] }, { "cell_type": "markdown", "id": "454c5969", "metadata": {}, "source": [ "> Note that the HGSRouter produces a **radial** or **ringed** _topology_, never a **branched** one. The three topologies are defined in [Network topologies](../problem.md#network-topologies); the figure below contrasts them." ] }, { "cell_type": "code", "execution_count": 19, "id": "0251de67-d621-4087-ba09-08d1dfaaeab5", "metadata": {}, "outputs": [], "source": [ "from IPython.display import HTML\n", "\n", "from optiwindnet.api import NetworkConstraints\n", "from optiwindnet.synthetic import toyfarm" ] }, { "cell_type": "code", "execution_count": 20, "id": "78b2a511-03a4-444c-aa4c-3840df2ecbb5", "metadata": {}, "outputs": [ { "data": { "text/html": [ "

Radial

Branched

Ringed

" ], "text/plain": [ "" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "wfn_toy = WindFarmNetwork(L=toyfarm(), cables=4)\n", "opt_milp = {\n", " 'mip_gap': 0.001,\n", " 'time_limit': 5,\n", " 'solver_name': 'ortools.cp_sat',\n", " 'solver_options': {'random_seed': 1234567},\n", "}\n", "\n", "panels = []\n", "for topology in ('radial', 'branched', 'ringed'):\n", " wfn_toy.constraints = NetworkConstraints(topology=topology, feeders='min')\n", " wfn_toy.optimize(router=MILPRouter(**opt_milp))\n", " caption = f'

{topology.capitalize()}

'\n", " svg = wfn_toy.plot(infobox=False, tight=True).data\n", " panels.append(f'
{caption}{svg}
')\n", "\n", "HTML(f'
{\"\".join(panels)}
')" ] }, { "cell_type": "markdown", "id": "fb0f3edb", "metadata": {}, "source": [ "##### MILPRouter" ] }, { "cell_type": "markdown", "id": "5040c87d", "metadata": {}, "source": [ "MILP reports a bound and an optimality gap for its length model; routing detours can increase the delivered length. See [Exact optimization](../routers.md#exact-optimization)." ] }, { "cell_type": "markdown", "id": "f421ae7d", "metadata": {}, "source": [ "> Note:\n", ">\n", "> - [MILPRouter](../autoapi/optiwindnet/api/index.rst#optiwindnet.api.MILPRouter) warm-starts by default: it reuses the solution already stored in the [WindFarmNetwork](../autoapi/optiwindnet/api/index.rst#optiwindnet.api.WindFarmNetwork), and builds one with a fast heuristic when there is none or the stored one does not fit the model — see [Warm-starting](../routers.md#warm-starting).\n", "> - To solve cold instead, pass `warmup=False`; `warmup_time` is the budget for building a warm start.\n", "> - Set `verbose=True` to see warm-start messages in the logs." ] }, { "cell_type": "code", "execution_count": 21, "id": "64e22c2f-1339-48b6-86a8-635874e8b382", "metadata": {}, "outputs": [], "source": [ "res_milp = wfn.optimize(router=milp_router)" ] }, { "cell_type": "code", "execution_count": 22, "id": "c4d1f9b9", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'router': 'MILPRouter',\n", " 'capacity': 8,\n", " 'solver_name': 'ortools.cp_sat',\n", " 'mip_gap': 0.005,\n", " 'time_limit': 20,\n", " 'topology': ,\n", " 'feeder_route': ,\n", " 'balanced': False,\n", " 'runtime': 20.038915,\n", " 'bound': 229556.31575552459,\n", " 'objective': 240847.58781760116,\n", " 'relgap': 0.04688139982795958,\n", " 'termination': 'FEASIBLE'}" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "wfn.solution_info()" ] }, { "cell_type": "code", "execution_count": 23, "id": "acd659f9", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "('MILPRouter', 'branched', '240998.6')" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "results.append(\n", " (\n", " wfn.solution_info()['router'],\n", " wfn.G.graph['topology'].value,\n", " f'{wfn.length():.1f}',\n", " )\n", ")\n", "results[-1]" ] }, { "cell_type": "markdown", "id": "28e8ad3e", "metadata": {}, "source": [ "The two length values describe different stages of the solution. `wfn.solution_info()['objective']` is the length optimized by the MILP on the available-links graph, before route reconstruction. `wfn.length()` sums the physical cable routes after [PathFinder](../autoapi/optiwindnet/pathfinding/index.rst#optiwindnet.pathfinding.PathFinder) introduces any detours required by the navigation mesh. The routed length can therefore be slightly longer. Use `objective` to interpret the solver's bound and relative gap, and `wfn.length()` for the final installed cable length. If cable prices are supplied, `wfn.cost()` reports the cost of the routed network. Length and cost often track each other, but the MILP gap is a guarantee about modeled length, not a bound on cost suboptimality. See [Objective and reported cost](../problem.md#objective-and-reported-cost)." ] }, { "cell_type": "code", "execution_count": 24, "id": "1569125c", "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\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", " \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", " \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", " \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", " Σλ = 240 999 m(+0) SS1: 12κ = 8, T = 95\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "wfn" ] }, { "cell_type": "markdown", "id": "6a7dda5e", "metadata": {}, "source": [ "Compare the results of the three routers:" ] }, { "cell_type": "code", "execution_count": 25, "id": "28225f11", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
routertopologylength (m)
EWRouterbranched256617.7
HGSRouterradial243214.0
MILPRouterbranched240998.6
" ], "text/plain": [ "+------------------------------------+\n", "| router topology length (m) |\n", "+============+==========+============+\n", "| EWRouter | branched | 256617.7 |\n", "| HGSRouter | radial | 243214.0 |\n", "| MILPRouter | branched | 240998.6 |\n", "+------------+----------+------------+" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from prettyTables import Table\n", "\n", "Table(\n", " results,\n", " headers=('router', 'topology', 'length (m)'),\n", ")" ] } ], "metadata": { "language_info": { "name": "python" } }, "nbformat": 4, "nbformat_minor": 5 }