Source code for windkit.plot.roughness_rose

# (c) 2022 DTU Wind Energy
"""

Roughness rose plotting
"""

import numpy as np

from ..import_manager import _import_optional_dependency
from ._helpers import check_multipoint, check_plotting_attrs


def _add_plot_dist(ds):
    """Calculate the width of each roughness level for every sector and add it to
    the Dataset

    Parameters
    ----------
    ds : xarray.Dataset
        WindKit Dataset representation of a binned wind climate for a single point
        (for the moment)

    Returns
    -------
    ds : xarray.Dataset
        WindKit Dataset representation of a binned wind climate for a single point
        (for the moment)
    """
    max_dist = max(float(ds.dist.max()) * 1.1, 15_000)

    plot_dist = np.full(
        (ds.sizes["sector"], ds.sizes["max_rou_changes"] + 2),
        np.nan,
        dtype=ds.dist.dtype,
    )
    dist_from_origin = np.full(
        (ds.sizes["sector"], ds.sizes["max_rou_changes"] + 1),
        np.nan,
        dtype=ds.dist.dtype,
    )
    plot_dist[:, 0] = 0.0
    for s in range(ds.sizes["sector"]):
        nrch = ds.nrch.values[s]
        plot_dist[s, 1 : nrch + 1] = ds.dist.values[s, 0:nrch]  # rl changes
        plot_dist[s, nrch + 1] = max_dist  # No more rl changes

    ds["plot_dist"] = (ds.z0.dims, np.round(np.diff(plot_dist)))

    for s in range(ds.sizes["sector"]):
        for indx in range(len(ds["plot_dist"][s, :])):
            if not np.isnan(ds["plot_dist"][s, indx]):
                dist_from_origin[s, indx] = np.sum(ds["plot_dist"][s, 0 : indx + 1])

    ds["dist_from_origin"] = (ds.z0.dims, np.round(dist_from_origin))
    return ds


def _add_log_z0(ds):
    """Create a logarithmic scale of the roughness levels for every sector and add it
    to the Dataset

    Parameters
    ----------
    ds : xarray.Dataset
        WindKit Dataset representation of a binned wind climate for a single point (for
        the moment)

     Returns
    -------
    ds : xarray.Dataset
        WindKit Dataset representation of a binned wind climate for a single point
        (for the moment)
    """
    log_z0 = np.full(
        (ds.sizes["sector"], ds.sizes["max_rou_changes1"]),
        np.nan,
        dtype=ds.dist.dtype,
    )

    for s in range(ds.sizes["sector"]):
        for r in range(ds.sizes["max_rou_changes1"]):
            if ds.z0[s, r] != 0:
                log_z0[s, r] = np.log(ds.z0[s, r])
            else:
                log_z0[s, r] = ds.z0[s, r]

    ds["log_z0"] = (ds.z0.dims, log_z0)
    return ds


[docs] def roughness_rose(ds, style="rose", gap=False): """Create roughness rose plot. Parameters ---------- ds : xarray.Dataset WindKit Dataset representation of a roughness rose single point (for the moment). style : str, optional stacked "bar" plot or circular "rose" plot, by default "rose". gap : bool, optional Include a gap between sectors? (Default: no gap). Returns ------- plotly.graph_objects.Figure Plotly figure for display, additional modification, or output. """ px = _import_optional_dependency("plotly.express") plotly = _import_optional_dependency("plotly") hex_to_rgb = plotly.colors.hex_to_rgb ds = ds.transpose(..., "sector", "max_rou_changes1", "max_rou_changes").squeeze() check_multipoint(ds) ds = _add_plot_dist(ds) rou_rose_plot = _add_log_z0(ds)[["z0", "plot_dist", "log_z0", "dist_from_origin"]] df = rou_rose_plot.to_dataframe().reset_index().dropna() if style == "bar": plot_fun = px.bar plot_sect_name = "y" plot_dist_name = "x" orientation = "h" elif style == "rose": plot_fun = px.bar_polar plot_sect_name = "theta" plot_dist_name = "r" else: raise ValueError('Unknown plot style, please choose one of "bar" or "rose"') z0_title = check_plotting_attrs(rou_rose_plot.z0) distance_title = check_plotting_attrs(ds.dist) sector_title = check_plotting_attrs(rou_rose_plot.sector) hovertemplate = ( sector_title + ": %{customdata[1]}<br>" + z0_title + ": %{customdata[0]:.3f}<br>" + distance_title + ": %{customdata[3]}<br>Width of roughness level [m]: %{customdata[2]}" ) plot_dict = { plot_sect_name: "sector", plot_dist_name: "plot_dist", "color": "log_z0", "labels": {"sector": sector_title, "plot_dist": distance_title}, "color_continuous_scale": [ (0, f"rgb{hex_to_rgb('#2A479E')}"), (0.3, f"rgb{hex_to_rgb('#0DCF69')}"), (0.6, f"rgb{hex_to_rgb('#F9FA96')}"), (0.75, f"rgb{hex_to_rgb('#A8906A')}"), (1.0, f"rgb{hex_to_rgb('#006600')}"), ], "range_color": [np.log(0.0002), np.log(3.0)], "custom_data": [ "z0", "sector", "plot_dist", "dist_from_origin", ], # Data for the hover info } if style == "bar": plot_dict["orientation"] = orientation fig1 = plot_fun(df, **plot_dict) ticks = [0.0002, 0.03, 0.1, 0.4, 1.5] upd_dict = { "font": {"size": 13}, "coloraxis_colorbar": dict( title="z0 [m]", tickvals=np.array(np.log(np.array(ticks))), ticktext=[f"{i:g}" for i in ticks], ), } if not gap: if style == "bar": upd_dict["bargap"] = 0 elif style == "rose": fig1.update_polars(bargap=0) fig1.update_layout(**upd_dict) return fig1.update_traces(hovertemplate=hovertemplate)