Layout optimization

This example demonstrates some basics about running wind farm optimization tasks with foxes.

It requires the installation of two additional python packages:

  • foxes-opt: Adds optimization functionalities to foxes, based on the iwopy interface (both also by Fraunhofer IWES, see links for details),

  • pymoo: Contains a number of very nice genetic algorithm implementations. Within foxes we do that implicitely via the iwopy interface.

These dependencies can conveniently be installed by

pip install foxes[opt]

for standard users, or

pip install -e .[opt]

from the foxes root folder for developers.

Here are the required imports for this example:

In [1]:
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import foxes
import foxes.utils.geom2d as gm
import foxes.variables as FV
from iwopy.interfaces.pymoo import Optimizer_pymoo
from foxes_opt.problems.layout import FarmLayoutOptProblem
from foxes_opt.objectives import MaxFarmPower
from foxes_opt.constraints import FarmBoundaryConstraint, MinDistConstraint

Let’s initialize the foxes engine, which will be used for all calculations:

In [2]:
engine = foxes.Engine.new(
    "process",
    chunk_size_states=500,
    chunk_size_points=5000,
    verbosity=0,
)
engine.initialize()

In the following we are tackling the problem of optimizing a wind farm layout for a site near Bremen, Germany. The data of a (coarse) wind rose with 216 states is provided as static data file with name "wind_rose_bremen.csv":

state,wd,ws,weight
0,0.0,3.5,0.00158
1,0.0,6.0,0.00244
2,0.0,8.5,0.00319
3,0.0,12.5,0.0036700002
4,0.0,17.5,0.00042
...

First, let’s create the states object and have a look at the wind rose:

In [3]:
states = foxes.input.states.StatesTable(
    data_source="wind_rose_bremen.csv",
    output_vars=[FV.WS, FV.WD, FV.TI, FV.RHO],
    var2col={FV.WS: "ws", FV.WD: "wd", FV.WEIGHT: "weight"},
    fixed_vars={FV.RHO: 1.225, FV.TI: 0.05},
)

o = foxes.output.StatesRosePlotOutput(states, point=[0.0, 0.0, 100.0])
fig = o.get_figure(16, FV.AMB_WS, [0, 3.5, 6, 10, 15, 20], figsize=(6, 6))
plt.show()
../_images/notebooks_layout_opt_6_0.png

Next, we need to specify the area within which the turbines are allowed to move during optimization. We use the foxes.utils.geom2d sub-package for that purpose (imported as gm, see above) which allows us to add and subtract polygons, circles, etc.

In [4]:
boundary = (
    gm.ClosedPolygon(
        np.array([[0, 0], [0, 1200], [1000, 800], [900, -200]], dtype=np.float64)
    )
    + gm.ClosedPolygon(
        np.array([[500, 0], [500, 1500], [1000, 1500], [1000, 0]], dtype=np.float64)
    )
    - gm.Circle([-100.0, -100.0], 700)
)

fig, ax = plt.subplots()
boundary.add_to_figure(ax)
plt.show()
../_images/notebooks_layout_opt_8_0.png

Later on we wish to apply boundary constraints that make sure all turbines are placed within this area geometry. These conditions make use of the minimal distance calculation from each point in question to the boundary. We can check the results by plotting again, now using the fill_mode option:

In [5]:
fig, axs = plt.subplots(1, 2, figsize=(14, 8))
boundary.add_to_figure(axs[0], fill_mode="dist_inside")
boundary.add_to_figure(axs[1], fill_mode="dist_outside")
plt.show()
../_images/notebooks_layout_opt_10_0.png

We now setup the model book and a wind farm with 10 turbines in some initial layout, including the boundary:

In [6]:
farm = foxes.WindFarm(boundary=boundary)
foxes.input.farm_layout.add_row(
    farm=farm,
    xy_base=np.array([500.0, 500.0]),
    xy_step=np.array([50.0, 50.0]),
    n_turbines=10,
    turbine_models=["NREL5MW"],
)

ax = foxes.output.FarmLayoutOutput(farm).get_figure()
plt.show()
Turbine 0, T0: xy=(500.00, 500.00), NREL5MW
Turbine 1, T1: xy=(550.00, 550.00), NREL5MW
Turbine 2, T2: xy=(600.00, 600.00), NREL5MW
Turbine 3, T3: xy=(650.00, 650.00), NREL5MW
Turbine 4, T4: xy=(700.00, 700.00), NREL5MW
Turbine 5, T5: xy=(750.00, 750.00), NREL5MW
Turbine 6, T6: xy=(800.00, 800.00), NREL5MW
Turbine 7, T7: xy=(850.00, 850.00), NREL5MW
Turbine 8, T8: xy=(900.00, 900.00), NREL5MW
Turbine 9, T9: xy=(950.00, 950.00), NREL5MW
../_images/notebooks_layout_opt_12_1.png

Notice the appearing turbine model layout_opt. This is not part of the model book but will be defined shortly by the optimization problem. In the context of the turbine models it defines where in the model order the optimization variables application should be applied. In our case we are optimizing the (X, Y)-coordinates of the turbines, and they should be updated at the very beginning.

Let’s new define the algorithm and the layout optimization problem. The latter should include boundary constraints and a minimal distance of 2 rotor diameters between turbines. Our objective is the maximization of the total wind farm power:

In [7]:
algo = foxes.algorithms.Downwind(
    farm,
    states,
    rotor_model="centre",
    wake_models=["Bastankhah025_linear_k002"],
    partial_wakes=None,
    verbosity=0,
)

problem = FarmLayoutOptProblem("layout_opt", algo)
problem.add_objective(MaxFarmPower(problem))
problem.add_constraint(FarmBoundaryConstraint(problem))
problem.add_constraint(MinDistConstraint(problem, min_dist=2.0, min_dist_unit="D"))
problem.initialize()
Problem 'layout_opt' (FarmLayoutOptProblem): Initializing
---------------------------------------------------------
  n_vars_int  : 0
  n_vars_float: 20
---------------------------------------------------------
  n_objectives: 1
  n_obj_cmptns: 1
---------------------------------------------------------
  n_constraints: 2
  n_con_cmptns: 55
---------------------------------------------------------

Notice that the two added constraint models imply a total of 55 individual constraint component functions. The wake model choice Bastankhah025 corresponds to the Bastankhah2014 deficit model with parameter sbeta_factor=0.25. This choice switches off the near wake modelling, rendering the model a bit smoother. This is for demonstrational purposes only and not required for running this example.

Next, we setup the optimizer. In our case we use the genetic algorithm GA from pymoo via the iwopy interface, here in vectorized form (flag vectorize=True), with 100 generations (n_max_gen=100) with population size 50 (pop_size=50):

In [8]:
solver = Optimizer_pymoo(
    problem,
    problem_pars=dict(vectorize=True),
    algo_pars=dict(
        type="GA",
        pop_size=50,
        seed=42,
    ),
    setup_pars=dict(),
    term_pars=dict(
        type="default",
        n_max_gen=100,
        ftol=1e-6,
        xtol=1e-3,
    ),
)
solver.initialize()
solver.print_info()
Loading pymoo
pymoo successfully loaded
Initializing Optimizer_pymoo
Selecting sampling: float_random (FloatRandomSampling)
Selecting algorithm: GA (GA)
Selecting termination: default (DefaultSingleObjectiveTermination)

Problem:
--------
  vectorize: True

Algorithm:
----------
  type: GA
  pop_size: 50
  seed: 42

Termination:
------------
  n_max_gen: 100
  ftol: 1e-06
  xtol: 0.001

After all the setup we can now solve the problem:

In [9]:
results = solver.solve()
solver.finalize(results)

print()
print(results)
print(results.problem_results)
=================================================================================
n_gen  |  n_eval  |     cv_min    |     cv_avg    |     f_avg     |     f_min
=================================================================================
     1 |       50 |  4.257950E+02 |  1.094916E+03 |             - |             -
     2 |      100 |  2.589298E+02 |  7.141458E+02 |             - |             -
     3 |      150 |  2.108843E+02 |  5.097684E+02 |             - |             -
     4 |      200 |  2.011586E+02 |  3.869307E+02 |             - |             -
     5 |      250 |  1.250281E+02 |  3.238949E+02 |             - |             -
     6 |      300 |  1.250281E+02 |  2.668302E+02 |             - |             -
     7 |      350 |  1.250281E+02 |  2.500554E+02 |             - |             -
     8 |      400 |  1.011095E+02 |  2.198486E+02 |             - |             -
     9 |      450 |  8.571793E+01 |  1.998508E+02 |             - |             -
    10 |      500 |  8.571793E+01 |  1.775495E+02 |             - |             -
    11 |      550 |  2.695093E+01 |  1.482376E+02 |             - |             -
    12 |      600 |  2.695093E+01 |  1.369663E+02 |             - |             -
    13 |      650 |  2.695093E+01 |  1.255097E+02 |             - |             -
    14 |      700 |  2.461710E+01 |  1.135664E+02 |             - |             -
    15 |      750 |  2.461710E+01 |  1.098324E+02 |             - |             -
    16 |      800 |  2.461710E+01 |  9.266611E+01 |             - |             -
    17 |      850 |  2.446678E+01 |  8.484456E+01 |             - |             -
    18 |      900 |  2.440080E+01 |  7.151751E+01 |             - |             -
    19 |      950 |  2.440080E+01 |  5.917821E+01 |             - |             -
    20 |     1000 |  1.998273E+01 |  4.871628E+01 |             - |             -
    21 |     1050 |  1.019366E+01 |  4.071983E+01 |             - |             -
    22 |     1100 |  6.4405916400 |  3.193676E+01 |             - |             -
    23 |     1150 |  4.9794099827 |  2.349004E+01 |             - |             -
    24 |     1200 |  0.000000E+00 |  1.738912E+01 | -5.005446E-01 | -5.005446E-01
    25 |     1250 |  0.000000E+00 |  1.137290E+01 | -5.003716E-01 | -5.005446E-01
    26 |     1300 |  0.000000E+00 |  6.4573986190 | -5.001255E-01 | -5.007026E-01
    27 |     1350 |  0.000000E+00 |  2.2839046772 | -5.002029E-01 | -5.043050E-01
    28 |     1400 |  0.000000E+00 |  0.0401518036 | -5.002328E-01 | -5.046954E-01
    29 |     1450 |  0.000000E+00 |  0.000000E+00 | -5.010270E-01 | -5.047884E-01
    30 |     1500 |  0.000000E+00 |  0.000000E+00 | -5.018469E-01 | -5.047952E-01
    31 |     1550 |  0.000000E+00 |  0.000000E+00 | -5.033843E-01 | -5.048056E-01
    32 |     1600 |  0.000000E+00 |  0.000000E+00 | -5.045212E-01 | -5.052518E-01
    33 |     1650 |  0.000000E+00 |  0.000000E+00 | -5.047445E-01 | -5.054240E-01
    34 |     1700 |  0.000000E+00 |  0.000000E+00 | -5.049293E-01 | -5.054240E-01
    35 |     1750 |  0.000000E+00 |  0.000000E+00 | -5.051205E-01 | -5.058090E-01
    36 |     1800 |  0.000000E+00 |  0.000000E+00 | -5.053169E-01 | -5.059597E-01
    37 |     1850 |  0.000000E+00 |  0.000000E+00 | -5.055598E-01 | -5.067092E-01
    38 |     1900 |  0.000000E+00 |  0.000000E+00 | -5.058344E-01 | -5.067092E-01
    39 |     1950 |  0.000000E+00 |  0.000000E+00 | -5.061865E-01 | -5.072494E-01
    40 |     2000 |  0.000000E+00 |  0.000000E+00 | -5.064220E-01 | -5.073431E-01
    41 |     2050 |  0.000000E+00 |  0.000000E+00 | -5.068005E-01 | -5.081851E-01
    42 |     2100 |  0.000000E+00 |  0.000000E+00 | -5.070952E-01 | -5.085442E-01
    43 |     2150 |  0.000000E+00 |  0.000000E+00 | -5.074588E-01 | -5.085442E-01
    44 |     2200 |  0.000000E+00 |  0.000000E+00 | -5.078524E-01 | -5.093114E-01
    45 |     2250 |  0.000000E+00 |  0.000000E+00 | -5.082867E-01 | -5.098399E-01
    46 |     2300 |  0.000000E+00 |  0.000000E+00 | -5.087176E-01 | -5.098399E-01
    47 |     2350 |  0.000000E+00 |  0.000000E+00 | -5.091165E-01 | -5.099068E-01
    48 |     2400 |  0.000000E+00 |  0.000000E+00 | -5.095298E-01 | -5.102335E-01
    49 |     2450 |  0.000000E+00 |  0.000000E+00 | -5.098345E-01 | -5.105389E-01
    50 |     2500 |  0.000000E+00 |  0.000000E+00 | -5.101265E-01 | -5.108688E-01
    51 |     2550 |  0.000000E+00 |  0.000000E+00 | -5.104231E-01 | -5.112445E-01
    52 |     2600 |  0.000000E+00 |  0.000000E+00 | -5.107525E-01 | -5.114826E-01
    53 |     2650 |  0.000000E+00 |  0.000000E+00 | -5.110290E-01 | -5.118996E-01
    54 |     2700 |  0.000000E+00 |  0.000000E+00 | -5.112750E-01 | -5.124239E-01
    55 |     2750 |  0.000000E+00 |  0.000000E+00 | -5.115946E-01 | -5.124642E-01
    56 |     2800 |  0.000000E+00 |  0.000000E+00 | -5.118256E-01 | -5.126645E-01
    57 |     2850 |  0.000000E+00 |  0.000000E+00 | -5.121022E-01 | -5.126645E-01
    58 |     2900 |  0.000000E+00 |  0.000000E+00 | -5.123371E-01 | -5.128177E-01
    59 |     2950 |  0.000000E+00 |  0.000000E+00 | -5.125054E-01 | -5.128177E-01
    60 |     3000 |  0.000000E+00 |  0.000000E+00 | -5.126078E-01 | -5.129150E-01
    61 |     3050 |  0.000000E+00 |  0.000000E+00 | -5.127029E-01 | -5.129655E-01
    62 |     3100 |  0.000000E+00 |  0.000000E+00 | -5.127876E-01 | -5.129655E-01
    63 |     3150 |  0.000000E+00 |  0.000000E+00 | -5.128550E-01 | -5.131367E-01
    64 |     3200 |  0.000000E+00 |  0.000000E+00 | -5.129328E-01 | -5.132712E-01
    65 |     3250 |  0.000000E+00 |  0.000000E+00 | -5.130119E-01 | -5.132849E-01
    66 |     3300 |  0.000000E+00 |  0.000000E+00 | -5.130944E-01 | -5.133150E-01
    67 |     3350 |  0.000000E+00 |  0.000000E+00 | -5.131745E-01 | -5.133480E-01
    68 |     3400 |  0.000000E+00 |  0.000000E+00 | -5.132618E-01 | -5.134317E-01
    69 |     3450 |  0.000000E+00 |  0.000000E+00 | -5.133163E-01 | -5.134561E-01
    70 |     3500 |  0.000000E+00 |  0.000000E+00 | -5.133671E-01 | -5.134833E-01
    71 |     3550 |  0.000000E+00 |  0.000000E+00 | -5.134048E-01 | -5.134834E-01
    72 |     3600 |  0.000000E+00 |  0.000000E+00 | -5.134433E-01 | -5.136118E-01
    73 |     3650 |  0.000000E+00 |  0.000000E+00 | -5.134627E-01 | -5.136118E-01
    74 |     3700 |  0.000000E+00 |  0.000000E+00 | -5.134814E-01 | -5.136118E-01
    75 |     3750 |  0.000000E+00 |  0.000000E+00 | -5.135100E-01 | -5.136132E-01
    76 |     3800 |  0.000000E+00 |  0.000000E+00 | -5.135378E-01 | -5.136557E-01
    77 |     3850 |  0.000000E+00 |  0.000000E+00 | -5.135664E-01 | -5.136557E-01
    78 |     3900 |  0.000000E+00 |  0.000000E+00 | -5.135939E-01 | -5.136844E-01
    79 |     3950 |  0.000000E+00 |  0.000000E+00 | -5.136235E-01 | -5.137929E-01
    80 |     4000 |  0.000000E+00 |  0.000000E+00 | -5.136583E-01 | -5.137983E-01
    81 |     4050 |  0.000000E+00 |  0.000000E+00 | -5.136864E-01 | -5.138033E-01
    82 |     4100 |  0.000000E+00 |  0.000000E+00 | -5.137170E-01 | -5.138508E-01
    83 |     4150 |  0.000000E+00 |  0.000000E+00 | -5.137579E-01 | -5.138666E-01
    84 |     4200 |  0.000000E+00 |  0.000000E+00 | -5.137994E-01 | -5.139128E-01
    85 |     4250 |  0.000000E+00 |  0.000000E+00 | -5.138396E-01 | -5.140404E-01
    86 |     4300 |  0.000000E+00 |  0.000000E+00 | -5.138854E-01 | -5.140498E-01
    87 |     4350 |  0.000000E+00 |  0.000000E+00 | -5.139133E-01 | -5.141967E-01
    88 |     4400 |  0.000000E+00 |  0.000000E+00 | -5.139546E-01 | -5.141967E-01
    89 |     4450 |  0.000000E+00 |  0.000000E+00 | -5.140218E-01 | -5.147526E-01
    90 |     4500 |  0.000000E+00 |  0.000000E+00 | -5.141040E-01 | -5.156605E-01
    91 |     4550 |  0.000000E+00 |  0.000000E+00 | -5.142399E-01 | -5.156605E-01
    92 |     4600 |  0.000000E+00 |  0.000000E+00 | -5.144019E-01 | -5.157213E-01
    93 |     4650 |  0.000000E+00 |  0.000000E+00 | -5.147286E-01 | -5.157683E-01
    94 |     4700 |  0.000000E+00 |  0.000000E+00 | -5.154050E-01 | -5.165078E-01
    95 |     4750 |  0.000000E+00 |  0.000000E+00 | -5.157949E-01 | -5.165540E-01
    96 |     4800 |  0.000000E+00 |  0.000000E+00 | -5.160264E-01 | -5.166291E-01
    97 |     4850 |  0.000000E+00 |  0.000000E+00 | -5.163338E-01 | -5.166643E-01
    98 |     4900 |  0.000000E+00 |  0.000000E+00 | -5.165219E-01 | -5.166849E-01
    99 |     4950 |  0.000000E+00 |  0.000000E+00 | -5.165851E-01 | -5.167818E-01
   100 |     5000 |  0.000000E+00 |  0.000000E+00 | -5.166481E-01 | -5.169221E-01


Optimizer_pymoo: Optimization run finished
  Success: True
  Best maximize_power = 25846.105909172766

Results problem 'layout_opt':
------------------------------
  Float variables:
    0: X_0000 = 8.896358e+02
    1: Y_0000 = -1.973433e+02
    2: X_0001 = 1.084184e+01
    3: Y_0001 = 7.604301e+02
    4: X_0002 = 9.840827e+02
    5: Y_0002 = 8.443889e+02
    6: X_0003 = 9.159412e+02
    7: Y_0003 = 1.499215e+03
    8: X_0004 = 9.937176e+02
    9: Y_0004 = 4.555305e+02
    10: X_0005 = 8.251784e+00
    11: Y_0005 = 1.145879e+03
    12: X_0006 = 3.867863e+02
    13: Y_0006 = 4.038220e+02
    14: X_0007 = 5.260085e+02
    15: Y_0007 = 1.497802e+03
    16: X_0008 = 9.564081e+02
    17: Y_0008 = 1.366148e+02
    18: X_0009 = 9.822039e+02
    19: Y_0009 = 1.209201e+03
------------------------------
  Objectives:
    0: maximize_power = 2.584611e+04
------------------------------
  Constraints:
    0: boundary_0000 = -3.451458e-01
    1: boundary_0001 = -1.084184e+01
    2: boundary_0002 = -1.591733e+01
    3: boundary_0003 = -7.848904e-01
    4: boundary_0004 = -6.282416e+00
    5: boundary_0005 = -8.251784e+00
    6: boundary_0006 = -5.693910e-01
    7: boundary_0007 = -2.198379e+00
    8: boundary_0008 = -4.359194e+01
    9: boundary_0009 = -1.779611e+01
    10: dist_0_1      = -1.047849e+03
    11: dist_0_2      = -7.940049e+02
    12: dist_0_3      = -1.444762e+03
    13: dist_0_4      = -4.091182e+02
    14: dist_0_5      = -1.354575e+03
    15: dist_0_6      = -5.317457e+02
    16: dist_0_7      = -1.481707e+03
    17: dist_0_8      = -8.856793e+01
    18: dist_0_9      = -1.157587e+03
    19: dist_1_2      = -7.248556e+02
    20: dist_1_3      = -9.163357e+02
    21: dist_1_4      = -7.770814e+02
    22: dist_1_5      = -1.334577e+02
    23: dist_1_6      = -2.661733e+02
    24: dist_1_7      = -6.475074e+02
    25: dist_1_8      = -8.808023e+02
    26: dist_1_9      = -8.180184e+02
    27: dist_2_3      = -4.063621e+02
    28: dist_2_4      = -1.369777e+02
    29: dist_2_5      = -7.693433e+02
    30: dist_2_6      = -4.902009e+02
    31: dist_2_7      = -5.459850e+02
    32: dist_2_8      = -4.563150e+02
    33: dist_2_9      = -1.128167e+02
    34: dist_3_4      = -7.945786e+02
    35: dist_3_5      = -7.220362e+02
    36: dist_3_6      = -9.645077e+02
    37: dist_3_7      = -1.379353e+02
    38: dist_3_8      = -1.111201e+03
    39: dist_3_9      = -4.548794e+01
    40: dist_4_5      = -9.512140e+02
    41: dist_4_6      = -3.571300e+02
    42: dist_4_7      = -8.904014e+02
    43: dist_4_8      = -6.909073e+01
    44: dist_4_9      = -5.017582e+02
    45: dist_5_6      = -5.810288e+02
    46: dist_5_7      = -3.740363e+02
    47: dist_5_8      = -1.132780e+03
    48: dist_5_9      = -7.240084e+02
    49: dist_6_7      = -8.508029e+02
    50: dist_6_8      = -3.771809e+02
    51: dist_6_9      = -7.495773e+02
    52: dist_7_8      = -1.175611e+03
    53: dist_7_9      = -2.878191e+02
    54: dist_8_9      = -8.208961e+02
------------------------------
  Success: True
------------------------------

<xarray.Dataset> Size: 451kB
Dimensions:     (state: 216, turbine: 10)
Coordinates:
  * state       (state) int64 2kB 0 1 2 3 4 5 6 ... 209 210 211 212 213 214 215
Dimensions without coordinates: turbine
Data variables: (12/27)
    AMB_CT      (state, turbine) float64 17kB 0.995 0.995 0.995 ... 0.081 0.081
    AMB_P       (state, turbine) float64 17kB 109.1 109.1 109.1 ... 5e+03 5e+03
    AMB_REWS    (state, turbine) float64 17kB 3.5 3.5 3.5 3.5 ... 20.0 20.0 20.0
    AMB_REWS2   (state, turbine) float64 17kB 3.5 3.5 3.5 3.5 ... 20.0 20.0 20.0
    AMB_REWS3   (state, turbine) float64 17kB 3.5 3.5 3.5 3.5 ... 20.0 20.0 20.0
    AMB_RHO     (state, turbine) float64 17kB 1.225 1.225 1.225 ... 1.225 1.225
    ...          ...
    YAW         (state, turbine) float64 17kB 0.0 0.0 0.0 ... 350.0 350.0 350.0
    order       (state, turbine) int64 17kB 3 7 9 5 2 1 4 6 ... 5 9 1 2 6 4 8 0
    order_inv   (state, turbine) int64 17kB 2 3 6 9 7 0 1 5 ... 2 4 0 5 6 9 8 1
    order_ssel  (state, turbine) int64 17kB 0 0 0 0 0 0 ... 215 215 215 215 215
    weight      (state, turbine) float64 17kB 0.00158 0.00158 ... 0.00013
    tname       (turbine) <U2 80B 'T0' 'T1' 'T2' 'T3' ... 'T6' 'T7' 'T8' 'T9'

This visualizes the results, once the layout and once the mean wind speed over all wind rose states:

In [10]:
fig, axs = plt.subplots(1, 2, figsize=(12, 8))

foxes.output.FarmLayoutOutput(farm).get_figure(fig=fig, ax=axs[0])

o = foxes.output.FlowPlots2D(algo, results.problem_results)
p_min = np.array([-100.0, -350.0])
p_max = np.array([1100.0, 1600.0])
fig = o.get_mean_fig_xy(
    "WS",
    resolution=20,
    fig=fig,
    ax=axs[1],
    xmin=p_min[0],
    xmax=p_max[0],
    ymin=p_min[1],
    ymax=p_max[1],
)
dpars = dict(alpha=0.6, zorder=10, p_min=p_min, p_max=p_max)
farm.boundary.add_to_figure(axs[1], fill_mode="outside_white", pars_distance=dpars)

plt.show()
States 'StatesTable': Reading file /home/jonas/venv/foxes-opt/lib/python3.11/site-packages/foxes/data/states/wind_rose_bremen.csv
../_images/notebooks_layout_opt_20_1.png

Finally, now that all calculations are done, we close the engine:

In [11]:
engine.finalize()