{ "cells": [ { "cell_type": "markdown", "id": "63627cb8", "metadata": {}, "source": [ "# Engineering Wind Farm Models Object" ] }, { "cell_type": "markdown", "id": "ae118221", "metadata": {}, "source": [ "PyWake contains three general engineering wind farm models, namely [PropagateDownwind](#PropagateDownwind), [All2AllIerative](#All2AllIterative) and [PropagateUpDownIteative](#PropagateUpDownIterative). \n", "\n", "The table below compares their properties:\n", "\n", "/ | `All2AllIterative` | `PropagateDownwind` | `PropagateUpDownIteartive` |\n", "-- | -- | -- | --|\n", "Includes wakes | Yes | Yes | Yes\n", "Includes blockage | Yes | No | Yes\n", "Memory requirement | High | Low | Low |\n", "Simulation speed | Slow | Fast | Medium\n", "\n", "\n", "In addition different [pre-defined models}(#Predefined-Wind-Farm-Models) exits. The predefined models covers often-used model combinations and models from the literature. I.e. they instantiates one of the above-mentioned wind farm models with a combination of wake deficit models, superposition models, turbulence models, etc. However, these are easily customizable to study the impact of different models on the results." ] }, { "cell_type": "markdown", "id": "aeefd655", "metadata": {}, "source": [ "### PropagateDownwind\n", "\n", "The `PropagateDownwind` wind farm model is very fast as it only performs a minimum of deficit calculations. It iterates over all turbines in downstream order. In each iteration it calculates the effective wind speed at the current wind turbine as the free stream wind speed minus the sum of the deficit from upstream sources. Based on this effective wind speed, it calculates the deficit caused by the current turbine on all downstream destinations. Note, that this procedure neglects upstream blockage effects.\n", "\n", "```python\n", "for wt in wind turbines in downstream order:\n", " ws_eff[wt] = ws[wt] - superposition(deficit[from_upstream_src,to_wt])\n", " ct = windTurbines.ct(ws_eff[wt])\n", " deficit[from_wt,to_downstream_dst] = wakeDeficitModel(ct, distances[from_wt,to_downstream_dst], ...)\n", "```\n", "\n", "The proceedure is illustrated in the animation below:\n", "\n", "- **Iteration 1:** WT0 sees the free wind (10m/s). Its deficit on WT1 and WT2 is calculated.\n", "- **Iteration 2:** WT1 sees the free wind minus the deficit from WT0. Its deficit on WT2 is calculated and the effective wind speed at WT2 is updated." ] }, { "cell_type": "markdown", "id": "7a3718b4", "metadata": {}, "source": [ "![PropagateDownwind](../_static/PropagateDownwind.png)" ] }, { "cell_type": "markdown", "id": "42e4e3ef-d4ec-476c-bcf2-7d9588371436", "metadata": {}, "source": [ "In PyWake, the class is represented as follows:" ] }, { "cell_type": "code", "execution_count": null, "id": "f760da11", "metadata": {}, "outputs": [], "source": [ "# Install PyWake if needed\n", "try:\n", " import py_wake\n", "except ModuleNotFoundError:\n", " !pip install git+https://gitlab.windenergy.dtu.dk/TOPFARM/PyWake.git" ] }, { "cell_type": "code", "execution_count": 6, "id": "6e266a6f-7b75-4352-a385-86b998904239", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Help on function __init__ in module py_wake.wind_farm_models.engineering_models:\n", "\n", "__init__(self, site, windTurbines, wake_deficitModel, superpositionModel=, deflectionModel=None, turbulenceModel=None, rotorAvgModel=None, inputModifierModels=[])\n", " Initialize flow model\n", " \n", " Parameters\n", " ----------\n", " site : Site\n", " Site object\n", " windTurbines : WindTurbines\n", " WindTurbines object representing the wake generating wind turbines\n", " wake_deficitModel : DeficitModel\n", " Model describing the wake(downstream) deficit\n", " rotorAvgModel : RotorAvgModel, optional\n", " Model defining one or more points at the down stream rotors to\n", " calculate the rotor average wind speeds from.\n", " \n", " if None, default, the wind speed at the rotor center is used\n", " superpositionModel : SuperpositionModel\n", " Model defining how deficits sum up\n", " deflectionModel : DeflectionModel\n", " Model describing the deflection of the wake due to yaw misalignment, sheared inflow, etc.\n", " turbulenceModel : TurbulenceModel\n", " Model describing the amount of added turbulence in the wake\n", "\n" ] } ], "source": [ "from py_wake.wind_farm_models import PropagateDownwind\n", "help(PropagateDownwind.__init__)" ] }, { "cell_type": "markdown", "id": "c40d685f", "metadata": {}, "source": [ "`site` and `windTurbines` and `wake_deficitModel` are required inputs. By default, the class uses the `LinearSum` superposition model to add the wakes from upstream turbines." ] }, { "cell_type": "markdown", "id": "5591a990", "metadata": {}, "source": [ "### All2AllIterative\n", "\n", "The `All2AllIterative` wind farm model is slower but is capable of handling blockage effects. It iterates until the effective wind speed converges or it reaches the max number of iterations (number of turbines). The converge tolerance is an input parameter. In each iteration it sums up the deficit from all wind turbine sources and calculates the deficit caused by the all wind turbines turbine on all wind turbines.\n", "\n", "```python\n", "while ws_eff not converged:\n", " ws_eff[all] = ws[all] - superposition(deficit[from_all,to_all])\n", " ct[all] = windTurbines.ct(ws_eff[all])\n", " deficit[from_all,to_all] = wakeDeficitModel(ct[all], distances[from_all,to_all], ...)\n", "```\n", "\n", "The proceedure is illustrated in the animation below:\n", "\n", "- **Iteration 1:** All three WT see the free wind (10m/s) and their CT values and resulting deficits are therefore equal.\n", "- **Iteration 2:** The local effective wind speeds are updated taking into account the wake and blockage effects of the other WT. Based on these wind speeds the CT and deficits are recalculated.\n", "- **Iteration 3:** Repeat after which the flow field has converged." ] }, { "cell_type": "markdown", "id": "807ff4ee", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "id": "fd666385-fd52-405b-9131-be7d6af0068a", "metadata": {}, "source": [ "In PyWake, the class is represented as follows:" ] }, { "cell_type": "code", "execution_count": 8, "id": "84d96c36-c2b7-443a-ac5e-1c5fd608f655", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Help on function __init__ in module py_wake.wind_farm_models.engineering_models:\n", "\n", "__init__(self, site, windTurbines, wake_deficitModel, superpositionModel=, blockage_deficitModel=None, deflectionModel=None, turbulenceModel=None, convergence_tolerance=1e-06, rotorAvgModel=None, inputModifierModels=[])\n", " Initialize flow model\n", " \n", " Parameters\n", " ----------\n", " site : Site\n", " Site object\n", " windTurbines : WindTurbines\n", " WindTurbines object representing the wake generating wind turbines\n", " wake_deficitModel : DeficitModel\n", " Model describing the wake(downstream) deficit\n", " rotorAvgModel : RotorAvgModel, optional\n", " Model defining one or more points at the down stream rotors to\n", " calculate the rotor average wind speeds from.\n", " \n", " if None, default, the wind speed at the rotor center is used\n", " superpositionModel : SuperpositionModel\n", " Model defining how deficits sum up\n", " blockage_deficitModel : DeficitModel\n", " Model describing the blockage(upstream) deficit\n", " deflectionModel : DeflectionModel\n", " Model describing the deflection of the wake due to yaw misalignment, sheared inflow, etc.\n", " turbulenceModel : TurbulenceModel\n", " Model describing the amount of added turbulence in the wake\n", " convergence_tolerance : float or None\n", " if float: maximum accepted change in WS_eff_ilk [m/s]\n", " if None: return after first iteration. This only makes sense for benchmark studies where CT,\n", " wakes and blockage are independent of effective wind speed WS_eff_ilk\n", "\n" ] } ], "source": [ "from py_wake.wind_farm_models import All2AllIterative\n", "help(All2AllIterative.__init__)" ] }, { "cell_type": "markdown", "id": "ef902e01", "metadata": {}, "source": [ "In addition to the parameters specified in the `PropagateDownwind` class, here we determine a convergence tolerance in terms of the maximum accepted change in `WS_eff_ilk` in m/s. In this case, the blockage deficit model is set to `None` as default, but this should be changed depending on the engineering wind farm model used." ] }, { "cell_type": "markdown", "id": "cc2fdb9c-d6b6-464d-a1c9-7aca7880419d", "metadata": {}, "source": [ "As default, the `All2AllIterative` simulation runs a `PropagateDownwind` simulation during initialization and uses the resulting effective wind speed as starting condition for its own iterative process." ] }, { "cell_type": "markdown", "id": "15453ded-9c0c-4583-8909-2b15c8b43eb2", "metadata": {}, "source": [ "### PropagateUpDownIterative\n", "The `PropagateUpDownIterative` wind farm model combines the approaches of `PropagateDownwind` and `All2AllIterative`" ] }, { "cell_type": "markdown", "id": "8a75a25e-1858-4377-8b96-9e91dbb11375", "metadata": {}, "source": [ "- **Iteration 1 (Propagate wake down):**\n", " - **Iteration 1.1:** WT0 sees the free wind (10m/s). Its wake deficit on WT1 and WT2 is calculated.\n", " - **Iteration 1.2:** WT1 sees the free wind minus the wake deficit from WT0. Its wake deficit on WT2 is calculated and the effective wind speed at WT2 is updated." ] }, { "cell_type": "markdown", "id": "f8a84095-febb-4f4c-aa2f-0711dadfaf60", "metadata": {}, "source": [ "![PropagateUpDownIterative1](../_static/PropagateUpDownIterative1.png)" ] }, { "cell_type": "markdown", "id": "728dcbca-4852-462b-9e5c-fc77aa008ffb", "metadata": {}, "source": [ "- **Iteration 2 (Propagate blockage up)**\n", " - **Iteration 2.1:** All wind turbines see the free wind speed minus the wake deficit obtained in iteration 1. WT2 sees 6.73m/s and its blockage deficit on WT1 and WT0 is calculated.\n", " - **Iteration 2.2:** WT1 sees the free wind speed minus the wake deficit obtained in iteration 1 and the blockage deficit from WT2. Its blockage deficit on WT0 is calculated and the effective wind speed at WT0 is updated." ] }, { "cell_type": "markdown", "id": "7ae869c3-1f98-4cde-8677-9b14f599ed12", "metadata": {}, "source": [ "![PropagateUpDownIterative2](../_static/PropagateUpDownIterative2.png)" ] }, { "cell_type": "markdown", "id": "9cd3634a-6d33-4ec4-874f-699eb8216664", "metadata": {}, "source": [ "- **Iteration 3 (Propagate wake down):**\n", " - **Iteration 3.1:** All wind turbines see the free wind minus the blockage obtained in iteration 2. WT0 seees 9.94 m/s and its wake deficit on WT1 and WT2 is calculated.\n", " - **Iteration 3.2:** WT1 sees the free wind minus the blockage deficit obtained in iteration 2 and the wake deficit from WT0. Its wake deficit on WT2 is calculated and the effective wind speed at WT2 is updated." ] }, { "cell_type": "markdown", "id": "f9731953-ee5b-423d-b41a-0f288825b650", "metadata": {}, "source": [ "![PropagateUpDownIterative3](../_static/PropagateUpDownIterative3.png)" ] }, { "cell_type": "markdown", "id": "cf402c8b-b9fb-48a4-b39e-94101a30d0ac", "metadata": {}, "source": [ "- **Iteration 4 (Propagate blockage up)**\n", " - **Iteration 4.1:** All wind turbines see the free wind speed minus the wake deficit obtained in iteration 3. WT2 sees 6.74m/s and its blockage deficit on WT1 and WT0 is calculated.\n", " - **Iteration 4.2:** WT1 sees the free wind speed minus the wake deficit obtained in iteration 3 and the blockage deficit from WT2. Its blockage deficit on WT0 is calculated and the effective wind speed at WT0 is updated." ] }, { "cell_type": "markdown", "id": "36ad7852-e9ac-40cc-b827-6f3cdea5091a", "metadata": {}, "source": [ "![PropagateUpDownIterative4](../_static/PropagateUpDownIterative4.png)" ] }, { "cell_type": "markdown", "id": "e05c0ff0-f7e2-4142-b955-6f4c0d623e80", "metadata": {}, "source": [ "The constructor of `PropagateUpDownIterative` is very similar to the constructor of `All2AllIterative`:" ] }, { "cell_type": "code", "execution_count": 14, "id": "ac9a58f3-19e7-4c1f-ba3d-b3435a86f696", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Help on function __init__ in module py_wake.wind_farm_models.engineering_models:\n", "\n", "__init__(self, site, windTurbines, wake_deficitModel, superpositionModel=, blockage_deficitModel=None, deflectionModel=None, turbulenceModel=None, rotorAvgModel=None, inputModifierModels=[], convergence_tolerance=1e-06)\n", " Initialize flow model\n", " \n", " Parameters\n", " ----------\n", " site : Site\n", " Site object\n", " windTurbines : WindTurbines\n", " WindTurbines object representing the wake generating wind turbines\n", " wake_deficitModel : DeficitModel\n", " Model describing the wake(downstream) deficit\n", " rotorAvgModel : RotorAvgModel, optional\n", " Model defining one or more points at the down stream rotors to\n", " calculate the rotor average wind speeds from.\n", " \n", " if None, default, the wind speed at the rotor center is used\n", " superpositionModel : SuperpositionModel\n", " Model defining how deficits sum up\n", " deflectionModel : DeflectionModel\n", " Model describing the deflection of the wake due to yaw misalignment, sheared inflow, etc.\n", " turbulenceModel : TurbulenceModel\n", " Model describing the amount of added turbulence in the wake\n", "\n" ] } ], "source": [ "from py_wake.wind_farm_models import PropagateUpDownIterative\n", "help(PropagateUpDownIterative.__init__)" ] }, { "cell_type": "markdown", "id": "37b841ce-f7c2-43e6-b84a-1cd07f489007", "metadata": {}, "source": [] }, { "cell_type": "markdown", "id": "37c5c42d-bb25-4ec3-8037-6c1374d52eff", "metadata": {}, "source": [ "### Predefined Wind Farm Models\n", "\n", "The pre-defines wind farm models are adapted from the literature, where their corresponding default superposition model, turbulence model and calibration parameters are taken from the paper describing each model.\n", "\n", "**The engineering wind farm models comprise:**\n", "\n", "| Reference | Name | WindFarm Model | Wake Deficit Model | Blockage Deficit Model | Superposition Model | Turbulence Model |\n", "| :--- | :--- | :--- | :--- | :--- | :--- | :--- |\n", "| [1](https://gitlab.windenergy.dtu.dk/TOPFARM/PyWake/-/blob/master/py_wake/literature/noj.py) | Jensen_1983 | [PropagateDownwind](#PropagateDownwind) | [NOJDeficit](WakeDeficitModels.ipynb#NOJDeficit) | - | [SquaredSum](SuperpositionModels.ipynb#SquaredSum) | - |\n", "| [2](https://gitlab.windenergy.dtu.dk/TOPFARM/PyWake/-/blob/master/py_wake/literature/fuga.py) | Ott_Nielsen_2014 | [PropagateDownwind](#PropagateDownwind) | [FugaDeficit](WakeDeficitModels.ipynb#FugaDeficit) | - | [LinearSum](SuperpositionModels.ipynb#LinearSum) | - |\n", " [3](https://gitlab.windenergy.dtu.dk/TOPFARM/PyWake/-/blob/master/py_wake/literature/fuga.py) | Ott_Nielsen_2014_Blockage | [All2AllIterative](#All2AllIterative) | [FugaDeficit](WakeDeficitModels.ipynb#FugaDeficit) | [FugaDeficit](WakeDeficitModels.ipynb#FugaDeficit) | [LinearSum](SuperpositionModels.ipynb#LinearSum) | - |\n", " [4](https://gitlab.windenergy.dtu.dk/TOPFARM/PyWake/-/blob/master/py_wake/literature/gaussian_models.py) | Bastankhah_PorteAgel_2014 | [PropagateDownwind](#PropagateDownwind) | [BastankhahGaussianDeficit](WakeDeficitModels.ipynb#BastankhahGaussianDeficit) | - | [SquaredSum](SuperpositionModels.ipynb#SquaredSum) | - |\n", " [5](https://gitlab.windenergy.dtu.dk/TOPFARM/PyWake/-/blob/master/py_wake/literature/iea37_case_study1.py) | IEA37CaseStudy1 | [PropagateDownwind](#PropagateDownwind) | [IEA37SimpleBastankhahGaussianDeficit](WakeDeficitModels.ipynb#IEA37SimpleBastankhahGaussianDeficit) | - | [SquaredSum](SuperpositionModels.ipynb#SquaredSum) | - |\n", " [6](https://gitlab.windenergy.dtu.dk/TOPFARM/PyWake/-/blob/master/py_wake/literature/gaussian_models.py) | Niayifar_PorteAgel_2016 | [PropagateDownwind](#PropagateDownwind) | [NiayifarGaussianDeficit](WakeDeficitModels.ipynb#NiayifarGaussianDeficit) | - | [LinearSum](SuperpositionModels.ipynb#LinearSum) | [CrespoHernandez](TurbulenceModels.ipynb#CrespoHernandez) |\n", " [7](https://gitlab.windenergy.dtu.dk/TOPFARM/PyWake/-/blob/master/py_wake/literature/gaussian_models.py) | Zong_PorteAgel_2020 | [PropagateDownwind](#PropagateDownwind) | [ZongGaussianDeficit](WakeDeficitModels.ipynb#ZongGaussianDeficit) | - | [WeightedSum](SuperpositionModels.ipynb#WeightedSum) | [CrespoHernandez](TurbulenceModels.ipynb#CrespoHernandez) |\n", " [8](https://gitlab.windenergy.dtu.dk/TOPFARM/PyWake/-/blob/master/py_wake/literature/turbopark.py) | Nygaard_2022 | [PropagateDownwind](#PropagateDownwind) | [TurboGaussianDeficit](WakeDeficitModels.ipynb#TurboGaussianDeficit) | - | [SquaredSum](SuperpositionModels.ipynb#SquaredSum) | - |\n", " [9](https://gitlab.windenergy.dtu.dk/TOPFARM/PyWake/-/blob/master/py_wake/literature/gaussian_models.py) | Blondel_Cathelain_2020 | [PropagateDownwind](#PropagateDownwind) | [BlondelSuperGaussianDeficit2020](WakeDeficitModels.ipynb#SuperGaussianDeficit) | - | [LinearSum](SuperpositionModels.ipynb#LinearSum) | - |\n", "\n", "- Default rotor-average model: `RotorCenter`\n", "- Default turbulence model: `None`" ] }, { "cell_type": "markdown", "id": "0738b80f", "metadata": {}, "source": [ "All models require a `Site` and a `WindTurbine` object to run, and each of them may require additional inputs such as calibration parameters. The parameters shown are the default and used in the literature, but if necessary, they can be modified to better suit a particular case study.\n", "\n", "In the case of Fuga, a file with look up tables is needed; the file is created by specifying the turbine's rotor diameter, hub height and roughness length of the site to study.\n", "\n", "For information on the calculation of the velocity deficit for each model, please refer to the [Wake Deficit Models](WakeDeficitModels.ipynb) notebook." ] }, { "cell_type": "markdown", "id": "c015a900", "metadata": {}, "source": [ "Below are some examples of different wind farm models under the `PropagateDownwind` base class. The full set of pre-defined wind farm models can be found in PyWake's repository under the [literature](https://gitlab.windenergy.dtu.dk/TOPFARM/PyWake/-/tree/master/py_wake/literature) folder." ] }, { "cell_type": "markdown", "id": "04cafce5", "metadata": {}, "source": [ "**1) Bastankhah Gaussian**\n", " \n", "```python\n", "class Bastankhah_PorteAgel_2014(PropagateDownwind):\n", "def __init__(self, site, windTurbines, k=0.0324555, ceps=.2, ct2a=ct2a_madsen, use_effective_ws=False,\n", " rotorAvgModel=None, superpositionModel=SquaredSum(),\n", " deflectionModel=None, turbulenceModel=None, groundModel=None):\n", "```\n", "\n", "**2) Niayifar Gaussian**\n", "\n", "```python\n", "class Niayifar_PorteAgel_2016(PropagateDownwind):\n", "def __init__(self, site, windTurbines, a=[0.38, 4e-3], ceps=.2, superpositionModel=LinearSum(),\n", " deflectionModel=None, turbulenceModel=CrespoHernandez(), rotorAvgModel=None, groundModel=None,\n", " use_effective_ws=True, use_effective_ti=True):\n", "```\n", "\n", "**3) Zong Gaussian**\n", "\n", "```python\n", "class Zong_PorteAgel_2020(PropagateDownwind):\n", "def __init__(self, site, windTurbines, a=[0.38, 4e-3], deltawD=1. / np.sqrt(2), lam=7.5, B=3,\n", " rotorAvgModel=None, superpositionModel=WeightedSum(), deflectionModel=None,\n", " turbulenceModel=CrespoHernandez(), groundModel=None, use_effective_ws=True, use_effective_ti=True):\n", "```\n", "\n", "**3) TurbOPark**\n", "\n", "```python\n", "class Nygaard_2022(PropagateDownwind):\n", "def __init__(self, site, windTurbines):\n", "\n", " wake_deficitModel = TurboGaussianDeficit(\n", " ct2a=ct2a_mom1d,\n", " groundModel=Mirror(),\n", " rotorAvgModel=GaussianOverlapAvgModel())\n", "```\n", "\n", "**4) Fuga**\n", "```python\n", "class Ott_Nielsen_2014(PropagateDownwind):\n", " def __init__(self, LUT_path, site, windTurbines,\n", " rotorAvgModel=None, deflectionModel=None, turbulenceModel=None, remove_wriggles=False):\n", "```" ] }, { "cell_type": "markdown", "id": "39d52732", "metadata": {}, "source": [ "In addition, Fuga's wind farm model comes with the possibility of modeling blockage. For this, the `All2AllIterative` base class is used.\n", "\n", "```python\n", "class Ott_Nielsen_2014_Blockage(All2AllIterative):\n", " def __init__(self, LUT_path, site, windTurbines, rotorAvgModel=None,\n", " deflectionModel=None, turbulenceModel=None, convergence_tolerance=1e-6, remove_wriggles=False):\n", "```" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "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.11.7" } }, "nbformat": 4, "nbformat_minor": 5 }