import numpy as np
from foxes.core import WakeK
from foxes.models.wake_models.gaussian import GaussianWakeModel
from foxes.config import config
import foxes.variables as FV
import foxes.constants as FC
[docs]
class Bastankhah2014(GaussianWakeModel):
"""
The Bastankhah 2014 wake model
Notes
-----
Reference:
"A new analytical model for wind-turbine wakes"
Majid Bastankhah, Fernando Porté-Agel
https://doi.org/10.1016/j.renene.2014.01.002
Attributes
----------
sbeta_factor: float
Factor multiplying sbeta
induction: foxes.core.AxialInductionModel or str
The induction model
wake_k: foxes.core.WakeK
Handler for the wake growth parameter k
:group: models.wake_models.wind
"""
[docs]
def __init__(self, superposition, sbeta_factor=0.2, induction="Madsen", **wake_k):
"""
Constructor.
Parameters
----------
superposition: str
The wind speed deficit superposition.
sbeta_factor: float
Factor multiplying sbeta
induction: foxes.core.AxialInductionModel or str
The induction model
wake_k: dict, optional
Parameters for the WakeK class
"""
super().__init__(superpositions={FV.WS: superposition})
self.sbeta_factor = sbeta_factor
self.induction = induction
self.wake_k = WakeK(**wake_k)
[docs]
def __repr__(self):
iname = (
self.induction if isinstance(self.induction, str) else self.induction.name
)
s = f"{type(self).__name__}"
s += f"({self.superpositions[FV.WS]}, induction={iname}, "
s += self.wake_k.repr() + ")"
return s
[docs]
def sub_models(self):
"""
List of all sub-models
Returns
-------
smdls: list of foxes.core.Model
All sub models
"""
return [self.wake_k, self.induction]
[docs]
def initialize(self, algo, verbosity=0, force=False):
"""
Initializes the model.
Parameters
----------
algo: foxes.core.Algorithm
The calculation algorithm
verbosity: int
The verbosity level, 0 = silent
force: bool
Overwrite existing data
"""
if isinstance(self.induction, str):
self.induction = algo.mbook.axial_induction[self.induction]
super().initialize(algo, verbosity, force)
[docs]
def calc_amplitude_sigma(
self,
algo,
mdata,
fdata,
tdata,
downwind_index,
x,
):
"""
Calculate the amplitude and the sigma,
both depend only on x (not on r).
Parameters
----------
algo: foxes.core.Algorithm
The calculation algorithm
mdata: foxes.core.MData
The model data
fdata: foxes.core.FData
The farm data
tdata: foxes.core.TData
The target point data
downwind_index: int
The index in the downwind order
x: numpy.ndarray
The x values, shape: (n_states, n_targets)
Returns
-------
amsi: tuple
The amplitude and sigma, both numpy.ndarray
with shape (n_st_sel,)
st_sel: numpy.ndarray of bool
The state-target selection, for which the wake
is non-zero, shape: (n_states, n_targets)
"""
# get ct:
ct = self.get_data(
FV.CT,
FC.STATE_TARGET,
lookup="w",
algo=algo,
fdata=fdata,
tdata=tdata,
downwind_index=downwind_index,
upcast=True,
)
# select targets:
st_sel = (x > 1e-8) & (ct > 1e-8)
if np.any(st_sel):
# apply selection:
x = x[st_sel]
ct = ct[st_sel]
# get D:
D = self.get_data(
FV.D,
FC.STATE_TARGET,
lookup="w",
algo=algo,
fdata=fdata,
tdata=tdata,
downwind_index=downwind_index,
upcast=False,
selection=st_sel,
)
# get k:
k = self.wake_k(
FC.STATE_TARGET,
algo=algo,
fdata=fdata,
tdata=tdata,
downwind_index=downwind_index,
upcast=False,
selection=st_sel,
)
# calculate sigma:
# beta = 0.5 * (1 + np.sqrt(1.0 - ct)) / np.sqrt(1.0 - ct)
a = self.induction.ct2a(ct)
beta = (1 - a) / (1 - 2 * a)
sigma = k * x + self.sbeta_factor * np.sqrt(beta) * D
del beta, a
# calculate amplitude:
ct_eff = ct / (8 * (sigma / D) ** 2)
ampld = np.maximum(-2 * self.induction.ct2a(ct_eff), -1)
# case no targets:
else:
st_sel = np.zeros_like(x, dtype=bool)
n_sp = np.sum(st_sel)
ampld = np.zeros(n_sp, dtype=config.dtype_double)
sigma = np.zeros(n_sp, dtype=config.dtype_double)
return {FV.WS: (ampld, sigma)}, st_sel