Source code for floris.core.wake_velocity.jensen


from typing import Any, Dict

import numexpr as ne
import numpy as np
from attrs import (
    define,
    field,
    fields,
)

from floris.core import (
    BaseModel,
    Farm,
    FlowField,
    Grid,
    Turbine,
)


NUM_EPS = fields(BaseModel).NUM_EPS.default

[docs] @define class JensenVelocityDeficit(BaseModel): """ The Jensen model computes the wake velocity deficit based on the classic Jensen/Park model :cite:`jvm-jensen1983note`. - **we** (*float*): The linear wake decay constant that defines the cone boundary for the wake as well as the velocity deficit. D/2 +/- we*x is the cone boundary for the wake. References: .. bibliography:: /references.bib :style: unsrt :filter: docname in docnames :keyprefix: jvm- """ we: float = field(converter=float, default=0.05)
[docs] def prepare_function( self, grid: Grid, flow_field: FlowField, ) -> Dict[str, Any]: """ This function prepares the inputs from the various FLORIS data structures for use in the Jensen model. This should only be used to 'initialize' the inputs. For any data that should be updated successively, do not use this function and instead pass that data directly to the model function. """ kwargs = { "x": grid.x_sorted, "y": grid.y_sorted, "z": grid.z_sorted, } return kwargs
# @profile
[docs] def function( self, x_i: np.ndarray, y_i: np.ndarray, z_i: np.ndarray, axial_induction_i: np.ndarray, deflection_field_i: np.ndarray, yaw_angle_i: np.ndarray, turbulence_intensity_i: np.ndarray, ct_i: np.ndarray, hub_height_i, rotor_diameter_i, # enforces the use of the below as keyword arguments and adherence to the # unpacking of the results from prepare_function() *, x: np.ndarray, y: np.ndarray, z: np.ndarray, ) -> None: # u is 4-dimensional (n wind speeds, n turbines, grid res 1, grid res 2) # velocities is 3-dimensional (n turbines, grid res 1, grid res 2) # TODO: check the rotations with multiple directions or non-0/270 # grid.rotate_fields(flow_field.wind_directions) # Calculate and apply wake mask # x = grid.x_sorted # mesh_x_rotated - x_coord_rotated # This is the velocity deficit seen by the i'th turbine due to wake effects # from upstream turbines. # Indeces of velocity_deficit corresponding to unwaked turbines will have 0's # velocity_deficit = np.zeros(np.shape(flow_field.u_initial)) rotor_radius = rotor_diameter_i / 2.0 # Numexpr - do not change below without corresponding changes above. dx = ne.evaluate("x - x_i") dy = ne.evaluate("y - y_i - deflection_field_i") dz = ne.evaluate("z - z_i") we = self.we # Construct a boolean mask to include all points downstream of the turbine downstream_mask = ne.evaluate("dx > 0 + NUM_EPS") # Construct a boolean mask to include all points within the wake boundary # as defined by the Jensen model. This is a linear wake expansion that makes # a shape like a cone and starts at the turbine disc. # The left side of the inequality below evaluates the distance from the wake centerline # for all points including positive and negative values. The inequality compares distance # from the centerline and it must be below the line defined by the wake # expansion parameter, "we". boundary_mask = ne.evaluate("sqrt(dy ** 2 + dz ** 2) < we * dx + rotor_radius") # Calculate C for points within the mask and fill points outside with 0 c = np.where( np.logical_and(downstream_mask, boundary_mask), ne.evaluate("(rotor_radius / (rotor_radius + we * dx + NUM_EPS)) ** 2"), # This is "C" 0.0, ) velocity_deficit = ne.evaluate("2 * axial_induction_i * c") return velocity_deficit