Source code for foxes.models.wake_deflections.bastankhah2016

import numpy as np

from foxes.config import config
from foxes.core.wake_deflection import WakeDeflection
from foxes.core.wake_model import WakeK
from foxes.models.wake_models.wind.bastankhah16 import (
    Bastankhah2016Model,
    Bastankhah2016,
)
import foxes.constants as FC
import foxes.variables as FV


[docs] class Bastankhah2016Deflection(WakeDeflection): """ Bend the wakes for yawed turbines, based on the Bastankhah 2016 wake model Notes ----- Reference: "Experimental and theoretical study of wind turbine wakes in yawed conditions" Majid Bastankhah, Fernando Porté-Agel https://doi.org/10.1017/jfm.2016.595 Attributes ---------- model: Bastankhah2016Model The model for computing common data alpha: float model parameter used to determine onset of far wake region, if not found in wake model beta: float model parameter used to determine onset of far wake region, if not found in wake model wake_k: dict Parameters for the WakeK class, if not found in wake model induction: foxes.core.AxialInductionModel The induction model, if not found in wake model :group: models.wake_deflections """
[docs] def __init__( self, alpha=0.58, beta=0.07, induction="Madsen", **wake_k, ): """ Constructor. Parameters ---------- alpha: float model parameter used to determine onset of far wake region, if not found in wake model beta: float model parameter used to determine onset of far wake region, if not found in wake model induction: foxes.core.AxialInductionModel or str The induction model, if not found in wake model wake_k: dict, optional Parameters for the WakeK class, if not found in wake model """ super().__init__() self.model = None self.alpha = alpha self.beta = beta self.induction = induction self.wake_k = None self._wake_k_pars = wake_k setattr(self, FV.YAWM, 0.0)
[docs] def __repr__(self): s = f"{type(self).__name__}(" s += self.wake_k.repr() if self.wake_k is not None else "" s += ")" return s
[docs] def sub_models(self): """ List of all sub-models Returns ------- smdls: list of foxes.core.Model Names of all sub models """ return [self.wake_k, self.model]
[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 not self.initialized: for w in algo.wake_models.values(): if isinstance(w, Bastankhah2016): if not w.initialized: w.initialize(algo, verbosity, force) self.model = w.model self.wake_k = w.wake_k break if self.model is None: self.model = Bastankhah2016Model( alpha=self.alpha, beta=self.beta, induction=self.induction ) if self.wake_k is None: wake_k = WakeK(**self._wake_k_pars) if not wake_k.all_none: self.wake_k = wake_k else: for w in algo.wake_models.values(): if hasattr(w, "wake_k"): self.wake_k = w.wake_k break super().initialize(algo, verbosity, force)
def _update_y(self, algo, mdata, fdata, tdata, downwind_index, x, y): """ Helper function for y deflection """ # get gamma: gamma = self.get_data( FV.YAWM, FC.STATE_TARGET, lookup="wfs", algo=algo, fdata=fdata, tdata=tdata, upcast=True, downwind_index=downwind_index, accept_nan=False, ) gamma = gamma * np.pi / 180 # get k: k = self.wake_k( FC.STATE_TARGET, lookup_ti="f", lookup_k="sf", algo=algo, fdata=fdata, tdata=tdata, upcast=True, downwind_index=downwind_index, accept_nan=False, ) # run model calculation: self.model.calc_data(algo, mdata, fdata, tdata, downwind_index, x, gamma, k) # select targets: st_sel = self.model.get_data(Bastankhah2016Model.ST_SEL, mdata) if np.any(st_sel): # prepare: n_st_sel = np.sum(st_sel) ydef = np.zeros((n_st_sel,), dtype=config.dtype_double) # collect data: near = self.model.get_data(Bastankhah2016Model.NEAR, mdata) far = ~near # near wake: if np.any(near): # collect data: delta = self.model.get_data(Bastankhah2016Model.DELTA_NEAR, mdata) # set deflection: ydef[near] = delta # far wake: if np.any(far): # collect data: delta = self.model.get_data(Bastankhah2016Model.DELTA_FAR, mdata) # set deflection: ydef[far] = delta # apply deflection: y[st_sel] -= ydef
[docs] def calc_deflection( self, algo, mdata, fdata, tdata, downwind_index, coos, ): """ Calculates the wake deflection. This function optionally adds FC.WDEFL_ROT_ANGLE or FC.WDEFL_DWS_FACTOR to the tdata. 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 of the wake causing turbine in the downwind order coos: numpy.ndarray The wake frame coordinates of the evaluation points, shape: (n_states, n_targets, n_tpoints, 3) Returns ------- coos: numpy.ndarray The wake frame coordinates of the evaluation points, shape: (n_states, n_targets, n_tpoints, 3) """ # take rotor average: xy = np.einsum("stpd,p->std", coos[..., :2], tdata[FC.TWEIGHTS]) x = xy[:, :, 0] y = xy[:, :, 1] # apply deflection: self._update_y(algo, mdata, fdata, tdata, downwind_index, x, y) coos[..., 1] = y[:, :, None] return coos