import numpy as np
from foxes.config import config
[docs]
class WindFarm:
"""
The wind farm.
Attributes
----------
name: str
The wind farm name
turbines: list of foxes.core.Turbine
The wind turbines
boundary: foxes.utils.geom2d.AreaGeometry, optional
The wind farm boundary
:group: core
"""
[docs]
def __init__(self, name="wind_farm", boundary=None):
"""
Constructor.
Parameters
----------
name: str
The wind farm name
"""
self.name = name
self.turbines = []
self.boundary = boundary
[docs]
def add_turbine(self, turbine, verbosity=1):
"""
Add a wind turbine to the list.
Parameters
----------
turbine: foxes.core.Turbine
The wind turbine
verbosity: int
The output verbosity, 0 = silent
"""
if turbine.index is None:
turbine.index = len(self.turbines)
if turbine.name is None:
turbine.name = f"T{turbine.index}"
self.turbines.append(turbine)
if verbosity > 0:
print(
f"Turbine {turbine.index}, {turbine.name}: xy=({turbine.xy[0]:.2f}, {turbine.xy[1]:.2f}), {', '.join(turbine.models)}"
)
@property
def n_turbines(self):
"""
The number of turbines in the wind farm
Returns
-------
n_turbines: int
The total number of turbines
"""
return len(self.turbines)
@property
def turbine_names(self):
"""
The list of names of all turbines
Returns
-------
names: list of str
The names of all turbines
"""
return [t.name for t in self.turbines]
@property
def xy_array(self):
"""
Returns an array of the wind farm ground points
Returns
-------
xya: numpy.ndarray
The turbine ground positions, shape: (n_turbines, 2)
"""
return np.array([t.xy for t in self.turbines], dtype=config.dtype_double)
[docs]
def get_xy_bounds(self, extra_space=None, algo=None):
"""
Returns min max points of the wind farm ground points
Parameters
----------
extra_space: float or str, optional
The extra space, either float in m,
or str for units of D, e.g. '2.5D'
algo: foxes.core.Algorithm, optional
The algorithm
Returns
-------
x_mima: numpy.ndarray
The (x_min, x_max) point
y_mima: numpy.ndarray
The (y_min, y_max) point
"""
if self.boundary is not None:
xy = None
p_min, p_max = self.boundary.p_min(), self.boundary.p_max()
else:
xy = self.xy_array
p_min, p_max = np.min(xy, axis=0), np.max(xy, axis=0)
if extra_space is not None:
if isinstance(extra_space, str):
assert (
algo is not None
), f"WindFarm: require algo argument for extra_space '{extra_space}'"
assert (
len(extra_space) > 1 and extra_space[-1] == "D"
), f"Expecting float or str like '2.5D', got extra_space = '{extra_space}'"
extra_space = float(extra_space[:-1])
rds = self.get_rotor_diameters(algo)
if xy is None:
extra_space *= np.max(rds)
else:
p_min = np.min(xy - extra_space * rds[:, None], axis=0)
p_max = np.max(xy + extra_space * rds[:, None], axis=0)
return p_min, p_max
p_min -= extra_space
p_max += extra_space
return p_min, p_max
[docs]
def get_rotor_diameters(self, algo):
"""
Gets the rotor diameters
Parameters
----------
algo: foxes.core.Algorithm
The algorithm
Returns
-------
rds: numpy.ndarray
The rotor diameters, shape: (n_turbienes,)
"""
rds = [
t.D if t.D is not None else algo.farm_controller.turbine_types[i].D
for i, t in enumerate(self.turbines)
]
return np.array(rds, dtype=config.dtype_double)
[docs]
def get_hub_heights(self, algo):
"""
Gets the hub heights
Parameters
----------
algo: foxes.core.Algorithm
The algorithm
Returns
-------
hhs: numpy.ndarray
The hub heights, shape: (n_turbienes,)
"""
hhs = [
t.H if t.H is not None else algo.farm_controller.turbine_types[i].H
for i, t in enumerate(self.turbines)
]
return np.array(hhs, dtype=config.dtype_double)