Source code for wcomp.floris_interface


import copy
from pathlib import Path

import numpy as np
from floris.tools import FlorisInterface
from windIO.utils.yml_utils import load_yaml

from .base_interface import WCompBase
from .output_struct import WakePlane, WakeProfile
from .plotting import plot_plane, plot_profile

# This dictionary maps generic model names in the windIO input file
# to the tool's specific name. It also maps parameter names from the
# referenced papers to the parameters in the implementation.
# "windIO_model_name": {
#   "model_ref": the current software's reference for this wake model,
#   "parameters": {
#       "model parameter": windIO parameter
#   }
# }
WAKE_MODEL_MAPPING = {

    # Velocity models
    "jensen": {
        "model_ref": "jensen",
        "parameters": {
            "we": "alpha",
        }
    },
    # "bastankhah2014": {     # NOT IMPLEMENTED
    #     "model_ref": None,
    #     "parameters": {}
    # },
    "bastankhah2016": {
        "model_ref": "gauss",
        "parameters": {
            "alpha": "alpha",
            "beta": "beta",
            "ka": "ka",
            "kb": "kb",
        }
    },
    "turbopark": {
        "model_ref": "turbopark",
        "parameters": {
            "A": "A",
        }
    },

    # Deflection model
    "jimenez": {
        "model_ref": "jimenez",
        "parameters": {
            "kd": "beta",
        }
    },
    "bastankhah2016_deflection": {
        "model_ref": "gauss",
        "parameters": {
            "alpha": "alpha",
            "beta": "beta",
            "ka": "ka",
            "kb": "kb",
        }
    },
}

basic_dict = {
    'name': 'Jensen-Jimenez',
    'description': 'Three turbines using Jensen / Jimenez models',
    'floris_version': 'v3.4.0',
    'logging': {
        'console': {'enable': True, 'level': 'WARNING'},
        'file': {'enable': False, 'level': 'WARNING'}
    },
    'solver': {
        'type': 'turbine_grid', 'turbine_grid_points': 3
    },
    'farm': {
        'layout_x': [0.0, 630.0, 1260.0],
        'layout_y': [0.0, 0.0, 0.0],
        'turbine_type': ['nrel_5MW']
    },
    'flow_field': {
        'air_density': 1.225,
        'reference_wind_height': -1,
        'turbulence_intensity': 0.06,
        'wind_directions': [270.0],
        'wind_shear': 0.12,
        'wind_speeds': [8.0],
        'wind_veer': 0.0
    },
    'wake': {
        'model_strings': {
            'combination_model': 'sosfs',
            'deflection_model': 'jimenez',
            'turbulence_model': 'crespo_hernandez',
            'velocity_model': 'jensen'
        },
        'enable_secondary_steering': False,
        'enable_yaw_added_recovery': False,
        'enable_transverse_velocities': False,
        'wake_deflection_parameters': {
            'jimenez': {
                'ad': 0.0,
                'bd': 0.0,
                'kd': 0.05
            }
        },
        'wake_velocity_parameters': {
            'jensen': {
                'we': 0.05
            }
        },
        'wake_turbulence_parameters': {
            'crespo_hernandez': {
                'initial': 0.1,
                'constant': 0.5,
                'ai': 0.8,
                'downstream': -0.32
            }
        }
    }
}

[docs] class WCompFloris(WCompBase): """ `WCompFloris` implements the wcomp interface to the FLORIS software. The connection points to FLORIS are shown in the diagram below. .. mermaid:: classDiagram class FlorisInterface class WCompFloris { fi : FlorisInterface floris_dict : dict hub_height rotor_diameter yaw_angles : ndarray AEP() _create_floris_dict(windIO_wes_dict: dict) } WCompFloris --* FlorisInterface """ LINE_PLOT_COLOR = "green" LINE_PLOT_MARKER = "" LINE_PLOT_LINESTYLE = "--" LINE_PLOT_LINEWIDTH = 2 LEGEND = "FLORIS" def __init__(self, input_file: str | Path): input_dictionary = load_yaml(input_file) self.floris_dict = self._create_floris_dict(input_dictionary) self.fi = FlorisInterface(self.floris_dict) n_wind_directions = self.fi.floris.flow_field.n_wind_directions n_wind_speeds = self.fi.floris.flow_field.n_wind_speeds n_turbines = self.fi.floris.farm.n_turbines self.yaw_angles = np.zeros((n_wind_directions, n_wind_speeds, n_turbines)) self.yaw_angles[:,:] = input_dictionary["attributes"]["analyses"]["yaw_angles"] self.fi.calculate_wake(yaw_angles=self.yaw_angles) @property def rotor_diameter(self) -> float: return self.fi.floris.farm.rotor_diameters[0,0,0] @property def hub_height(self) -> float: return self.fi.floris.farm.hub_heights[0,0,0] ### Create the model from windIO def _create_floris_dict(self, wes): # "wes" is the wind energy system windIO file new_dict = copy.deepcopy(basic_dict) new_dict["name"] = wes["name"] new_dict["description"] = wes["name"] new_dict["floris_version"] = "v3.4.0" wes_wind_resource = wes["site"]["energy_resource"]["wind_resource"] new_dict["flow_field"] = { 'air_density': 1.225, 'reference_wind_height': -1, 'turbulence_intensity': wes_wind_resource["turbulence_intensity"]["data"], 'wind_directions': wes_wind_resource["wind_direction"], 'wind_shear': 0.12, 'wind_speeds': wes_wind_resource["wind_speed"], 'wind_veer': 0.0 } wes_turbine = wes["wind_farm"]["turbines"] new_turbine = { "turbine_type": wes_turbine["name"], "generator_efficiency": 1.0, "hub_height": wes_turbine["hub_height"], "pP": 1.88, "pT": 1.88, # How to set these? "rotor_diameter": wes_turbine["rotor_diameter"], "TSR": 8.0, # Can we calculate this from diameter and power curve? "ref_density_cp_ct": 1.225, "ref_tilt_cp_ct": 0.0, # Set to 0 "power_thrust_table": { "power": wes_turbine["performance"]["Cp_curve"]["Cp_values"], "thrust": wes_turbine["performance"]["Ct_curve"]["Ct_values"], "wind_speed": wes_turbine["performance"]["Ct_curve"]["Ct_wind_speeds"] } } new_dict["farm"] = { "layout_x": wes["wind_farm"]["layouts"]["initial_layout"]["coordinates"]["x"], "layout_y": wes["wind_farm"]["layouts"]["initial_layout"]["coordinates"]["y"], 'turbine_type': [new_turbine] } wes_analysis = wes["attributes"]["analyses"] _velocity_model_mapping = WAKE_MODEL_MAPPING[wes_analysis["wake_model"]["velocity"]["name"]] _velocity_model = _velocity_model_mapping["model_ref"] _velocity_model_parameters = { k: wes_analysis["wake_model"]["velocity"]["parameters"][v] for k, v in _velocity_model_mapping["parameters"].items() } if wes_analysis["wake_model"]["deflection"]["name"] is not None: _deflection_model_mapping = WAKE_MODEL_MAPPING[wes_analysis["wake_model"]["deflection"]["name"]] _deflection_model = _deflection_model_mapping["model_ref"] _deflection_model_parameters = { k: wes_analysis["wake_model"]["deflection"]["parameters"][v] for k, v in _deflection_model_mapping["parameters"].items() } else: _deflection_model = "none" _deflection_model_parameters = {} new_dict['wake'] = { 'model_strings': { 'combination_model': 'sosfs', 'deflection_model': _deflection_model, 'turbulence_model': 'crespo_hernandez', 'velocity_model': _velocity_model }, 'enable_secondary_steering': False, 'enable_yaw_added_recovery': False, 'enable_transverse_velocities': False, 'wake_deflection_parameters': {_deflection_model: _deflection_model_parameters}, 'wake_velocity_parameters': {_velocity_model: _velocity_model_parameters}, 'wake_turbulence_parameters': { 'crespo_hernandez': { 'initial': 0.1, 'constant': 0.5, 'ai': 0.8, 'downstream': -0.32 } } } return new_dict ### Post processing
[docs] def AEP(self): self.fi.get_farm_AEP()
# 1D line plots
[docs] def vertical_profile_plot( self, wind_direction: float, x_coordinate: float, y_coordinate: float, zmax: float ) -> WakeProfile: cut_plane = self.fi.calculate_y_plane( crossstream_dist=y_coordinate, wd=[wind_direction], x_resolution=self.N_POINTS_1D, z_resolution=self.N_POINTS_1D, x_bounds=[x_coordinate, x_coordinate], z_bounds=[0, zmax], yaw_angles=self.yaw_angles, ) profile = WakeProfile( cut_plane.df.x2, cut_plane.df.u, ) plot_profile( profile, # direction='x', # component='u', color=self.LINE_PLOT_COLOR, marker=self.LINE_PLOT_MARKER, linestyle=self.LINE_PLOT_LINESTYLE, linewidth=self.LINE_PLOT_LINEWIDTH, label=self.LEGEND ) return profile
[docs] def streamwise_profile_plot( self, wind_direction: float, y_coordinate: float, xmin: float, xmax: float ) -> WakeProfile: cut_plane = self.fi.calculate_y_plane( crossstream_dist=y_coordinate, wd=[wind_direction], x_resolution=self.N_POINTS_1D, z_resolution=self.N_POINTS_1D, x_bounds=[xmin, xmax], z_bounds=[self.hub_height, self.hub_height], yaw_angles=self.yaw_angles, ) profile = WakeProfile( cut_plane.df.x1, cut_plane.df.u, ) plot_profile( profile, # direction='x', # component='u', color=self.LINE_PLOT_COLOR, marker=self.LINE_PLOT_MARKER, linestyle=self.LINE_PLOT_LINESTYLE, linewidth=self.LINE_PLOT_LINEWIDTH, label=self.LEGEND ) return profile
[docs] def xsection_profile_plot( self, wind_direction: float, x_coordinate: float, ymin: float, ymax: float ) -> WakeProfile: cut_plane = self.fi.calculate_horizontal_plane( height=self.hub_height, wd=[wind_direction], # x_resolution=resolution[0], y_resolution=self.N_POINTS_1D, x_bounds=[x_coordinate, x_coordinate], y_bounds=[ymin, ymax], yaw_angles=self.yaw_angles, ) profile = WakeProfile( cut_plane.df.x2, cut_plane.df.u, ) plot_profile( profile, # direction='y', # component='u', color=self.LINE_PLOT_COLOR, marker=self.LINE_PLOT_MARKER, linestyle=self.LINE_PLOT_LINESTYLE, linewidth=self.LINE_PLOT_LINEWIDTH, label=self.LEGEND ) return profile
# 2D contour plots
[docs] def horizontal_contour(self, wind_direction: float) -> WakePlane: coordinates = np.array([ (x, y, self.hub_height) for x, y in list(zip(self.fi.layout_x, self.fi.layout_y)) ]) _x, _y, _ = coordinates.T x_min = np.min(_x) - 2 * self.rotor_diameter x_max = np.max(_x) + 10 * self.rotor_diameter y_min = np.min(_y) - 2 * self.rotor_diameter y_max = np.max(_y) + 2 * self.rotor_diameter x, y = np.meshgrid( np.linspace(x_min, x_max, int((x_max - x_min) / self.RESOLUTION_2D) + 1), np.linspace(y_min, y_max, int((y_max - y_min) / self.RESOLUTION_2D) + 1), indexing='ij' ) x = x.flatten() y = y.flatten() z = self.hub_height * np.ones_like(x) u = self.fi.sample_flow_at_points(x, y, z)[0,0] plane = WakePlane(x, y, u, "z") plot_plane( plane, # cmap='Blues_r', # color_bar=True, clevels=100 ) return plane
[docs] def xsection_contour(self, wind_direction: float, x_coordinate: float) -> WakePlane: coordinates = np.array([ (x, y, self.hub_height) for x, y in list(zip(self.fi.layout_x, self.fi.layout_y)) ]) _, _y, _z = coordinates.T y_min = np.min(_y) - 2 * self.rotor_diameter y_max = np.max(_y) + 2 * self.rotor_diameter z_min = 0.001 z_max = 6 * self.hub_height y, z = np.meshgrid( np.linspace(y_min, y_max, int((y_max - y_min) / self.RESOLUTION_2D) + 1), np.linspace(z_min, z_max, int((z_max - z_min) / self.RESOLUTION_2D) + 1), indexing='ij' ) y = y.flatten() z = z.flatten() x = x_coordinate * np.ones_like(y) u = self.fi.sample_flow_at_points(x, y, z)[0,0] plane = WakePlane(y, z, u, "x") plot_plane( plane, # cmap='Blues_r', # color_bar=True, clevels=100 ) return plane