Example: Plot velocity deficit profiles

Example: Plot velocity deficit profiles#

"""Example: Plot velocity deficit profiles

This example illustrates how to plot velocity deficit profiles at several locations
downstream of a turbine. Here we use the following definition:
    velocity_deficit = (homogeneous_wind_speed - u) / homogeneous_wind_speed
        , where u is the wake velocity obtained when the incoming wind speed is the
        same at all heights and equal to `homogeneous_wind_speed`.
"""


import matplotlib.pyplot as plt
import numpy as np
from matplotlib import ticker

import floris.flow_visualization as flowviz
from floris import FlorisModel
from floris.flow_visualization import VelocityProfilesFigure
from floris.utilities import reverse_rotate_coordinates_rel_west


# The first two functions are just used to plot the coordinate system in which the
# profiles are sampled. Please go to the main function to begin the example.
def plot_coordinate_system(x_origin, y_origin, wind_direction):
    quiver_length = 1.4 * D
    plt.quiver(
        [x_origin, x_origin],
        [y_origin, y_origin],
        [quiver_length, quiver_length],
        [0, 0],
        angles=[270 - wind_direction, 360 - wind_direction],
        scale_units="x",
        scale=1,
    )
    annotate_coordinate_system(x_origin, y_origin, quiver_length)


def annotate_coordinate_system(x_origin, y_origin, quiver_length):
    x1 = np.array([quiver_length + 0.35 * D, 0.0])
    x2 = np.array([0.0, quiver_length + 0.35 * D])
    x3 = np.array([90.0, 90.0])
    x, y, _ = reverse_rotate_coordinates_rel_west(
        fmodel.wind_directions,
        x1[None, :],
        x2[None, :],
        x3[None, :],
        x_center_of_rotation=0.0,
        y_center_of_rotation=0.0,
    )
    x = np.squeeze(x, axis=0) + x_origin
    y = np.squeeze(y, axis=0) + y_origin
    plt.text(x[0], y[0], "$x_1$", bbox={"facecolor": "white"})
    plt.text(x[1], y[1], "$x_2$", bbox={"facecolor": "white"})


if __name__ == "__main__":
    D = 125.88  # Turbine diameter
    hub_height = 90.0
    homogeneous_wind_speed = 8.0

    fmodel = FlorisModel("../inputs/gch.yaml")
    fmodel.set(layout_x=[0.0], layout_y=[0.0])

    # ------------------------------ Single-turbine layout ------------------------------
    # We first show how to sample and plot velocity deficit profiles on a single-turbine layout.
    # Lines are drawn on a horizontal plane to indicate were the velocity is sampled.
    downstream_dists = D * np.array([3, 5, 7])
    # Sample three profiles along three corresponding lines that are all parallel to the y-axis
    # (cross-stream direction). The streamwise location of each line is given in `downstream_dists`.
    profiles = fmodel.sample_velocity_deficit_profiles(
        direction="cross-stream",
        downstream_dists=downstream_dists,
        homogeneous_wind_speed=homogeneous_wind_speed,
    )

    horizontal_plane = fmodel.calculate_horizontal_plane(height=hub_height)
    fig, ax = plt.subplots(figsize=(6.4, 3))
    flowviz.visualize_cut_plane(horizontal_plane, ax)
    colors = ["b", "g", "c"]
    for i, profile in enumerate(profiles):
        # Plot profile coordinates on the horizontal plane
        ax.plot(profile["x"], profile["y"], colors[i], label=f"x/D={downstream_dists[i] / D:.1f}")
    ax.set_xlabel("x [m]")
    ax.set_ylabel("y [m]")
    ax.set_title("Streamwise velocity in a horizontal plane: gauss velocity model")
    fig.tight_layout(rect=[0, 0, 0.82, 1])
    ax.legend(bbox_to_anchor=[1.29, 1.04])

    # Initialize a VelocityProfilesFigure. The workflow is similar to a matplotlib Figure:
    # Initialize it, plot data, and then customize it further if needed.
    profiles_fig = VelocityProfilesFigure(
        downstream_dists_D=downstream_dists / D,
        layout=["cross-stream"],
        coordinate_labels=["x/D", "y/D"],
    )
    # Add profiles to the VelocityProfilesFigure. This method automatically matches the supplied
    # profiles to the initialized axes in the figure.
    profiles_fig.add_profiles(profiles, color="k")

    # Change velocity model to jensen, get the velocity deficit profiles,
    # and add them to the figure.
    floris_dict = fmodel.core.as_dict()
    floris_dict["wake"]["model_strings"]["velocity_model"] = "jensen"
    fmodel = FlorisModel(floris_dict)
    profiles = fmodel.sample_velocity_deficit_profiles(
        direction="cross-stream",
        downstream_dists=downstream_dists,
        homogeneous_wind_speed=homogeneous_wind_speed,
        resolution=400,
    )
    profiles_fig.add_profiles(profiles, color="r")

    # The dashed reference lines show the extent of the rotor
    profiles_fig.add_ref_lines_x2([-0.5, 0.5])
    for ax in profiles_fig.axs[0]:
        ax.xaxis.set_major_locator(ticker.MultipleLocator(0.2))

    profiles_fig.axs[0, 0].legend(["gauss", "jensen"], fontsize=11)
    profiles_fig.fig.suptitle(
        "Velocity deficit profiles from different velocity models",
        fontsize=14,
    )

    # -------------------------------- Two-turbine layout --------------------------------
    # This is a two-turbine case where the wind direction is north-west. Velocity profiles
    # are sampled behind the second turbine. This illustrates the need for a
    # sampling-coordinate-system (x1, x2, x3) that is rotated such that x1 is always in the
    # streamwise direction. The user may define the origin of this coordinate system
    # (i.e. where to start sampling the profiles).
    wind_direction = 315.0  # Try to change this
    downstream_dists = D * np.array([3, 5])
    floris_dict = fmodel.core.as_dict()
    floris_dict["wake"]["model_strings"]["velocity_model"] = "gauss"
    fmodel = FlorisModel(floris_dict)
    # Let (x_t1, y_t1) be the location of the second turbine
    x_t1 = 2 * D
    y_t1 = -2 * D
    fmodel.set(wind_directions=[wind_direction], layout_x=[0.0, x_t1], layout_y=[0.0, y_t1])

    # Extract profiles at a set of downstream distances from the starting point (x_start, y_start)
    cross_profiles = fmodel.sample_velocity_deficit_profiles(
        direction="cross-stream",
        downstream_dists=downstream_dists,
        homogeneous_wind_speed=homogeneous_wind_speed,
        x_start=x_t1,
        y_start=y_t1,
    )

    horizontal_plane = fmodel.calculate_horizontal_plane(
        height=hub_height, x_bounds=[-2 * D, 9 * D]
    )
    ax = flowviz.visualize_cut_plane(horizontal_plane)
    colors = ["b", "g", "c"]
    for i, profile in enumerate(cross_profiles):
        ax.plot(
            profile["x"],
            profile["y"],
            colors[i],
            label=f"$x_1/D={downstream_dists[i] / D:.1f}$",
        )
    ax.set_xlabel("x [m]")
    ax.set_ylabel("y [m]")
    ax.set_title("Streamwise velocity in a horizontal plane")
    ax.legend()
    plot_coordinate_system(x_origin=x_t1, y_origin=y_t1, wind_direction=wind_direction)

    # Sample velocity deficit profiles in the vertical direction at the same downstream
    # locations as before. We stay directly downstream of the turbine (i.e. x2 = 0). These
    # profiles are almost identical to the cross-stream profiles. However, we now explicitly
    # set the profile range. The default range is [-2 * D, 2 * D].
    vertical_profiles = fmodel.sample_velocity_deficit_profiles(
        direction="vertical",
        profile_range=[-1.5 * D, 1.5 * D],
        downstream_dists=downstream_dists,
        homogeneous_wind_speed=homogeneous_wind_speed,
        x_start=x_t1,
        y_start=y_t1,
    )

    profiles_fig = VelocityProfilesFigure(
        downstream_dists_D=downstream_dists / D,
        layout=["cross-stream", "vertical"],
    )
    profiles_fig.add_profiles(cross_profiles + vertical_profiles, color="k")

    profiles_fig.set_xlim([-0.05, 0.85])
    profiles_fig.axs[1, 0].set_ylim([-2.2, 2.2])
    for ax in profiles_fig.axs[0]:
        ax.xaxis.set_major_locator(ticker.MultipleLocator(0.4))

    profiles_fig.fig.suptitle(
        "Cross-stream profiles at hub-height, and\nvertical profiles at $x_2 = 0$",
        fontsize=14,
    )

    plt.show()
import warnings
warnings.filterwarnings('ignore')
../../_images/5e49ff92ab64ee85b5cf79c14a13021c177ee4a5b1f8b388000f68d7d48702cb.png ../../_images/9e629bae329808d5102f2a9714e5db2a9b0e9a74234095dd173751a72e76b041.png ../../_images/fcb94e3692e184e10c73daabfc2bd761d4064e32c4d31bedda6437e8f6f9166f.png ../../_images/96355c5c38bfb185ad6e1d756b8e721a73bf77f941dcb5b555aa0c5d845fd8dc.png