{
"cells": [
{
"cell_type": "markdown",
"id": "f7b072be-a181-48f3-9a7c-5d8f16012d49",
"metadata": {},
"source": [
"# WindFarmNetwork/Router"
]
},
{
"cell_type": "markdown",
"id": "2ce7a38d",
"metadata": {},
"source": [
"This notebook is a practical guide to *OptiWindNet*’s Network/Router API (i.e. high-level API). It covers creating a wind farm model, using different routers (`EWRouter`, `HGSRouter`, `MILPRouter`), and applying key `WindFarmNetwork` functionalities. Specifically, we will:\n",
"\n",
"* Explore the `WindFarmNetwork` class\n",
"* Compare available routers and their use cases\n",
"* Run a complete optimization example"
]
},
{
"cell_type": "markdown",
"id": "80c8c206-b99d-4ec4-a593-a10e8fbd57ed",
"metadata": {},
"source": [
"## ✅ WindFarmNetwork"
]
},
{
"cell_type": "markdown",
"id": "2196d3c5",
"metadata": {},
"source": [
"The `WindFarmNetwork` class is the **central user-facing component** in the OptiWindNet Network/Router-API. It is flexible and extensible, supporting multiple input formats and routers (electrical network optimizers), and is used to model and optimize the electrical network of a wind farm."
]
},
{
"cell_type": "markdown",
"id": "a8acf159",
"metadata": {},
"source": [
"### To create a `WindFarmNetwork` instance:\n",
"\n",
"**Required:**\n",
"\n",
"* Turbine and substation coordinates *(see [Data Input](a01_data_input.ipynb) for formats)*\n",
"* Cable data (capacities and costs, or at least maximum capacity as a single number)\n",
"\n",
"**Optional:**\n",
"\n",
"* Borders and obstacles: to add spatial constraints\n",
"* Router: optimization strategy (defaults to `EWRouter` if not specified)\n",
"* verbose: log/hide logging messages (default to `False`)."
]
},
{
"cell_type": "markdown",
"id": "4c944a01",
"metadata": {},
"source": [
"### Key Responsibilities"
]
},
{
"cell_type": "markdown",
"id": "d128ab24",
"metadata": {},
"source": [
"| Feature | Description |\n",
"| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |\n",
"| **Initialization & Parsing** | Accepts turbine/substation coordinates or the Location geometry `L` (Can be constructed from YAML, PBF, or WindIO formats for integration with real-world datasets), validates cable data, and constructs the internal graph structure. |\n",
"| **Optimization** | Interfaces smoothly with different routers (`EWRouter`, `HGSRouter`, `MILPRouter`) to optimize the network (cable routing) for cost and cable length. |\n",
"| **Visualization** | Provides plotting functions for location geometry, links, mesh, and the optimized network. |\n",
"| **Gradient & Update Graph** | Computes gradients for optimization and allows updating the electrical network using a compact \"terse link\" format. |\n"
]
},
{
"cell_type": "markdown",
"id": "a41b0f1b",
"metadata": {},
"source": [
"### Example Workflow"
]
},
{
"cell_type": "markdown",
"id": "1d7ab495",
"metadata": {},
"source": [
"```python\n",
"# Initialize with coordinates and cable types\n",
"wfn = WindFarmNetwork(\n",
" cables=[(2, 1500.0), (5, 1800.0)],\n",
" turbinesC=...,\n",
" substationsC=...,\n",
")\n",
"\n",
"# Optimize electrical network using the default router (EWRouter)\n",
"wfn.optimize()\n",
"\n",
"# Access total cost or cable length\n",
"total_cost = wfn.cost()\n",
"total_length = wfn.length()\n",
"\n",
"# Visualize network\n",
"wfn.plot() # or simply wfn if you are running on notebooks\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "dcf9eec1",
"metadata": {},
"source": [
"## 🧭 **Router**"
]
},
{
"cell_type": "markdown",
"id": "ffd963f6",
"metadata": {},
"source": [
"In OptiWindNet, a `Router` is used to compute the optimal network (cable routing) of the wind farm’s electrical network. Given turbine and substation positions (layout), available cable options, and routing constraints, a router determines *which turbines connect to which substations* and how cables should be laid to *minimize length (and consequently cost)*."
]
},
{
"cell_type": "markdown",
"id": "d9f536d0",
"metadata": {},
"source": [
"### Currently Available Routers"
]
},
{
"cell_type": "markdown",
"id": "dbf55f1a",
"metadata": {},
"source": [
"The routers currently included in the `optiwindnet.api` module are:\n",
"\n",
"* **EWRouter**\n",
"\n",
" * *Fastest option* — completes in a fraction of a second\n",
" * Great for quick network generation\n",
" * Produces only **branched-topology** solutions\n",
" * Heuristic method (EW = modified Esau-Williams) — solutions may be far from the optimum\n",
"\n",
"* **HGSRouter**\n",
"\n",
" * *Still fast* — can provide high-quality solutions in **0.5–2 seconds**\n",
" * Produces only **radial-topology** solutions (no branching)\n",
" * A maximum number of feeders can be enforced\n",
" * Meta-heuristic method (HGS = Hybrid Genetic Search)\n",
"\n",
"* **MILPRouter**\n",
"\n",
" * Delivers solutions with **quality guarantees** (a bound on the distance from the optimum)\n",
" * May take a **few to several minutes** depending on the problem size and quality required\n",
" * Full set of model options to choose from\n",
" * Should be used when optimality is more important than speed\n",
" * MILP = Mixed Integer Linear Programming"
]
},
{
"cell_type": "markdown",
"id": "961f9090",
"metadata": {},
"source": [
"## Run an example"
]
},
{
"cell_type": "markdown",
"id": "39bcea21",
"metadata": {},
"source": [
"In this section we will:\n",
"- Create a simple wind farm network\n",
"- Use heuristic and MILP optimization\n",
"- Explore key methods such as:\n",
" - `.optimize()`\n",
" - `.plot()`\n",
" - `.cost()`, `.length()`\n",
" - `.terse_links()`, `.update_from_terse_links()`\n",
" - `.gradient()`\n",
" - `.get_network()`\n",
" - `.add_buffer()`"
]
},
{
"cell_type": "markdown",
"id": "de0f0c52",
"metadata": {},
"source": [
"### Create a `WindFarmNetwork` instance"
]
},
{
"cell_type": "markdown",
"id": "c36112a8",
"metadata": {},
"source": [
"Import required modules"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "afeaaf92",
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"from optiwindnet.api import WindFarmNetwork, EWRouter, MILPRouter, ModelOptions"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "d8fb593b",
"metadata": {},
"outputs": [],
"source": [
"# Display figures as SVG in Jupyter notebooks\n",
"%config InlineBackend.figure_formats = ['svg']"
]
},
{
"cell_type": "markdown",
"id": "d5373461",
"metadata": {},
"source": [
"#### Load location data"
]
},
{
"cell_type": "markdown",
"id": "4ff13e41",
"metadata": {},
"source": [
"OptiWindNet operates on location geometries, which define turbine and substation positions (plus optional borders/obstacles).\n",
"\n",
"You can load a location from:\n",
"- `.yaml` or `.osm.pbf` files;\n",
"- The included locations repository;\n",
"- Your own coordinate arrays.\n",
"\n",
"For more details see [Data Input](a01_data_input.ipynb).\n",
"\n",
"We start with a simple geometry (5 turbines and 1 substation), and define a simple set of cables."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "910dc391-6bf5-4fda-9a0b-a4faab58248a",
"metadata": {},
"outputs": [],
"source": [
"wfn = WindFarmNetwork(\n",
" cables=[(2, 1500.0), (5, 1800.0)],\n",
" turbinesC=np.array([[0, 0], [1, 1], [2, 0], [3, 1], [4, 0]]),\n",
" substationsC=np.array([[2, -2]]),\n",
" borderC=np.array([[0, -3], [0, 2], [4, 2.1], [5, 1], [4, -3]]),\n",
" obstacleC_=[np.array([[0.2, -2.5], [1.5, -2.5], [0.2, -1]])]\n",
")"
]
},
{
"cell_type": "markdown",
"id": "fcafb617",
"metadata": {},
"source": [
"##### Plot location\n",
"\n",
"> **Note:** Many of the Jupyter notebooks provided include SVG figures as output. To ensure these visuals are displayed correctly in JupyterLab or Jupyter Notebook, make sure the notebook is marked as **trusted**.\n",
"> *In JupyterLab, you can do this by pressing* `Ctrl + Shift + C` *and selecting* **Trust Notebook**."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "c2b946a4",
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"wfn"
]
},
{
"cell_type": "markdown",
"id": "9cd882d6",
"metadata": {},
"source": [
"### Method: `optimize(router=...)`"
]
},
{
"cell_type": "markdown",
"id": "03cf721a",
"metadata": {},
"source": [
"This is the main method that optimizes the electrical network using the given router (heuristic, metaheuristic or MILP).\n",
" > Note that the router could be passed directly to `WindFarmNetwork(router=...)`."
]
},
{
"cell_type": "markdown",
"id": "2865ca33-b8d1-42c9-bb49-5d088e866a9d",
"metadata": {},
"source": [
"#### Use a heuristic router (Esau-Williams)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "43d027d8-1b4c-4b78-96ba-2207a6a6056a",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([ 1, 2, -1, 2, 3])"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"wfn.optimize(router=EWRouter())"
]
},
{
"cell_type": "markdown",
"id": "b2bb6e6d",
"metadata": {},
"source": [
"Plot the result"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "1388e2d8",
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"wfn"
]
},
{
"cell_type": "markdown",
"id": "775d7118",
"metadata": {},
"source": [
"#### Use a MILP router with full control over topology and feeders\n",
"\n",
"The solution from the previous call to `wfn.optimize()` is stored within the `wfn` object.\n",
"If this solution is *feasible under the current `MILPRouter` settings*, it will be *automatically used as a warm start*.\n",
"Otherwise, if it does not meet the current `ModelOptions` constraints, it will be *ignored*, and the MILP solver will start from scratch.\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "a5621cbd",
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"IntegerBoundsPreprocessor 85 rows, 46 columns, 267 entries with magnitude in [1.000000e+00, 5.000000e+00]\n",
"BoundPropagationPreprocessor 85 rows, 46 columns, 267 entries with magnitude in [1.000000e+00, 5.000000e+00]\n",
"ImpliedIntegerPreprocessor 85 rows, 46 columns, 267 entries with magnitude in [1.000000e+00, 5.000000e+00]\n",
"IntegerBoundsPreprocessor 85 rows, 46 columns, 267 entries with magnitude in [1.000000e+00, 5.000000e+00]\n",
"ReduceCostOverExclusiveOrConstraintPreprocessor 85 rows, 46 columns, 267 entries with magnitude in [1.000000e+00, 5.000000e+00]\n",
"\n",
"Scaling to pure integer problem.\n",
"Num integers: 46/46 (implied: 0 in_inequalities: 0 max_scaling: 0) [IP] \n",
"Maximum constraint coefficient relative error: 0\n",
"Maximum constraint worst-case activity error: 0\n",
"Constraint scaling factor range: [1, 1]\n",
"\n",
"Starting CP-SAT solver v9.15.6755\n",
"Parameters: max_time_in_seconds: 2 log_search_progress: true catch_sigint_signal: false relative_gap_limit: 0.01\n",
"Setting number of workers to 16\n",
"\n",
"Initial optimization model 'optiwindnet': (model_fingerprint: 0x625dc01df5e87447)\n",
"#Variables: 46 (#bools: 18 in floating point objective) (35 primary variables)\n",
" - 23 Booleans in [0,1]\n",
" - 18 in [0,4]\n",
" - 5 in [0,5]\n",
"#kLinear2: 55\n",
"#kLinear3: 8\n",
"#kLinearN: 22 (#terms: 133)\n",
"\n",
"Starting presolve at 0.00s\n",
"The solution hint is complete, but it is infeasible! we will try to repair it.\n",
"[Scaling] Floating point objective has 18 terms with magnitude in [0.251936, 2.33385] average = 1.20522\n",
"[Scaling] Objective coefficient relative error: 5.27705e-05\n",
"[Scaling] Objective worst-case absolute error: 9.19728e-05\n",
"[Scaling] Objective scaling factor: 32768\n",
" 2.25e-05s 0.00e+00d [DetectDominanceRelations] \n",
" 3.18e-04s 0.00e+00d [PresolveToFixPoint] #num_loops=2 #num_dual_strengthening=1 \n",
" 5.25e-06s 0.00e+00d [ExtractEncodingFromLinear] #potential_supersets=18 \n",
" 8.41e-06s 0.00e+00d [DetectDuplicateColumns] \n",
" 1.24e-05s 0.00e+00d [DetectDuplicateConstraints] \n",
"[Symmetry] Graph for symmetry has 186 nodes and 331 arcs.\n",
"[Symmetry] Symmetry computation done. time: 4.2877e-05 dtime: 5.163e-05\n",
"[SAT presolve] num removable Booleans: 0 / 23\n",
"[SAT presolve] num trivial clauses: 0\n",
"[SAT presolve] [0s] clauses:9 literals:18 vars:18 one_side_vars:18 simple_definition:0 singleton_clauses:0\n",
"[SAT presolve] [4.88e-06s] clauses:9 literals:18 vars:18 one_side_vars:18 simple_definition:0 singleton_clauses:0\n",
"[SAT presolve] [7.559e-06s] clauses:9 literals:18 vars:18 one_side_vars:18 simple_definition:0 singleton_clauses:0\n",
" 2.66e-05s 0.00e+00d [DetectDuplicateConstraintsWithDifferentEnforcements] \n",
" 6.29e-04s 1.48e-04d [Probe] #probed=86 #fixed_bools=2 #new_bounds=9 #new_binary_clauses=27 \n",
" 3.27e-05s 1.54e-05d [MaxClique] Merged 21 constraints with 60 literals into 15 constraints with 48 literals\n",
" 2.47e-05s 0.00e+00d [DetectDominanceRelations] \n",
" 2.91e-04s 0.00e+00d [PresolveToFixPoint] #num_loops=2 #num_dual_strengthening=1 \n",
" 5.47e-05s 0.00e+00d [ProcessAtMostOneAndLinear] #num_changes=14 \n",
" 2.03e-05s 0.00e+00d [DetectDuplicateConstraints] \n",
" 1.33e-05s 0.00e+00d [DetectDuplicateConstraintsWithDifferentEnforcements] \n",
" 2.00e-05s 3.23e-07d [DetectDominatedLinearConstraints] #relevant_constraints=11 #num_inclusions=5 \n",
" 3.02e-06s 0.00e+00d [DetectDifferentVariables] \n",
" 3.35e-05s 7.62e-07d [ProcessSetPPC] #relevant_constraints=23 #num_inclusions=18 \n",
" 2.88e-05s 0.00e+00d [TransformClausesToExactlyOne] #num_amos=27 \n",
" 2.96e-05s 0.00e+00d [DetectEncodedComplexDomains] \n",
" 4.58e-06s 0.00e+00d [FindAlmostIdenticalLinearConstraints] \n",
" 2.63e-05s 3.93e-06d [FindBigAtMostOneAndLinearOverlap] \n",
" 6.48e-06s 3.13e-06d [FindBigVerticalLinearOverlap] \n",
" 3.08e-06s 1.95e-07d [FindBigHorizontalLinearOverlap] #linears=5 \n",
" 1.76e-06s 0.00e+00d [MergeClauses] \n",
" 2.12e-05s 0.00e+00d [DetectDominanceRelations] \n",
" 2.16e-04s 0.00e+00d [PresolveToFixPoint] #num_loops=2 #num_dual_strengthening=1 \n",
" 1.67e-05s 0.00e+00d [DetectDominanceRelations] \n",
" 1.10e-04s 0.00e+00d [PresolveToFixPoint] #num_loops=1 #num_dual_strengthening=1 \n",
" 5.61e-06s 0.00e+00d [DetectDuplicateColumns] \n",
" 1.39e-05s 0.00e+00d [DetectDuplicateConstraints] #duplicates=1 \n",
"[Symmetry] Graph for symmetry has 156 nodes and 236 arcs.\n",
"[Symmetry] Symmetry computation done. time: 6.3732e-05 dtime: 6.161e-05\n",
"[Symmetry] #generators: 1, average support size: 38\n",
"[Symmetry] 19 orbits on 38 variables with sizes: 2,2,2,2,2,2,2,2,2,2,...\n",
"[Symmetry] Num fixable by intersecting at_most_one with orbits: 2 largest_orbit: 2\n",
"[SAT presolve] num removable Booleans: 0 / 21\n",
"[SAT presolve] num trivial clauses: 0\n",
"[SAT presolve] [0s] clauses:4 literals:8 vars:8 one_side_vars:8 simple_definition:0 singleton_clauses:0\n",
"[SAT presolve] [3.952e-06s] clauses:4 literals:8 vars:8 one_side_vars:8 simple_definition:0 singleton_clauses:0\n",
"[SAT presolve] [6.582e-06s] clauses:4 literals:8 vars:8 one_side_vars:8 simple_definition:0 singleton_clauses:0\n",
" 2.52e-05s 0.00e+00d [DetectDuplicateConstraintsWithDifferentEnforcements] \n",
" 2.93e-04s 3.72e-05d [Probe] #probed=51 #fixed_bools=1 #new_bounds=6 #new_binary_clauses=13 \n",
" 1.95e-05s 6.31e-06d [MaxClique] \n",
" 1.99e-05s 0.00e+00d [DetectDominanceRelations] \n",
" 1.54e-04s 0.00e+00d [PresolveToFixPoint] #num_loops=2 #num_dual_strengthening=1 \n",
" 1.79e-05s 0.00e+00d [ProcessAtMostOneAndLinear] \n",
" 1.41e-05s 0.00e+00d [DetectDuplicateConstraints] \n",
" 1.04e-05s 0.00e+00d [DetectDuplicateConstraintsWithDifferentEnforcements] \n",
" 1.65e-05s 2.17e-07d [DetectDominatedLinearConstraints] #relevant_constraints=10 #num_inclusions=5 \n",
" 2.20e-06s 0.00e+00d [DetectDifferentVariables] \n",
" 1.11e-05s 1.85e-07d [ProcessSetPPC] #relevant_constraints=18 \n",
" 1.66e-05s 0.00e+00d [TransformClausesToExactlyOne] #num_amos=12 \n",
" 1.63e-05s 0.00e+00d [DetectEncodedComplexDomains] \n",
" 2.79e-06s 0.00e+00d [FindAlmostIdenticalLinearConstraints] \n",
" 8.55e-06s 2.98e-06d [FindBigAtMostOneAndLinearOverlap] \n",
" 4.94e-06s 2.17e-06d [FindBigVerticalLinearOverlap] \n",
" 2.35e-06s 1.65e-07d [FindBigHorizontalLinearOverlap] #linears=5 \n",
" 9.61e-07s 0.00e+00d [MergeClauses] \n",
" 1.98e-05s 0.00e+00d [DetectDominanceRelations] \n",
" 1.11e-04s 0.00e+00d [PresolveToFixPoint] #num_loops=1 #num_dual_strengthening=1 \n",
" 1.51e-05s 0.00e+00d [DetectDominanceRelations] \n",
" 9.47e-05s 0.00e+00d [PresolveToFixPoint] #num_loops=1 #num_dual_strengthening=1 \n",
" 4.66e-06s 0.00e+00d [DetectDuplicateColumns] \n",
" 1.14e-05s 0.00e+00d [DetectDuplicateConstraints] \n",
"[Symmetry] Graph for symmetry has 137 nodes and 189 arcs.\n",
"[Symmetry] Symmetry computation done. time: 2.4193e-05 dtime: 2.144e-05\n",
"[SAT presolve] num removable Booleans: 0 / 18\n",
"[SAT presolve] num trivial clauses: 0\n",
"[SAT presolve] [0s] clauses:6 literals:12 vars:9 one_side_vars:9 simple_definition:0 singleton_clauses:0\n",
"[SAT presolve] [3.668e-06s] clauses:6 literals:12 vars:9 one_side_vars:9 simple_definition:0 singleton_clauses:0\n",
"[SAT presolve] [2.5755e-05s] clauses:6 literals:12 vars:9 one_side_vars:9 simple_definition:0 singleton_clauses:0\n",
" 1.63e-05s 0.00e+00d [DetectDuplicateConstraintsWithDifferentEnforcements] \n",
" 5.20e-04s 3.27e-05d [Probe] #probed=50 #new_binary_clauses=14 \n",
" 2.07e-05s 7.83e-06d [MaxClique] Merged 12 constraints with 32 literals into 11 constraints with 30 literals\n",
" 1.85e-05s 0.00e+00d [DetectDominanceRelations] \n",
" 1.13e-04s 0.00e+00d [PresolveToFixPoint] #num_loops=1 #num_dual_strengthening=1 \n",
" 1.84e-05s 0.00e+00d [ProcessAtMostOneAndLinear] \n",
" 1.19e-05s 0.00e+00d [DetectDuplicateConstraints] \n",
" 9.43e-06s 0.00e+00d [DetectDuplicateConstraintsWithDifferentEnforcements] \n",
" 1.50e-05s 2.17e-07d [DetectDominatedLinearConstraints] #relevant_constraints=10 #num_inclusions=5 \n",
" 2.23e-06s 0.00e+00d [DetectDifferentVariables] \n",
" 1.03e-05s 1.76e-07d [ProcessSetPPC] #relevant_constraints=17 \n",
" 1.53e-05s 0.00e+00d [TransformClausesToExactlyOne] #num_amos=11 \n",
" 1.58e-05s 0.00e+00d [DetectEncodedComplexDomains] \n",
" 2.64e-06s 0.00e+00d [FindAlmostIdenticalLinearConstraints] \n",
" 8.65e-06s 3.00e-06d [FindBigAtMostOneAndLinearOverlap] \n",
" 3.95e-06s 2.16e-06d [FindBigVerticalLinearOverlap] \n",
" 1.99e-06s 1.65e-07d [FindBigHorizontalLinearOverlap] #linears=5 \n",
" 8.15e-07s 0.00e+00d [MergeClauses] \n",
" 1.52e-05s 0.00e+00d [DetectDominanceRelations] \n",
" 9.35e-05s 0.00e+00d [PresolveToFixPoint] #num_loops=1 #num_dual_strengthening=1 \n",
" 8.38e-07s 0.00e+00d [MergeNoOverlap] \n",
" 5.27e-07s 0.00e+00d [MergeNoOverlap2D] \n",
" 1.39e-05s 0.00e+00d [ExpandObjective] #entries=54 #tight_variables=21 #tight_constraints=6 \n",
"\n",
"Presolve summary:\n",
" - 12 affine relations were detected.\n",
" - rule 'TODO linear inclusion: superset is equality' was applied 15 times.\n",
" - rule 'TODO linear2: convert ax + by != cte to clauses for large domains' was applied 120 times.\n",
" - rule 'affine: new relation' was applied 12 times.\n",
" - rule 'at_most_one: removed literals' was applied 10 times.\n",
" - rule 'at_most_one: transformed into max clique' was applied 2 times.\n",
" - rule 'bool_and: x => x' was applied 14 times.\n",
" - rule 'bool_or: implications' was applied 16 times.\n",
" - rule 'deductions: 52 stored' was applied 1 time.\n",
" - rule 'duplicate: removed constraint' was applied 1 time.\n",
" - rule 'enforcement: false literal' was applied 3 times.\n",
" - rule 'enforcement: true literal' was applied 5 times.\n",
" - rule 'exactly_one: removed literals' was applied 5 times.\n",
" - rule 'exactly_one: simplified objective' was applied 2 times.\n",
" - rule 'linear + amo: extracted enforcement literal' was applied 14 times.\n",
" - rule 'linear1: transformed to implication' was applied 4 times.\n",
" - rule 'linear2: contains a boolean' was applied 23 times.\n",
" - rule 'linear2: convert ax + by != cte to clauses' was applied 16 times.\n",
" - rule 'linear: divide by GCD' was applied 4 times.\n",
" - rule 'linear: empty' was applied 11 times.\n",
" - rule 'linear: enforcement literal in expression' was applied 4 times.\n",
" - rule 'linear: fixed or dup variables' was applied 22 times.\n",
" - rule 'linear: positive at most one' was applied 12 times.\n",
" - rule 'linear: positive equal one' was applied 7 times.\n",
" - rule 'linear: remapped using affine relations' was applied 19 times.\n",
" - rule 'linear: simplified rhs' was applied 5 times.\n",
" - rule 'new_bool: integer encoding' was applied 7 times.\n",
" - rule 'objective: shifted cost with exactly ones' was applied 4 times.\n",
" - rule 'presolve: 10 unused variables removed.' was applied 1 time.\n",
" - rule 'presolve: iteration' was applied 3 times.\n",
" - rule 'setppc: exactly_one included in linear' was applied 5 times.\n",
" - rule 'setppc: reduced linear coefficients' was applied 4 times.\n",
" - rule 'setppc: removed dominated constraints' was applied 2 times.\n",
" - rule 'setppc: removed trivial linear constraint' was applied 1 time.\n",
" - rule 'symmetry: fixed to false in general orbit' was applied 2 times.\n",
" - rule 'variables with 2 values: new affine relation' was applied 3 times.\n",
" - rule 'variables with 2 values: register other encoding' was applied 3 times.\n",
" - rule 'variables: add encoding constraint' was applied 4 times.\n",
" - rule 'variables: both boolean and its negation fix the same variable' was applied 2 times.\n",
" - rule 'variables: detect fully reified value encoding' was applied 14 times.\n",
" - rule 'variables: detect half reified value encoding' was applied 39 times.\n",
"\n",
"Presolved optimization model 'optiwindnet': (model_fingerprint: 0x3bd35ec8d182bcae)\n",
"#Variables: 31 (#bools: 9 in objective) (21 primary variables)\n",
" - 18 Booleans in [0,1]\n",
" - 2 in [0][2,4]\n",
" - 1 in [0,3]\n",
" - 10 in [0,4]\n",
"#kAtMostOne: 5 (#literals: 18)\n",
"#kBoolAnd: 5 (#enforced: 5) (#literals: 11)\n",
"#kExactlyOne: 6 (#literals: 21)\n",
"#kLinear1: 26 (#enforced: 26)\n",
"#kLinear2: 2\n",
"#kLinear3: 1\n",
"#kLinearN: 7 (#terms: 41)\n",
"[Symmetry] Graph for symmetry has 114 nodes and 187 arcs.\n",
"[Symmetry] Symmetry computation done. time: 2.1219e-05 dtime: 2.148e-05\n",
"\n",
"Preloading model.\n",
"#Bound 0.00s best:inf next:[7.65682461,16.6584115] initial_domain\n",
"The solution hint is complete, but it is infeasible! we will try to repair it.\n",
"#Model 0.00s var:31/31 constraints:52/52\n",
"\n",
"Starting search at 0.00s with 16 workers.\n",
"11 full problem subsolvers: [core, default_lp, lb_tree_search, max_lp, no_lp, objective_lb_search, probing, pseudo_costs, quick_restart, quick_restart_no_lp, reduced_costs]\n",
"5 first solution subsolvers: [fj(2), fs_random, fs_random_no_lp, fs_random_quick_restart_no_lp]\n",
"11 interleaved subsolvers: [feasibility_pump, graph_arc_lns, graph_cst_lns, graph_dec_lns, graph_var_lns, lb_relax_lns, ls, ls_lin, rins/rens, rnd_cst_lns, rnd_var_lns]\n",
"3 helper subsolvers: [neighborhood_helper, synchronization_agent, update_gap_integral]\n",
"\n",
"#1 0.01s best:8.82839443 next:[7.65682461,8.82836392] core (fixed_bools=0/18)\n",
"#2 0.01s best:8.48525478 next:[7.65682461,8.48522427] no_lp [hint] (fixed_bools=3/18)\n",
"#Done 0.01s no_lp\n",
"#Done 0.01s no_lp\n",
"#Done 0.01s core\n",
"\n",
"Task timing n [ min, max] avg dev time n [ min, max] avg dev dtime\n",
" 'core': 1 [ 4.08ms, 4.08ms] 4.08ms 0.00ns 4.08ms 2 [ 37.12us, 78.36us] 57.74us 20.62us 115.48us\n",
" 'default_lp': 1 [ 4.00ms, 4.00ms] 4.00ms 0.00ns 4.00ms 1 [ 39.19us, 39.19us] 39.19us 0.00ns 39.19us\n",
" 'feasibility_pump': 1 [ 1.22ms, 1.22ms] 1.22ms 0.00ns 1.22ms 0 [ 0.00ns, 0.00ns] 0.00ns 0.00ns 0.00ns\n",
" 'fj': 0 [ 0.00ns, 0.00ns] 0.00ns 0.00ns 0.00ns 0 [ 0.00ns, 0.00ns] 0.00ns 0.00ns 0.00ns\n",
" 'fj': 1 [ 1.20ms, 1.20ms] 1.20ms 0.00ns 1.20ms 0 [ 0.00ns, 0.00ns] 0.00ns 0.00ns 0.00ns\n",
" 'fs_random': 1 [ 2.61ms, 2.61ms] 2.61ms 0.00ns 2.61ms 0 [ 0.00ns, 0.00ns] 0.00ns 0.00ns 0.00ns\n",
" 'fs_random_no_lp': 1 [ 2.76ms, 2.76ms] 2.76ms 0.00ns 2.76ms 0 [ 0.00ns, 0.00ns] 0.00ns 0.00ns 0.00ns\n",
" 'fs_random_quick_restart_no_lp': 1 [ 1.34ms, 1.34ms] 1.34ms 0.00ns 1.34ms 0 [ 0.00ns, 0.00ns] 0.00ns 0.00ns 0.00ns\n",
" 'graph_arc_lns': 0 [ 0.00ns, 0.00ns] 0.00ns 0.00ns 0.00ns 0 [ 0.00ns, 0.00ns] 0.00ns 0.00ns 0.00ns\n",
" 'graph_cst_lns': 0 [ 0.00ns, 0.00ns] 0.00ns 0.00ns 0.00ns 0 [ 0.00ns, 0.00ns] 0.00ns 0.00ns 0.00ns\n",
" 'graph_dec_lns': 0 [ 0.00ns, 0.00ns] 0.00ns 0.00ns 0.00ns 0 [ 0.00ns, 0.00ns] 0.00ns 0.00ns 0.00ns\n",
" 'graph_var_lns': 0 [ 0.00ns, 0.00ns] 0.00ns 0.00ns 0.00ns 0 [ 0.00ns, 0.00ns] 0.00ns 0.00ns 0.00ns\n",
" 'lb_relax_lns': 0 [ 0.00ns, 0.00ns] 0.00ns 0.00ns 0.00ns 0 [ 0.00ns, 0.00ns] 0.00ns 0.00ns 0.00ns\n",
" 'lb_tree_search': 1 [ 3.01ms, 3.01ms] 3.01ms 0.00ns 3.01ms 1 [114.07us, 114.07us] 114.07us 0.00ns 114.07us\n",
" 'ls': 0 [ 0.00ns, 0.00ns] 0.00ns 0.00ns 0.00ns 0 [ 0.00ns, 0.00ns] 0.00ns 0.00ns 0.00ns\n",
" 'ls_lin': 0 [ 0.00ns, 0.00ns] 0.00ns 0.00ns 0.00ns 0 [ 0.00ns, 0.00ns] 0.00ns 0.00ns 0.00ns\n",
" 'max_lp': 1 [ 3.61ms, 3.61ms] 3.61ms 0.00ns 3.61ms 1 [115.07us, 115.07us] 115.07us 0.00ns 115.07us\n",
" 'no_lp': 1 [ 2.66ms, 2.66ms] 2.66ms 0.00ns 2.66ms 1 [ 40.97us, 40.97us] 40.97us 0.00ns 40.97us\n",
" 'objective_lb_search': 1 [ 2.76ms, 2.76ms] 2.76ms 0.00ns 2.76ms 1 [ 37.42us, 37.42us] 37.42us 0.00ns 37.42us\n",
" 'probing': 1 [ 2.98ms, 2.98ms] 2.98ms 0.00ns 2.98ms 1 [ 36.95us, 36.95us] 36.95us 0.00ns 36.95us\n",
" 'pseudo_costs': 1 [ 2.95ms, 2.95ms] 2.95ms 0.00ns 2.95ms 1 [ 37.57us, 37.57us] 37.57us 0.00ns 37.57us\n",
" 'quick_restart': 1 [ 3.44ms, 3.44ms] 3.44ms 0.00ns 3.44ms 1 [ 37.42us, 37.42us] 37.42us 0.00ns 37.42us\n",
" 'quick_restart_no_lp': 1 [ 1.92ms, 1.92ms] 1.92ms 0.00ns 1.92ms 1 [ 37.13us, 37.13us] 37.13us 0.00ns 37.13us\n",
" 'reduced_costs': 1 [ 3.24ms, 3.24ms] 3.24ms 0.00ns 3.24ms 1 [ 37.57us, 37.57us] 37.57us 0.00ns 37.57us\n",
" 'rins/rens': 0 [ 0.00ns, 0.00ns] 0.00ns 0.00ns 0.00ns 0 [ 0.00ns, 0.00ns] 0.00ns 0.00ns 0.00ns\n",
" 'rnd_cst_lns': 0 [ 0.00ns, 0.00ns] 0.00ns 0.00ns 0.00ns 0 [ 0.00ns, 0.00ns] 0.00ns 0.00ns 0.00ns\n",
" 'rnd_var_lns': 0 [ 0.00ns, 0.00ns] 0.00ns 0.00ns 0.00ns 0 [ 0.00ns, 0.00ns] 0.00ns 0.00ns 0.00ns\n",
"\n",
"Search stats Bools Conflicts Branches Restarts BacktrackToRoot Backtrack BoolPropag IntegerPropag\n",
" 'core': 18 1 106 0 66 91 469 904\n",
" 'default_lp': 18 0 36 0 36 36 149 288\n",
" 'fs_random': 18 0 0 0 0 0 0 0\n",
" 'fs_random_no_lp': 18 0 0 0 0 0 0 0\n",
" 'fs_random_quick_restart_no_lp': 18 0 0 0 0 0 0 0\n",
" 'lb_tree_search': 18 0 36 0 36 36 149 350\n",
" 'max_lp': 18 0 36 0 36 36 149 350\n",
" 'no_lp': 18 0 39 0 37 37 179 357\n",
" 'objective_lb_search': 18 0 36 0 36 36 149 288\n",
" 'probing': 18 0 36 0 36 36 149 287\n",
" 'pseudo_costs': 18 0 36 0 36 36 149 349\n",
" 'quick_restart': 18 0 36 0 36 36 149 288\n",
" 'quick_restart_no_lp': 18 0 36 0 36 36 149 288\n",
" 'reduced_costs': 18 0 36 0 36 36 149 349\n",
"\n",
"SAT formula Fixed Equiv Total VarLeft BinaryClauses PermanentClauses TemporaryClauses\n",
" 'core': 18 0 18 0 66 8 0\n",
" 'default_lp': 0 0 18 18 48 6 0\n",
" 'fs_random': 0 0 18 18 0 0 0\n",
" 'fs_random_no_lp': 0 0 18 18 0 0 0\n",
" 'fs_random_quick_restart_no_lp': 0 0 18 18 0 0 0\n",
" 'lb_tree_search': 0 0 18 18 48 6 0\n",
" 'max_lp': 0 0 18 18 48 6 0\n",
" 'no_lp': 18 0 18 0 60 6 0\n",
" 'objective_lb_search': 0 0 18 18 48 6 0\n",
" 'probing': 0 0 18 18 32 6 0\n",
" 'pseudo_costs': 0 0 18 18 48 6 0\n",
" 'quick_restart': 0 0 18 18 48 6 0\n",
" 'quick_restart_no_lp': 0 0 18 18 48 6 0\n",
" 'reduced_costs': 0 0 18 18 48 6 0\n",
"\n",
"SAT stats ClassicMinim LitRemoved LitRemovedBinary LitLearned LitForgotten Subsumed\n",
" 'core': 0 0 1 3 0 0\n",
" 'default_lp': 0 0 0 0 0 0\n",
" 'fs_random': 0 0 0 0 0 0\n",
" 'fs_random_no_lp': 0 0 0 0 0 0\n",
" 'fs_random_quick_restart_no_lp': 0 0 0 0 0 0\n",
" 'lb_tree_search': 0 0 0 0 0 0\n",
" 'max_lp': 0 0 0 0 0 0\n",
" 'no_lp': 0 0 0 0 0 0\n",
" 'objective_lb_search': 0 0 0 0 0 0\n",
" 'probing': 0 0 0 0 0 0\n",
" 'pseudo_costs': 0 0 0 0 0 0\n",
" 'quick_restart': 0 0 0 0 0 0\n",
" 'quick_restart_no_lp': 0 0 0 0 0 0\n",
" 'reduced_costs': 0 0 0 0 0 0\n",
"\n",
"Vivification Clauses Decisions LitTrue Subsumed LitRemoved DecisionReused Conflicts\n",
" 'core': 13 26 0 0 0 0 0\n",
" 'default_lp': 0 0 0 0 0 0 0\n",
" 'fs_random': 0 0 0 0 0 0 0\n",
" 'fs_random_no_lp': 0 0 0 0 0 0 0\n",
" 'fs_random_quick_restart_no_lp': 0 0 0 0 0 0 0\n",
" 'lb_tree_search': 0 0 0 0 0 0 0\n",
" 'max_lp': 0 0 0 0 0 0 0\n",
" 'no_lp': 0 0 0 0 0 0 0\n",
" 'objective_lb_search': 0 0 0 0 0 0 0\n",
" 'probing': 0 0 0 0 0 0 0\n",
" 'pseudo_costs': 0 0 0 0 0 0 0\n",
" 'quick_restart': 0 0 0 0 0 0 0\n",
" 'quick_restart_no_lp': 0 0 0 0 0 0 0\n",
" 'reduced_costs': 0 0 0 0 0 0 0\n",
"\n",
"Clause deletion at_true l_and_not(l) to_binary sub_conflict sub_extra sub_decisions sub_eager sub_vivify sub_probing sub_inpro blocked eliminated forgotten promoted conflicts\n",
" 'core': 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1\n",
" 'default_lp': 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n",
" 'fs_random': 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n",
" 'fs_random_no_lp': 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n",
" 'fs_random_quick_restart_no_lp': 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n",
" 'lb_tree_search': 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n",
" 'max_lp': 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n",
" 'no_lp': 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n",
" 'objective_lb_search': 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n",
" 'probing': 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n",
" 'pseudo_costs': 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n",
" 'quick_restart': 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n",
" 'quick_restart_no_lp': 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n",
" 'reduced_costs': 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n",
"\n",
"Lp stats Component Iterations AddedCuts OPTIMAL DUAL_F. DUAL_U.\n",
" 'default_lp': 1 4 0 2 0 0\n",
" 'lb_tree_search': 1 19 0 1 0 0\n",
" 'max_lp': 1 19 0 1 1 0\n",
" 'objective_lb_search': 1 0 0 1 0 0\n",
" 'probing': 1 0 0 1 0 0\n",
" 'pseudo_costs': 1 0 0 1 0 0\n",
" 'quick_restart': 1 0 0 1 0 0\n",
" 'reduced_costs': 1 0 0 1 0 0\n",
"\n",
"Lp dimension Final dimension of first component\n",
" 'default_lp': 5 rows, 25 columns, 33 entries\n",
" 'lb_tree_search': 66 rows, 31 columns, 186 entries\n",
" 'max_lp': 66 rows, 31 columns, 186 entries\n",
" 'objective_lb_search': 5 rows, 25 columns, 33 entries\n",
" 'probing': 5 rows, 25 columns, 33 entries\n",
" 'pseudo_costs': 9 rows, 31 columns, 48 entries\n",
" 'quick_restart': 5 rows, 25 columns, 33 entries\n",
" 'reduced_costs': 9 rows, 31 columns, 48 entries\n",
"\n",
"Lp debug CutPropag CutEqPropag Adjust Overflow Bad BadScaling\n",
" 'default_lp': 0 0 0 0 0 0\n",
" 'lb_tree_search': 0 0 1 0 0 0\n",
" 'max_lp': 0 0 2 0 0 0\n",
" 'objective_lb_search': 0 0 0 0 0 0\n",
" 'probing': 0 0 0 0 0 0\n",
" 'pseudo_costs': 0 0 0 0 0 0\n",
" 'quick_restart': 0 0 0 0 0 0\n",
" 'reduced_costs': 0 0 0 0 0 0\n",
"\n",
"Lp pool Constraints Updates Simplif Merged Shortened Split Strengthened Cuts/Call\n",
" 'default_lp': 42 0 0 0 0 0 0 0/0\n",
" 'lb_tree_search': 66 0 0 0 0 0 0 0/0\n",
" 'max_lp': 66 0 0 0 0 0 0 0/0\n",
" 'objective_lb_search': 42 0 0 0 0 0 0 0/0\n",
" 'probing': 42 0 0 0 0 0 0 0/0\n",
" 'pseudo_costs': 66 0 0 0 0 0 0 0/0\n",
" 'quick_restart': 42 0 0 0 0 0 0 0/0\n",
" 'reduced_costs': 66 0 0 0 0 0 0 0/0\n",
"\n",
"LNS stats Improv/Calls Closed Difficulty TimeLimit\n",
" 'graph_arc_lns': 0/0 0% 5.00e-01 0.10\n",
" 'graph_cst_lns': 0/0 0% 5.00e-01 0.10\n",
" 'graph_dec_lns': 0/0 0% 5.00e-01 0.10\n",
" 'graph_var_lns': 0/0 0% 5.00e-01 0.10\n",
" 'lb_relax_lns': 0/0 0% 5.00e-01 0.50\n",
" 'rins/rens': 0/0 0% 5.00e-01 0.10\n",
" 'rnd_cst_lns': 0/0 0% 5.00e-01 0.10\n",
" 'rnd_var_lns': 0/0 0% 5.00e-01 0.10\n",
"\n",
"Solutions (2) Num Rank\n",
" 'core': 2 [0,1]\n",
" 'no_lp': 2 [1,2]\n",
"\n",
"Objective bounds Num\n",
" 'initial_domain': 1\n",
"\n",
"Solution repositories Added Queried Synchro\n",
" 'alternative_path': 1 0 1\n",
" 'best_solutions': 3 0 3\n",
" 'fj solution hints': 0 0 0\n",
" 'lp solutions': 0 0 0\n",
" 'pump': 0 0\n",
"\n",
"Improving bounds shared Num Sym\n",
" 'core': 31 0\n",
"\n",
"Clauses shared #Exported #Imported #BinaryRead #BinaryTotal\n",
" 'core': 6 0 6 6\n",
" 'default_lp': 0 0 0 6\n",
" 'fs_random': 0 0 0 6\n",
" 'fs_random_no_lp': 0 0 0 6\n",
" 'fs_random_quick_restart_no_lp': 0 0 0 6\n",
" 'lb_tree_search': 0 0 0 6\n",
" 'max_lp': 0 0 0 6\n",
" 'no_lp': 0 0 6 6\n",
" 'objective_lb_search': 0 0 0 6\n",
" 'probing': 0 0 0 6\n",
" 'pseudo_costs': 0 0 0 6\n",
" 'quick_restart': 0 0 0 6\n",
" 'quick_restart_no_lp': 0 0 0 6\n",
" 'reduced_costs': 0 0 0 6\n",
"\n",
"LRAT_status: NA\n",
"[Scaling] scaled_objective_bound: 8.48525 corrected_bound: 8.48526 delta: -8.1213e-07\n",
"CpSolverResponse summary:\n",
"status: OPTIMAL\n",
"objective: 8.485281374238571\n",
"best_bound: 8.48525559680267\n",
"integers: 26\n",
"booleans: 18\n",
"conflicts: 0\n",
"branches: 39\n",
"propagations: 179\n",
"integer_propagations: 357\n",
"restarts: 0\n",
"lp_iterations: 0\n",
"walltime: 0.011203\n",
"usertime: 0.011203\n",
"deterministic_time: 0.000916404\n",
"gap_integral: 0.000265343\n",
"solution_fingerprint: 0x32d6c99bc4018a9b\n",
"\n"
]
},
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model_opts = ModelOptions(\n",
" topology='radial',\n",
" feeder_limit='minimum',\n",
" feeder_route='straight',\n",
")\n",
"wfn.optimize(\n",
" router=MILPRouter(\n",
" solver_name='ortools.cp_sat',\n",
" time_limit=2,\n",
" mip_gap=0.01,\n",
" model_options=model_opts\n",
" ),\n",
" verbose=True,\n",
")\n",
"wfn"
]
},
{
"cell_type": "markdown",
"id": "2511d0d4-06d9-4baf-8de9-7939373fa5e8",
"metadata": {},
"source": [
"### Method: `plot()` and Variants"
]
},
{
"cell_type": "markdown",
"id": "3dbd5d88",
"metadata": {},
"source": [
"These methods help you visualize different stages of the optimization:\n",
"\n",
"- `plot_location()`: plot turbine/substation coordinates and borders\n",
"- `plot()`: plot optimized electrical network\n",
"\n",
"> For more details, see the [Plotting](a04_Plotting.ipynb) tutorial."
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "89161332",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
""
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/svg+xml": [
"\n",
"\n",
"\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"image/svg+xml": [
"\n",
"\n",
"\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"wfn.plot_location()\n",
"wfn.plot() # wfn will do the same on notebooks (and if no G is available it will display L)"
]
},
{
"cell_type": "markdown",
"id": "b93855a6-3175-4c75-8358-0bc56e8e787b",
"metadata": {},
"source": [
"### Method: `cost()` and `length()`"
]
},
{
"cell_type": "markdown",
"id": "711e2851-3c45-4e25-b6d2-30db7f0d56da",
"metadata": {},
"source": [
"Returns the total cost and cable length of the optimized network."
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "72ef1d9a",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Network cost: 14424.97833620557\n",
"Network length: 8.485281374238571\n"
]
}
],
"source": [
"print(\"Network cost:\", wfn.cost())\n",
"print(\"Network length:\", wfn.length())"
]
},
{
"cell_type": "markdown",
"id": "405e5e24-76d9-481f-bf5b-3e2b84feeb46",
"metadata": {},
"source": [
"### Method: `terse_links()`"
]
},
{
"cell_type": "markdown",
"id": "5a5a48f5-c2b7-449b-8a10-0b2f62d19ca6",
"metadata": {},
"source": [
"A terse link is a compact way to describe how each turbine is connected in the electrical network. It’s just a list (or array) where:\n",
"\n",
"* Each position `i` represents turbine `i`\n",
"\n",
"* The value at position `i` is the node that turbine `i` connects to\n",
"(this could be another turbine or a substation)"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "deb7d87f",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Terse link array: [ 1 2 3 4 -1]\n"
]
}
],
"source": [
"terse = wfn.terse_links()\n",
"print(\"Terse link array:\", terse)"
]
},
{
"cell_type": "markdown",
"id": "831e287d-0cf1-44cb-9b0c-11433a5006ea",
"metadata": {},
"source": [
"### Method: `update_from_terse_links()`"
]
},
{
"cell_type": "markdown",
"id": "3e95d580-3031-43bd-b277-f020b96731b6",
"metadata": {},
"source": [
"`update_from_terse_links()` allows you to reconstruct the network from a known `terse_link`, optionally updating coordinates. This method assumes a **valid and feasible network**, so it’s your responsibility to ensure that:\n",
"* The connections form a proper **feeder tree**\n",
"* Every turbine is (indirectly or directly) connected to a **substation**\n",
"* Capacity constraints are not violated\n",
"\n",
"Suppose we have a wind farm with:\n",
"* **5 turbines** → nodes `0, 1, 2, 3, 4`\n",
"* **1 substation** → node `-1` (*Note: In OptiWindNet, substations are assigned negative indices*)\n",
"\n",
"We want the following connections:\n",
"* Turbine 0 → Substation (-1)\n",
"* Turbine 1 → Turbine 0\n",
"* Turbine 2 → Substation (-1)\n",
"* Turbine 3 → Turbine 2\n",
"* Turbine 4 → Turbine 3\n",
"\n",
"This gives us the `terse_links` array:\n",
"\n",
"```python\n",
"terse_links = [-1, 0, -1, 2, 3]"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "af4c603a",
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"new_terse_links = np.array([-1, 0, -1, 2, 3])\n",
"\n",
"# Apply the new configuration\n",
"wfn.update_from_terse_links(new_terse_links)\n",
"\n",
"# Visualize the updated network (cable routing)\n",
"wfn\n"
]
},
{
"cell_type": "markdown",
"id": "3f277b56",
"metadata": {},
"source": [
"### Method: `gradient()`"
]
},
{
"cell_type": "markdown",
"id": "96a70239",
"metadata": {},
"source": [
"This method computes the gradient of the cost or length with respect to turbine/substation positions.\n",
"Useful for hybrid optimization or sensitivity analysis.\n",
" > Note: default of `gradient_type` is `length`."
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "3fe297b4",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"--- gradient_type=length ---\n",
"\n",
"Gradient (w.r.t. turbines):\n",
" [[-1.41421356 0. ]\n",
" [ 0.70710678 0.70710678]\n",
" [-0.70710678 0.29289322]\n",
" [ 0. 1.41421356]\n",
" [ 0.70710678 -0.70710678]] \n",
"\n",
"Gradient (w.r.t. substations):\n",
" [[ 0.70710678 -1.70710678]]\n",
"\n",
"\n",
"--- gradient_type=cost ---\n",
"\n",
"Gradient (w.r.t. turbines):\n",
" [[-2121.32034356 0. ]\n",
" [ 1060.66017178 1060.66017178]\n",
" [-1060.66017178 739.33982822]\n",
" [ 0. 2121.32034356]\n",
" [ 1060.66017178 -1060.66017178]] \n",
"\n",
"Gradient (w.r.t. substations):\n",
" [[ 1060.66017178 -2860.66017178]]\n"
]
}
],
"source": [
"print('--- gradient_type=length ---\\n')\n",
"grad_turb, grad_subs = wfn.gradient()\n",
"print(\"Gradient (w.r.t. turbines):\\n\", grad_turb, \"\\n\")\n",
"print(\"Gradient (w.r.t. substations):\\n\", grad_subs)\n",
"print('\\n')\n",
"print('--- gradient_type=cost ---\\n')\n",
"grad_turb, grad_subs = wfn.gradient(gradient_type='cost')\n",
"print(\"Gradient (w.r.t. turbines):\\n\", grad_turb, \"\\n\")\n",
"print(\"Gradient (w.r.t. substations):\\n\", grad_subs)"
]
},
{
"cell_type": "markdown",
"id": "95831235",
"metadata": {},
"source": [
"### Method: `get_network()`"
]
},
{
"cell_type": "markdown",
"id": "4eea9d03",
"metadata": {},
"source": [
"This method returns the **final optimized network** as a **structured NumPy array**, where each row represents an edge in the network.\n",
"\n",
"Each edge includes detailed attributes such as:\n",
"\n",
"* **`src`**: index of the source node\n",
"* **`tgt`**: index of the target (destination node)\n",
"* **`length`**: physical cable length\n",
"* **`load`**: electrical load carried through the cable (number of turbines)\n",
"* **`cable`**: index of the cable type used (e.g., 0 for first type in cable list)\n",
"* **`cost`**: cost associated with the used cable (if cost is provided by the user)"
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "9a0b2118",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([(1, 0, 1.41421356, 1., 0), (0, -1, 2.82842712, 2., 0)],\n",
" dtype=[('src', '"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/svg+xml": [
"\n",
"\n",
"\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"wfn.plot_original_vs_buffered()"
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "77dab41a",
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"wfn.optimize()\n",
"wfn"
]
},
{
"cell_type": "markdown",
"id": "9276fb5d",
"metadata": {},
"source": [
"### In this section:\n",
"\n",
"we explored the most useful methods of the `WindFarmNetwork` class:\n",
"\n",
"| Method | Purpose |\n",
"|--------|---------|\n",
"| `optimize()` | Run optimization with a router |\n",
"| `plot()` | Visualize the network |\n",
"| `cost()`, `length()` | Get total cost and length |\n",
"| `terse_links()` | Get compact link encoding |\n",
"| `update_from_terse_links()` | Apply terse links manually |\n",
"| `gradient()` | Compute network's gradient |\n",
"| `get_network()` | Export the optimized network data |\n",
"| `add_buffer()`| Expand border - Shrink obstacles|\n",
"\n",
"For deeper insights into individual methods, refer to the dedicated notebooks provided.\n"
]
}
],
"metadata": {
"language_info": {
"name": "python"
}
},
"nbformat": 4,
"nbformat_minor": 5
}