{ "cells": [ { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "# Power mask" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "This example demonstrates how to derate or boost turbines by using a turbine model called `PowerMask`. We need the following imports:" ] }, { "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": [ "We start by creating a simple states table, this time via a `pandas.DataFrame` object:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sdata = pd.DataFrame(index=range(8))\n", "sdata.index.name = FC.STATE\n", "sdata[FV.WS] = [5., 8., 11., 14., 18., 20., 22., 25.]\n", "print(sdata)\n", "\n", "states = foxes.input.states.StatesTable(\n", " data_source=sdata,\n", " output_vars=[FV.WS, FV.WD, FV.TI, FV.RHO],\n", " fixed_vars={FV.WD: 270.0, FV.TI: 0.05, FV.RHO: 1.225},\n", ")" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Next, we create the power mask for these states. The idea is that for each state and turbine, we can set a maximal power value in a table. \n", "\n", "- If the calculated power value is above this value, the turbine is derated.\n", "- If the maximal power exceeds the nominal power of the turbine type and the calculated value is at nominal power, then the turbine is boosted to the maximal power value.\n", "- If the maximal power value in the power mask is `NaN`, then the turbine is neither derated nor boosted (normal operation)\n", "\n", "Again we create a `DataFrame` that contains the power mask data:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "pmask = pd.DataFrame(index=sdata.index, columns=[f\"PMax_{i}\" for i in range(5)])\n", "pmask.loc[np.s_[:4], \"PMax_4\"] = 1000\n", "pmask.loc[np.s_[4:], \"PMax_4\"] = 6000\n", "pmask.loc[np.s_[2:], \"PMax_2\"] = 3000\n", "pmask.loc[0, \"PMax_0\"] = 300.0\n", "pmask.loc[3, \"PMax_0\"] = 1000.0\n", "print(pmask)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Using this data we can now create the model book. Notice how the variable `FV.MAX_P` is set to the above values via the turbine model `SetFarmVars`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "mbook = foxes.models.ModelBook()\n", "mbook.turbine_models[\"set_Pmax\"] = foxes.models.turbine_models.SetFarmVars()\n", "mbook.turbine_models[\"set_Pmax\"].add_var(FV.MAX_P, pmask)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "We can now create a wind farm that consists of 5 turbines in a row. Some thoughts about the `turbine_models` argument:\n", "\n", "- The actual `PowerMask` turbine model is pre-defined in the default `ModelBook` under the name `PMask`, so we can just add it to the turbine model list.\n", "- It should appear after the turbine type model `NREL5`, since `PMask` corrects the results od the latter.\n", "- Furthermore, `PMask` should be placed somewhere _after_ the above created turbine model `set_Pmax` in the list of turbine models, such that the values of the variable `FV.MAX_P` are present at the time when `PMask` is called.\n", "- The models `NREL5` and `set_Pmax` have no influence on each other, so their order does not matter.\n", "\n", "We choose the following pattern:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "models = [\"NREL5MW\", \"set_Pmax\", \"PMask\"]\n", "\n", "farm = foxes.WindFarm()\n", "foxes.input.farm_layout.add_row(\n", " farm,\n", " xy_base=[0.0, 0.0],\n", " xy_step=[600.0, 0.0],\n", " n_turbines=5,\n", " turbine_models=models,\n", ")" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "We can now setup our algorithm and run the calculation:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "algo = foxes.algorithms.Downwind(\n", " mbook,\n", " farm,\n", " states=states,\n", " rotor_model=\"centre\",\n", " wake_models=[\"Bastankhah2014_linear_k002\"],\n", " wake_frame=\"rotor_wd\",\n", " partial_wakes_model=\"auto\",\n", " chunks={FC.STATE: 1000},\n", " verbosity=0,\n", ")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# run calculation with power mask:\n", "farm_results = algo.calc_farm()\n", "\n", "fr = farm_results.to_dataframe()\n", "print(fr[[FV.WD, FV.AMB_REWS, FV.REWS, FV.MAX_P, FV.AMB_P, FV.P]])\n", "\n", "o = foxes.output.FarmResultsEval(farm_results)\n", "P0 = o.calc_mean_farm_power(ambient=True)\n", "P = o.calc_mean_farm_power()\n", "print(f\"\\nFarm power: {P/1000:.1f} MW, Efficiency = {P/P0*100:.2f} %\")\n", "\n", "# this output is needed later:\n", "o1 = foxes.output.StateTurbineMap(farm_results)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "For a visualization of the results, let's re-run the case without the power mask:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# reset, for run calculation without power mask:\n", "models.remove(\"set_Pmax\")\n", "models.remove(\"PMask\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "farm_results = algo.calc_farm()\n", "o0 = foxes.output.StateTurbineMap(farm_results)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "We are now in the position to create plots that compare the turbine power results, using the two output objects `o0` and `o1`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "# show power:\n", "fig, axs = plt.subplots(1, 3, figsize=(15, 5))\n", "o0.plot_map(\n", " FV.P,\n", " ax=axs[0],\n", " edgecolor=\"white\",\n", " title=\"Power, no power mask\",\n", " cmap=\"YlOrRd\",\n", " vmin=0,\n", " vmax=np.nanmax(pmask),\n", ")\n", "o1.plot_map(\n", " FV.MAX_P,\n", " ax=axs[1],\n", " edgecolor=\"white\",\n", " cmap=\"YlOrRd\",\n", " title=\"Power mask\",\n", " vmin=0,\n", " vmax=np.nanmax(pmask),\n", ")\n", "o1.plot_map(\n", " FV.P,\n", " ax=axs[2],\n", " edgecolor=\"white\",\n", " cmap=\"YlOrRd\",\n", " title=\"Power, with power mask\",\n", " vmin=0,\n", " vmax=np.nanmax(pmask),\n", ")\n", "plt.show()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Similarly, for the thrust coefficients:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# show ct:\n", "fig, axs = plt.subplots(1, 3, figsize=(15, 5))\n", "o0.plot_map(\n", " FV.CT,\n", " ax=axs[0],\n", " edgecolor=\"white\",\n", " title=\"ct, no power mask\",\n", " cmap=\"YlGn\",\n", " vmin=0,\n", " vmax=1.0,\n", ")\n", "o1.plot_map(\n", " FV.MAX_P,\n", " ax=axs[1],\n", " edgecolor=\"white\",\n", " cmap=\"YlOrRd\",\n", " title=\"Power mask\",\n", " vmin=0,\n", " vmax=np.nanmax(pmask),\n", ")\n", "o1.plot_map(\n", " FV.CT,\n", " ax=axs[2],\n", " edgecolor=\"white\",\n", " cmap=\"YlGn\",\n", " title=\"ct, with power mask\",\n", " vmin=0,\n", " vmax=1.0,\n", ")\n", "plt.show()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "The above visualizations demonstrate that the power mask has effects on both the produced power and ct. Hence, also wakes are affected by derating and boosts.\n", "\n", "We can also visualize the effect of the `PowerMask` model on power and thrust curve, here for the case of derating from 5 MW to 3 MW:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, axs = plt.subplots(1, 2, figsize=(10, 4))\n", "o = foxes.output.TurbineTypeCurves(mbook)\n", "o.plot_curves(\"NREL5MW\", [FV.P, FV.CT], axs=axs, P_max=3000.)\n", "plt.show()" ] } ], "metadata": { "kernelspec": { "display_name": "foxescf", "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.8.10" }, "vscode": { "interpreter": { "hash": "a636808a35e91ab7fe187249f4bf0ca620f1546c7226807bc7f023195017eb5a" } } }, "nbformat": 4, "nbformat_minor": 2 }