Source code for foxes.models.wake_models.ti.iec_ti

import numpy as np

from foxes.core import WakeK
from foxes.models.wake_models.top_hat import TopHatWakeModel
import foxes.variables as FV
import foxes.constants as FC


[docs] class IECTIWake(TopHatWakeModel): """ The TI wake model from IEC-64100-1-2005-8 (2005): Notes ----- Reference: http://orbit.dtu.dk/files/3750291/2009_31.pdf v2: VolLuk: corrected implementation following: IEC-64100-1-2005-8 (Appearently an error in the document by DTU) and the Frandsen wake TI model, from IEC-64100 (2019): http://orbit.dtu.dk/files/3750291/2009_31.pdf Attributes ---------- wake_k: foxes.core.WakeK Handler for the wake growth parameter k :group: models.wake_models.ti """
[docs] def __init__( self, superposition, opening_angle=21.6, iec_type="2019", induction="Betz", **wake_k, ): """ Constructor. Parameters ---------- superposition: str The TI wake superposition. opening_angle: float, optional The wake opening angle. The wake growth parameter k is calculated based on the wake opening angle. iec_type: str Either '2005' or '2019'/'Frandsen' wake_k: dict, optional Parameters for the WakeK class """ super().__init__(superpositions={FV.TI: superposition}, induction=induction) self.iec_type = iec_type self.wake_k = None if opening_angle is None: self.wake_k = WakeK(**wake_k) else: if "k" in wake_k or "ka" in wake_k or "kb" in wake_k: raise KeyError( f"Can handle 'opening_angle' or ('k', 'ka', 'kb') parameters, not both" ) self._k = float(np.tan(np.deg2rad(opening_angle / 2.0)))
[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.TI]}, induction={iname}" if self.wake_k is not None: s += ", " + self.wake_k.repr() s += ")" 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] if self.wake_k is not None else []
[docs] def new_wake_deltas(self, algo, mdata, fdata, tdata): """ Creates new empty wake delta arrays. 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 Returns ------- wake_deltas: dict Key: variable name, value: The zero filled wake deltas, shape: (n_states, n_turbines, n_rpoints, ...) """ return {FV.TI: np.zeros_like(tdata[FC.TARGETS][..., 0])}
[docs] def calc_wake_radius( self, algo, mdata, fdata, tdata, downwind_index, x, ct, ): """ Calculate the wake radius, depending on x only (not 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) ct: numpy.ndarray The ct values of the wake-causing turbines, shape: (n_states, n_targets) Returns ------- wake_r: numpy.ndarray The wake radii, shape: (n_states, n_targets) """ if self.wake_k is None: return self._k * x else: D = self.get_data( FV.D, FC.STATE_TARGET, lookup="w", algo=algo, fdata=fdata, tdata=tdata, downwind_index=downwind_index, upcast=True, ) k = self.wake_k( FC.STATE_TARGET, algo=algo, fdata=fdata, tdata=tdata, upcast=False, downwind_index=downwind_index, ) return D / 2 + k * x
[docs] def calc_centreline( self, algo, mdata, fdata, tdata, downwind_index, st_sel, x, wake_r, ct, ): """ Calculate centre line results of wake deltas. 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 st_sel: numpy.ndarray of bool The state-target selection, for which the wake is non-zero, shape: (n_states, n_targets) x: numpy.ndarray The x values, shape: (n_st_sel,) wake_r: numpy.ndarray The wake radii, shape: (n_st_sel,) ct: numpy.ndarray The ct values of the wake-causing turbines, shape: (n_st_sel,) Returns ------- cl_del: dict The centre line wake deltas. Key: variable name str, varlue: numpy.ndarray, shape: (n_st_sel,) """ # read D from extra data: 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 ws: ws = self.get_data( FV.REWS, FC.STATE_TARGET, lookup="w", algo=algo, fdata=fdata, tdata=tdata, downwind_index=downwind_index, upcast=False, selection=st_sel, ) # calculate wind deficit: if self.iec_type == "2005": cl_deltas = np.sqrt(0.9) / (1.5 + 0.3 * x / D * np.sqrt(ws)) elif self.iec_type == "2019" or self.iec_type == "Frandsen": cl_deltas = 1.0 / (1.5 + 0.8 * x / D / np.sqrt(ct)) else: raise TypeError( f"Type of IEC {self.iec_type} not found. Select '2015' or '2019'/'Frandsen'." ) return {FV.TI: cl_deltas}