{ "cells": [ { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "# Yawed rotor wakes" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "This example demonstrates wake modelling for yawed wind turbine rotors, following [Bastankhah & Porte-Agel](https://doi.org/10.1017/jfm.2016.595). Let's start by importing the required packages:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%matplotlib inline\n", "import numpy as np\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "\n", "import foxes\n", "import foxes.variables as FV\n", "import foxes.constants as FC" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Our aim in this example is to model two turbines that are located along the x axis, and uniform wind from west. Three states should be computed, with identical wind conditions, in a setup where the first turbine is yawed by 30, 0, -30 degrees, respectively.\n", "\n", "We can realize these wind conditions with the `StatesTable` class, using a `pandas.DataFrame` object as data as source:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sdata = pd.DataFrame(index=range(3))\n", "sdata[FV.WS] = 8.0\n", "sdata[FV.WD] = 270.0\n", "sdata" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "states = foxes.input.states.StatesTable(\n", " data_source=sdata,\n", " output_vars=[FV.WS, FV.WD, FV.TI, FV.RHO],\n", " fixed_vars={FV.RHO: 1.225, FV.TI: 0.05},\n", ")" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "For each of these 3 states, and for each of the two turbines, we wish to set the yaw misalignment variable `FV.YAWM`. The following `numpy.ndarray` defines these angles, with shape `(3, 2)` representing `(FC.STATE, FC.TURBINE)` dimensions:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "yawm = np.array([\n", " [30.0, 0.0], [0.0, 0.0], [-30.0, 0.0]\n", "])\n", "yawm" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "For transferring these data to the variable `FV.YAWM`, we invoke the turbine model `SetFarmVars`, by adding it to the `turbine_models` section of the `ModelBook`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "mbook = foxes.ModelBook()\n", "mbook.turbine_models[\"set_yawm\"] = foxes.models.turbine_models.SetFarmVars()\n", "mbook.turbine_models[\"set_yawm\"].add_var(FV.YAWM, yawm)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Notice that this models appears together with the model `yawm2yaw`, which exists by default in the `ModelBook`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "farm = foxes.WindFarm()\n", "farm.add_turbine(\n", " foxes.Turbine(\n", " xy=[0.0, 0.0],\n", " turbine_models=[\"set_yawm\", \"yawm2yaw\", \"NREL5MW\", \"kTI_05\"],\n", " )\n", ")\n", "farm.add_turbine(\n", " foxes.Turbine(\n", " xy=[1000.0, 0.0],\n", " turbine_models=[\"set_yawm\", \"yawm2yaw\", \"NREL5MW\", \"kTI_05\"],\n", " )\n", ")" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "The `yawm2yaw` updates the `FV.YAW` variable by adding `FV.YAWM` to the wind direction, i.e., the yaw misalignment defines the absolute rotor yaw in this case (in cases where the latter is set, you may want to include the inverse model `yaw2yawm` instead).\n", "\n", "We can now create an `Algorithm` object and calculate the farm results. We combine the yaw-sensitive wake model `PorteAgel_linear` (c.f. [reference](https://doi.org/10.1017/jfm.2016.595)) with the wake frame called `yawed`, which is also based on the same publication:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "algo = foxes.algorithms.Downwind(\n", " mbook,\n", " farm,\n", " states,\n", " rotor_model=\"centre\",\n", " wake_models=[\"Bastankhah2016_linear\", \"IECTI2019_max\"],\n", " wake_frame=\"yawed\",\n", " partial_wakes_model=\"auto\",\n", " chunks=None,\n", " verbosity=0,\n", ")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "farm_results = algo.calc_farm()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Once the wind farm results are ready we can continue and plot the wind field:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "o = foxes.output.FlowPlots2D(algo, farm_results)\n", "g = o.gen_states_fig_xy(FV.WS, resolution=10, xmin=-100, xmax=3000, verbosity=0)\n", "for fig in g:\n", " plt.show()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "As well as some vertical cross sections some distance downstream:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "o = foxes.output.FlowPlots2D(algo, farm_results)\n", "g = o.gen_states_fig_yz(FV.WS, resolution=10, x=750, ymin=-200,ymax=200,zmin=0,zmax=250,verbosity=0)\n", "for fig in g:\n", " plt.show()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Clearly, the model bends the wake and a frontal hit of the downstream rotor is avoided under yawed conditions. This also reflects in the rotor equivalent wind speed (REWS) values:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "farm_df = farm_results.to_dataframe()\n", "print(\"\\nFarm results:\\n\")\n", "print(farm_df[[FV.X, FV.WD, FV.YAW, FV.YAWM, FV.REWS]])" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "By default, the turbine type models in `foxes` apply $\\cos(\\gamma)^p$ type yaw corection factors to the variables `FV.P` and `FV.CT`, where `\\gamma` is the yaw misalignment angle (i.e., `FV.YAWM` translated to radiants), and `p` is a constant exponent (in fact there is one for power and another for thrust). These exponents can be setup as desired, here the default values $p_P = 1.88$ and $p_{ct}=1.0$ were invoked.\n", "\n", "Note that the `yawed` wake frame can bend any kind of wake model. For example, we invoked the `CrespoHernandez` model for turbulence intensity wake effects above. This wake model follows the same bending rules as the wind deficit model, even though it is a simple top-hat type model:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "o = foxes.output.FlowPlots2D(algo, farm_results)\n", "g = o.gen_states_fig_xy(FV.TI, resolution=10, xmin=-100, xmax=3000, verbosity=0)\n", "for fig in g:\n", " plt.show()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Similarly any other choice of wake models can be combined with the `yawed` wake frame. Note, however, that the intensity of most wake models only depends on the yaw angle implicitely via ct in such setups." ] } ], "metadata": { "kernelspec": { "display_name": "foxes", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.8" }, "vscode": { "interpreter": { "hash": "67848f0d19d90e849d6febfe41e182bd0ecfb4ff4f03c83c0ab525da86fd1c48" } } }, "nbformat": 4, "nbformat_minor": 2 }