Wake steering¶
This example demonstrates how to optimize the yaw angles of wind turbine rotors, such that the wakes are steered away from downwind turbines.
In [1]:
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import foxes
import foxes.variables as FV
from iwopy.interfaces.pymoo import Optimizer_pymoo
from foxes_opt.problems import OptFarmVars
from foxes_opt.objectives import MaxFarmPower
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 this example we are looking at a small 3 x 3 regualar wind farm with NREL5MW
turbines:
In [3]:
farm = foxes.WindFarm()
foxes.input.farm_layout.add_grid(
farm,
xy_base=np.array([500.0, 500.0]),
step_vectors=np.array([[1300.0, 0], [200, 600.0]]),
steps=(3, 3),
turbine_models=["opt_yawm", "yawm2yaw", "NREL5MW"],
verbosity=0,
)
ax = foxes.output.FarmLayoutOutput(farm).get_figure()
plt.show()
plt.close(ax.get_figure())
Notice how the name of the optimization model opt_yawm
, which will be defined shortly, appears in the list of turbine models. The idea is as follows:
First, the optimizer sets the
FV.YAWM
variable, representing yaw misalignment in degrees, i.e., a delta yaw value from greedy conditionsThen, the model
yawm2yaw
translates this into the absolute yaw value, i.e., the absolute turbine axis orientation, expressed in degreesThis setting is then used for thrust and power calculations, by the turbine type model
NREL5MW
.
We are considering sinlge-state uniform inflow conditions:
In [4]:
states = foxes.input.states.SingleStateStates(ws=9, wd=270, ti=0.06, rho=1.225)
The algorithm is defined next, including a TI wake model and the Bastankhah2016
wake model. Also notice the wake frame choice yawed
, which realizes the wake bending for yawed conditions:
In [5]:
algo = foxes.algorithms.Downwind(
farm,
states,
rotor_model="grid25",
wake_models=["IECTI2019k_quadratic_ambka02", "Bastankhah2016_linear_ambka02"],
wake_frame="yawed",
verbosity=0,
)
The optimization problem is power maximization by finding the optimal value of the variable FV.YAWM
for each turbine:
In [6]:
problem = OptFarmVars("opt_yawm", algo)
problem.add_var(FV.YAWM, float, 0.0, -40.0, 40.0, level="turbine")
problem.add_objective(MaxFarmPower(problem))
problem.initialize()
Problem 'opt_yawm': Optimization variable list
name var type index level state turbine sel_turbine init \
0 YAWM_0000 YAWM float 0 turbine -1 0 0 0.0
1 YAWM_0001 YAWM float 1 turbine -1 1 1 0.0
2 YAWM_0002 YAWM float 2 turbine -1 2 2 0.0
3 YAWM_0003 YAWM float 3 turbine -1 3 3 0.0
4 YAWM_0004 YAWM float 4 turbine -1 4 4 0.0
5 YAWM_0005 YAWM float 5 turbine -1 5 5 0.0
6 YAWM_0006 YAWM float 6 turbine -1 6 6 0.0
7 YAWM_0007 YAWM float 7 turbine -1 7 7 0.0
8 YAWM_0008 YAWM float 8 turbine -1 8 8 0.0
min max pre_rotor model_key
0 -40.0 40.0 False opt_yawm
1 -40.0 40.0 False opt_yawm
2 -40.0 40.0 False opt_yawm
3 -40.0 40.0 False opt_yawm
4 -40.0 40.0 False opt_yawm
5 -40.0 40.0 False opt_yawm
6 -40.0 40.0 False opt_yawm
7 -40.0 40.0 False opt_yawm
8 -40.0 40.0 False opt_yawm
Problem 'opt_yawm' (OptFarmVars): Initializing
----------------------------------------------
n_vars_int : 0
n_vars_float: 9
----------------------------------------------
n_objectives: 1
n_obj_cmptns: 1
----------------------------------------------
n_constraints: 0
n_con_cmptns: 0
----------------------------------------------
Next, we setup the solver:
In [7]:
solver = Optimizer_pymoo(
problem,
problem_pars=dict(vectorize=True),
algo_pars=dict(type="GA", pop_size=100, seed=42),
setup_pars=dict(),
term_pars=("n_gen", 100),
)
solver.initialize()
solver.print_info()
Loading pymoo
pymoo successfully loaded
Initializing Optimizer_pymoo
Selecting sampling: float_random (FloatRandomSampling)
Selecting algorithm: GA (GA)
Problem:
--------
vectorize: True
Algorithm:
----------
type: GA
pop_size: 100
seed: 42
Termination:
------------
n_gen: 100
Now everything is setup, and we can solve the problem:
In [8]:
results = solver.solve()
solver.finalize(results)
=================================================
n_gen | n_eval | f_avg | f_min
=================================================
1 | 100 | -2.716086E-01 | -3.062184E-01
2 | 200 | -2.900518E-01 | -3.075843E-01
3 | 300 | -2.998327E-01 | -3.176548E-01
4 | 400 | -3.066163E-01 | -3.176548E-01
5 | 500 | -3.116990E-01 | -3.181045E-01
6 | 600 | -3.153652E-01 | -3.210101E-01
7 | 700 | -3.178322E-01 | -3.241374E-01
8 | 800 | -3.202072E-01 | -3.241374E-01
9 | 900 | -3.221594E-01 | -3.252988E-01
10 | 1000 | -3.233195E-01 | -3.255576E-01
11 | 1100 | -3.240882E-01 | -3.261391E-01
12 | 1200 | -3.246163E-01 | -3.261391E-01
13 | 1300 | -3.252244E-01 | -3.268285E-01
14 | 1400 | -3.256995E-01 | -3.271945E-01
15 | 1500 | -3.261343E-01 | -3.271945E-01
16 | 1600 | -3.264993E-01 | -3.271945E-01
17 | 1700 | -3.267905E-01 | -3.272931E-01
18 | 1800 | -3.269569E-01 | -3.273335E-01
19 | 1900 | -3.270618E-01 | -3.274558E-01
20 | 2000 | -3.271565E-01 | -3.274558E-01
21 | 2100 | -3.272321E-01 | -3.274674E-01
22 | 2200 | -3.272973E-01 | -3.275297E-01
23 | 2300 | -3.273522E-01 | -3.275642E-01
24 | 2400 | -3.274000E-01 | -3.275892E-01
25 | 2500 | -3.274407E-01 | -3.276063E-01
26 | 2600 | -3.274856E-01 | -3.276894E-01
27 | 2700 | -3.275084E-01 | -3.276894E-01
28 | 2800 | -3.275378E-01 | -3.276894E-01
29 | 2900 | -3.275671E-01 | -3.276894E-01
30 | 3000 | -3.275862E-01 | -3.276894E-01
31 | 3100 | -3.276094E-01 | -3.277010E-01
32 | 3200 | -3.276308E-01 | -3.277010E-01
33 | 3300 | -3.276463E-01 | -3.277112E-01
34 | 3400 | -3.276695E-01 | -3.277478E-01
35 | 3500 | -3.276892E-01 | -3.277723E-01
36 | 3600 | -3.277063E-01 | -3.277793E-01
37 | 3700 | -3.277210E-01 | -3.277793E-01
38 | 3800 | -3.277354E-01 | -3.277924E-01
39 | 3900 | -3.277519E-01 | -3.278052E-01
40 | 4000 | -3.277655E-01 | -3.278052E-01
41 | 4100 | -3.277770E-01 | -3.278052E-01
42 | 4200 | -3.277852E-01 | -3.278054E-01
43 | 4300 | -3.277915E-01 | -3.278054E-01
44 | 4400 | -3.277968E-01 | -3.278081E-01
45 | 4500 | -3.278009E-01 | -3.278086E-01
46 | 4600 | -3.278038E-01 | -3.278117E-01
47 | 4700 | -3.278054E-01 | -3.278122E-01
48 | 4800 | -3.278072E-01 | -3.278145E-01
49 | 4900 | -3.278085E-01 | -3.278154E-01
50 | 5000 | -3.278099E-01 | -3.278155E-01
51 | 5100 | -3.278108E-01 | -3.278172E-01
52 | 5200 | -3.278124E-01 | -3.278172E-01
53 | 5300 | -3.278136E-01 | -3.278172E-01
54 | 5400 | -3.278146E-01 | -3.278188E-01
55 | 5500 | -3.278157E-01 | -3.278193E-01
56 | 5600 | -3.278166E-01 | -3.278194E-01
57 | 5700 | -3.278173E-01 | -3.278197E-01
58 | 5800 | -3.278179E-01 | -3.278209E-01
59 | 5900 | -3.278183E-01 | -3.278210E-01
60 | 6000 | -3.278189E-01 | -3.278210E-01
61 | 6100 | -3.278195E-01 | -3.278213E-01
62 | 6200 | -3.278200E-01 | -3.278213E-01
63 | 6300 | -3.278205E-01 | -3.278213E-01
64 | 6400 | -3.278210E-01 | -3.278214E-01
65 | 6500 | -3.278211E-01 | -3.278214E-01
66 | 6600 | -3.278212E-01 | -3.278214E-01
67 | 6700 | -3.278212E-01 | -3.278214E-01
68 | 6800 | -3.278213E-01 | -3.278214E-01
69 | 6900 | -3.278213E-01 | -3.278215E-01
70 | 7000 | -3.278213E-01 | -3.278215E-01
71 | 7100 | -3.278214E-01 | -3.278216E-01
72 | 7200 | -3.278214E-01 | -3.278217E-01
73 | 7300 | -3.278214E-01 | -3.278217E-01
74 | 7400 | -3.278215E-01 | -3.278217E-01
75 | 7500 | -3.278215E-01 | -3.278217E-01
76 | 7600 | -3.278216E-01 | -3.278218E-01
77 | 7700 | -3.278217E-01 | -3.278218E-01
78 | 7800 | -3.278217E-01 | -3.278218E-01
79 | 7900 | -3.278217E-01 | -3.278218E-01
80 | 8000 | -3.278218E-01 | -3.278218E-01
81 | 8100 | -3.278218E-01 | -3.278218E-01
82 | 8200 | -3.278218E-01 | -3.278218E-01
83 | 8300 | -3.278218E-01 | -3.278218E-01
84 | 8400 | -3.278218E-01 | -3.278218E-01
85 | 8500 | -3.278218E-01 | -3.278218E-01
86 | 8600 | -3.278218E-01 | -3.278218E-01
87 | 8700 | -3.278218E-01 | -3.278218E-01
88 | 8800 | -3.278218E-01 | -3.278218E-01
89 | 8900 | -3.278218E-01 | -3.278218E-01
90 | 9000 | -3.278218E-01 | -3.278219E-01
91 | 9100 | -3.278218E-01 | -3.278219E-01
92 | 9200 | -3.278218E-01 | -3.278219E-01
93 | 9300 | -3.278218E-01 | -3.278219E-01
94 | 9400 | -3.278218E-01 | -3.278219E-01
95 | 9500 | -3.278219E-01 | -3.278219E-01
96 | 9600 | -3.278219E-01 | -3.278219E-01
97 | 9700 | -3.278219E-01 | -3.278219E-01
98 | 9800 | -3.278219E-01 | -3.278219E-01
99 | 9900 | -3.278219E-01 | -3.278219E-01
100 | 10000 | -3.278219E-01 | -3.278219E-01
Optimizer_pymoo: Optimization run finished
Success: True
Best maximize_power = 14751.984832734459
In [9]:
print()
print(results)
fr = results.problem_results.to_dataframe()
fr[[FV.X, FV.Y, FV.AMB_WD, FV.YAWM, FV.TI, FV.REWS, FV.P]]
Results problem 'opt_yawm':
----------------------------
Float variables:
0: YAWM_0000 = -3.024916e+01
1: YAWM_0001 = -2.555786e+01
2: YAWM_0002 = -2.555471e+01
3: YAWM_0003 = -2.458684e+01
4: YAWM_0004 = 2.456818e+01
5: YAWM_0005 = 2.458887e+01
6: YAWM_0006 = 2.077965e-02
7: YAWM_0007 = 1.625032e-02
8: YAWM_0008 = 1.515184e-02
----------------------------
Objectives:
0: maximize_power = 1.475198e+04
----------------------------
Success: True
----------------------------
Out[9]:
X | Y | AMB_WD | YAWM | TI | REWS | P | ||
---|---|---|---|---|---|---|---|---|
state | turbine | |||||||
0 | 0 | 500.0 | 500.0 | 270.0 | -30.249163 | 0.060000 | 9.000000 | 1928.995420 |
1 | 700.0 | 1100.0 | 270.0 | -25.557857 | 0.060000 | 8.119351 | 1544.518581 | |
2 | 900.0 | 1700.0 | 270.0 | -25.554709 | 0.060000 | 8.119564 | 1544.708539 | |
3 | 1800.0 | 500.0 | 270.0 | -24.586841 | 0.065713 | 7.857941 | 1422.846941 | |
4 | 2000.0 | 1100.0 | 270.0 | 24.568175 | 0.070112 | 7.665933 | 1317.609645 | |
5 | 2200.0 | 1700.0 | 270.0 | 24.588871 | 0.070116 | 7.664458 | 1316.361700 | |
6 | 3100.0 | 500.0 | 270.0 | 0.020780 | 0.072090 | 8.227575 | 1941.211689 | |
7 | 3300.0 | 1100.0 | 270.0 | 0.016250 | 0.073104 | 8.128745 | 1867.337034 | |
8 | 3500.0 | 1700.0 | 270.0 | 0.015152 | 0.073078 | 8.130161 | 1868.395284 |
Finally, we can visualize the result by looking at the flow field:
In [10]:
o = foxes.output.FlowPlots2D(algo, results.problem_results)
fig = o.get_mean_fig_xy("WS", resolution=10, xmax=5000)
plt.show()
Clearly the turbines are trying to avoid hitting downwind turbines with their wakes.
Finally, let’s shutdown the foxes engine:
In [11]:
engine.finalize()