# Partial wakes verification¶

In the following we investigate the accuracy of various partial wakes models.

We do this by comparison with the approximation of the truth that is provided by the choice `rotor_model=grid400`

in combination with `partial_wakes=rotor_points`

.

Our playground is the following turbine and wind setup:

Uniform wind from wind direction 270 degrees

One turbine located at

`x = y = 0`

, causing a wakeAnother turbine located at

`x = 4D`

and 1000 different`y`

positions between`y = -500 m`

and`y = 500 m`

, acting as a “probe” of the wind field.

This setup will lead to different partial wakes situations, and the partial wakes models will try to predict the correct effective wake effect at the probe turbine.

Let’s see how well they perform at this task!

```
In [1]:
```

```
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import foxes
import foxes.variables as FV
```

We start by creating a pandas DataFrame object that contains data for 1000 ambient states, including the different `y`

positions of the probe turbine:

```
In [2]:
```

```
Ny = 1000
ws0 = 9.0
sdata = pd.DataFrame(index=range(Ny + 1))
sdata.index.name = "state"
sdata["ws"] = ws0
sdata["wd"] = 270
sdata["ti"] = 0.05
sdata["rho"] = 1.225
sdata["y"] = np.linspace(-500, 500, Ny + 1)
sdata
```

```
Out[2]:
```

ws | wd | ti | rho | y | |
---|---|---|---|---|---|

state | |||||

0 | 9.0 | 270 | 0.05 | 1.225 | -500.0 |

1 | 9.0 | 270 | 0.05 | 1.225 | -499.0 |

2 | 9.0 | 270 | 0.05 | 1.225 | -498.0 |

3 | 9.0 | 270 | 0.05 | 1.225 | -497.0 |

4 | 9.0 | 270 | 0.05 | 1.225 | -496.0 |

... | ... | ... | ... | ... | ... |

996 | 9.0 | 270 | 0.05 | 1.225 | 496.0 |

997 | 9.0 | 270 | 0.05 | 1.225 | 497.0 |

998 | 9.0 | 270 | 0.05 | 1.225 | 498.0 |

999 | 9.0 | 270 | 0.05 | 1.225 | 499.0 |

1000 | 9.0 | 270 | 0.05 | 1.225 | 500.0 |

1001 rows × 5 columns

The main *foxes* calculation is carried out by the following function. Notice how the turbine model `SetFarmVars`

is used for initializing the different `y`

positions:

```
In [3]:
```

```
def calc(rotor, pwake, wake):
states = foxes.input.states.StatesTable(
sdata,
output_vars=[FV.WS, FV.WD, FV.TI, FV.RHO],
var2col={FV.WS: "ws", FV.WD: "wd", FV.TI: "ti", FV.RHO: "rho"},
)
ydata = np.full((len(sdata.index), 2), np.nan)
ydata[:, 1] = sdata["y"].to_numpy()
ttype="DTU10MW"
mbook = foxes.ModelBook()
mbook.turbine_models["sety"] = foxes.models.turbine_models.SetFarmVars(
pre_rotor=True
)
mbook.turbine_models["sety"].add_var(FV.Y, ydata)
D = mbook.turbine_types[ttype].D
farm = foxes.WindFarm()
farm.add_turbine(
foxes.Turbine(
xy=np.array([0.0, 0.0]),
turbine_models=[ttype]
),
verbosity=0,
)
farm.add_turbine(
foxes.Turbine(
xy=np.array([4*D, 0.0]),
turbine_models=["sety", ttype],
),
verbosity=0,
)
algo = foxes.algorithms.Downwind(
farm,
states,
wake_models=[wake],
rotor_model=rotor,
partial_wakes=pwake,
mbook=mbook,
verbosity=0,
)
print(f"Calculating rotor = {rotor}, pwake = {pwake}")
farm_results = algo.calc_farm()
return farm_results, D
```

This function calls the above function for a given list of partial wakes models, called `pwakes`

:

```
In [4]:
```

```
def run_calc(rotor, pwakes, wake):
results = {}
for pwake in pwakes:
farm_results, D = calc(rotor, pwake, wake)
results[pwake] = farm_results
return results, D
```

Another function will be used for evaluating the calculation results dictionary, creating a plot of results:

```
In [5]:
```

```
def get_plot(results, D, rotor, figsize=(10, 4)):
fig, ax = plt.subplots(figsize=figsize)
for pwake, fres in results.items():
ls = "--" if pwake=="rotor_points" else "-"
lw = 3 if pwake=="rotor_points" else 2
ax.plot(
fres[FV.Y][:, 1]/D,
fres[FV.REWS][:, 1]/ws0,
linestyle=ls,
linewidth=lw,
alpha=0.8,
label=pwake,
)
title = f"4D behind the rotor, ws0 = {ws0} m/s, rotor = {rotor}"
ax.set_title(title)
ax.set_xlabel("y/D")
ax.legend()
return ax
```

Now we can start the evaluation. First, let’s look at partial wakes of the `grid`

type, for a Gaussian wake model:

```
In [6]:
```

```
rotor = "grid400"
wake = "Bastankhah2014_linear_k004"
pwakes = ["rotor_points", "centre", "grid4", "grid9", "grid16", "grid100"]
results, D = run_calc(rotor, pwakes, wake)
ax = get_plot(results, D, rotor)
plt.show()
ax = get_plot(results, D, rotor, figsize=(7, 6))
ax.set_xlim(-0.2, 0.2)
ax.set_ylim(0.73, 0.78)
ax.legend(loc="upper center")
plt.show()
```

```
Calculating rotor = grid400, pwake = rotor_points
Calculating rotor = grid400, pwake = centre
Calculating rotor = grid400, pwake = grid4
Calculating rotor = grid400, pwake = grid9
Calculating rotor = grid400, pwake = grid16
Calculating rotor = grid400, pwake = grid100
```

Next, we repeat the exercise with `axiwake`

partial wakes, which approach the “true” result with much less wake evaluation points:

```
In [7]:
```

```
rotor = "grid400"
wake = "Bastankhah2014_linear_k004"
pwakes = ["rotor_points", "centre", "axiwake4", "axiwake6", "axiwake9"]
results, D = run_calc(rotor, pwakes, wake)
ax = get_plot(results, D, rotor)
plt.show()
ax = get_plot(results, D, rotor, figsize=(7, 6))
ax.set_xlim(-0.2, 0.2)
ax.set_ylim(0.73, 0.78)
ax.legend(loc="upper center")
plt.show()
```

```
Calculating rotor = grid400, pwake = rotor_points
Calculating rotor = grid400, pwake = centre
Calculating rotor = grid400, pwake = axiwake4
Calculating rotor = grid400, pwake = axiwake6
Calculating rotor = grid400, pwake = axiwake9
```

For top-hat models, the `top_hat`

partial wakes are exact and fast:

```
In [8]:
```

```
rotor = "grid400"
wake = "Jensen_linear_k004"
pwakes = ["rotor_points", "centre", "grid16", "axiwake9", "top_hat"]
results, D = run_calc(rotor, pwakes, wake)
ax = get_plot(results, D, rotor)
plt.show()
ax = get_plot(results, D, rotor, figsize=(7, 6))
ax.set_xlim(-0.5, 0.5)
ax.set_ylim(0.65, 0.78)
ax.legend(loc="upper center")
plt.show()
```

```
Calculating rotor = grid400, pwake = rotor_points
Calculating rotor = grid400, pwake = centre
Calculating rotor = grid400, pwake = grid16
Calculating rotor = grid400, pwake = axiwake9
Calculating rotor = grid400, pwake = top_hat
```

Clearly the `top_hat`

choice is the only one that can handle wake models with `top_hat`

shape.