{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"# Wake Models\n",
"\n",
"A wake model in FLORIS is made up of four components that together constitute a wake.\n",
"At minimum, the velocity deficit profile behind a wind turbine is required. For most models,\n",
"an additional wake deflection model is included to model the effect of yaw misalignment.\n",
"Turbulence models are also available to couple with the deficit and deflection components.\n",
"Finally, methods for combining wakes with the rest of the flow field are available.\n",
"\n",
"Computationally, the solver algorithm and grid-type supported by each wake model can also\n",
"be considered as part of the model itself. As shown in the diagram below, the mathematical\n",
"formulations can be considered as the main components of the model. These are typically\n",
"associated directly to each other and in some cases they are bundled together into\n",
"a single mathematical formulation. The solver algorithm and grid type are associated\n",
"to the math formulation, but they are typically more generic.\n",
"\n",
"```{mermaid}\n",
"flowchart LR\n",
" A[\"Deficit\"]\n",
" B[\"Deflection\"]\n",
" C[\"Turbulence\"]\n",
" D[\"Velocity\"]\n",
" E[\"Solver\"]\n",
" F[\"Grid\"]\n",
"\n",
" subgraph H[FLORIS Wake Model]\n",
" direction LR\n",
" subgraph G[Math Model]\n",
" direction LR\n",
" A---B\n",
" B---C\n",
" C---D\n",
" end\n",
" G---E\n",
" E---F\n",
" end\n",
"```\n",
"\n",
"The models in FLORIS are typically developed as a combination of velocity deficit and wake\n",
"deflection models, and some also have custom turbulence and combination models. The descriptions\n",
"below use the typical combinations except where indicated. The specific settings can be seen\n",
"in the corresponding input files found in the source code dropdowns."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"from floris import FlorisModel\n",
"import floris.flow_visualization as flowviz\n",
"import floris.layout_visualization as layoutviz\n",
"\n",
"NREL5MW_D = 126.0\n",
"\n",
"def model_plot(inputfile, include_wake_deflection=True):\n",
" fig, axes = plt.subplots(1, 1, figsize=(10, 10))\n",
" yaw_angles = np.zeros((1, 2))\n",
" if include_wake_deflection:\n",
" yaw_angles[:,0] = 20.0\n",
" fmodel = FlorisModel(inputfile)\n",
" fmodel.set(\n",
" layout_x=np.array([0.0, 2*NREL5MW_D]),\n",
" layout_y=np.array([0.0, 2*NREL5MW_D]),\n",
" yaw_angles=yaw_angles,\n",
" )\n",
" horizontal_plane = fmodel.calculate_horizontal_plane(height=90.0)\n",
" flowviz.visualize_cut_plane(horizontal_plane, ax=axes, clevels=100)\n",
" layoutviz.plot_turbine_rotors(fmodel, ax=axes, yaw_angles=yaw_angles)"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## Jensen and Jimenez\n",
"\n",
"The Jensen model computes the wake velocity deficit based on the classic Jensen/Park model\n",
"{cite:t}`jensen1983note`. It is often refered to as a \"top-hat\" model because the spanwise\n",
"velocity profile is constant across the wake and abruptly jumps to freestream outside of the\n",
"wake boundary line. The slope of the wake boundary line, or wake expansion, is a user parameter.\n",
"\n",
"The Jiménez wake deflection model is derived from {cite:t}`jimenez2010application`."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"model_plot(\"../examples/inputs/jensen.yaml\")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## Gauss and GCH\n",
"\n",
"The Gaussian velocity model is implemented based on {cite:t}`bastankhah2016experimental` and\n",
"{cite:t}`niayifar2016analytical`. This model represents the velocity deficity as a gaussian\n",
"distribution in the spanwise direction, and the gaussian profile is controlled by user parameters.\n",
"There is a near wake zone and a far wake zone. Both maintain the gaussian profile in the spanwise\n",
"direction, but they have different models for wake recovery.\n",
"\n",
"The Gauss deflection model is a blend of the models described in\n",
"{cite:t}`bastankhah2016experimental` and {cite:t}`King2019Controls` for calculating\n",
"the deflection field in turbine wakes."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"model_plot(\"../examples/inputs/gch.yaml\")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## Empirical Gaussian\n",
"\n",
"FLORIS's \"empirical\" model has the same Gaussian wake shape as other popular FLORIS models.\n",
"However, the models that describe the wake width and deflection have been reorganized to provide\n",
"simpler tuning and data fitting.\n",
"\n",
"For more information, see {ref}`empirical_gauss_model`"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"model_plot(\"../examples/inputs/emgauss.yaml\")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## Cumulative Curl\n",
"The cumulative curl model is an implementation of the model described in {cite:t}`bay_2022`,\n",
"which itself is based on the cumulative model of {cite:t}`bastankhah_2021`"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"model_plot(\"../examples/inputs/cc.yaml\")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## TurbOPark\n",
"\n",
"The TurbOPark model is designed to model long wakes from large wind farm clusters. It was originally presented as a “top-hat” model in {cite:t}`nygaard2020modelling` and was updated in {cite:t}`Pedersen_2022_turbopark2` to have a Gaussian profile. For the latter, Ørsted released the [Matlab code with documentation](https://github.com/OrstedRD/TurbOPark), which allows the verification of the implementation in FLORIS.\n",
"\n",
"The first implementation, the [`TurboparkVelocityDeficitModel`](https://github.com/NREL/floris/blob/main/floris/core/wake_velocity/turbopark.py), was released in [FLORIS v3.1](https://github.com/NREL/floris/releases/tag/v3.1). The second implementation, the [`TurboparkgaussVelocityDeficitModel`](https://github.com/NREL/floris/blob/main/floris/core/wake_velocity/turboparkgauss.py), was released in FLORIS v4.2 and shows a near-perfect match to the predictions of Ørsted’s Matlab implementation. As such, we will emphasize the use of the `TurboparkgaussVelocityDeficitModel` going forward, and suggest that new users use this model (by setting the `velocity_model` field of the FLORIS input file to `turboparkgauss` instead of the `TurboparkVelocityDeficitModel` (`velocity_model: turbopark`)) if they are interested in testing the TurbOPark model.\n",
"\n",
"The `TurboparkgaussVelocityDeficitModel` implementation was contributed by [Jasper Kreeft](https://github.com/JasperShell).\n",
"\n",
"Note that the original top-hat TurbOPark model ({cite:t}`nygaard2020modelling`) is _not_ available in FLORIS.\n",
"\n",
"The wakes as predicted by the `TurboparkgaussVelocityDeficit` model are demonstrated below."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"model_plot(\"../examples/inputs/turboparkgauss_cubature.yaml\", include_wake_deflection=False)"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## Turbulence Models\n",
"\n",
"### Crespo-Hernandez\n",
"\n",
"CrespoHernandez is a wake-turbulence model that is used to compute additional variability introduced\n",
"to the flow field by operation of a wind turbine. Implementation of the model follows the original\n",
"formulation and limitations outlined in {cite:t}`crespo1996turbulence`."
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## Wake Combination Models\n",
"\n",
"The wakes throughout the flow field need to be combined in a careful manner in order to\n",
"accurately capture their coupled effects. A simple model is to simple add them,\n",
"but this can result in negative velocities a few turbines into the farm. More careful\n",
"methods are available within FLORIS and shown here.\n",
"\n",
"Each model is described below and its effects are plotted with two turbines in a line.\n",
"These descriptions use the Jensen and Jimenez models since they highlight the differences\n",
"in the combination models themselves.\n",
"The upper plots show the turbine wakes individually to give a reference for the uncombined wake.\n",
"The lower plots show both turbines along with their wakes combined with the chosen model."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"def combination_plot(method: str):\n",
" X_UPSTREAM = 0.0\n",
" X_DOWNSTREAM = 5 * 126.0\n",
" X0_BOUND = -200\n",
" X1_BOUND = 1500\n",
"\n",
" # Set the combination method\n",
" fmodel = FlorisModel(\"../examples/inputs/jensen.yaml\")\n",
" settings = fmodel.core.as_dict()\n",
" settings[\"wake\"][\"model_strings\"][\"combination_model\"] = method\n",
" fmodel = FlorisModel(settings)\n",
"\n",
" # Plot two turbines individually\n",
" fig, axes = plt.subplots(1, 2, figsize=(10, 10))\n",
" fmodel.set(\n",
" layout_x=np.array([X_UPSTREAM]),\n",
" layout_y=np.zeros(1),\n",
" yaw_angles=np.array([[20.0]]),\n",
" )\n",
" horizontal_plane = fmodel.calculate_horizontal_plane(\n",
" height=90.0,\n",
" x_bounds=(X0_BOUND, X1_BOUND),\n",
" )\n",
" layoutviz.plot_turbine_rotors(fmodel, ax=axes[0])\n",
" flowviz.visualize_cut_plane(horizontal_plane, ax=axes[0], clevels=100)\n",
" layoutviz.plot_turbine_rotors(fmodel, ax=axes[1])\n",
"\n",
" fmodel.set(\n",
" layout_x=np.array([X_DOWNSTREAM]),\n",
" layout_y=np.zeros(1),\n",
" yaw_angles=np.array([[0.0]]),\n",
" )\n",
" horizontal_plane = fmodel.calculate_horizontal_plane(\n",
" height=90.0,\n",
" x_bounds=(X0_BOUND, X1_BOUND),\n",
" )\n",
" flowviz.visualize_cut_plane(horizontal_plane, ax=axes[1], clevels=100)\n",
" layoutviz.plot_turbine_rotors(fmodel, ax=axes[0])\n",
" layoutviz.plot_turbine_rotors(fmodel, ax=axes[1])\n",
"\n",
" # Plot the combination of turbines\n",
" fig, axes = plt.subplots(1, 1, figsize=(10, 10))\n",
" fmodel.set(\n",
" layout_x=np.array([X_UPSTREAM, X_DOWNSTREAM]),\n",
" layout_y=np.zeros(2),\n",
" yaw_angles=np.array([[20.0, 0.0]]),\n",
" )\n",
" horizontal_plane = fmodel.calculate_horizontal_plane(\n",
" height=90.0,\n",
" x_bounds=(X0_BOUND, X1_BOUND),\n",
" )\n",
" flowviz.visualize_cut_plane(horizontal_plane, ax=axes, clevels=100)\n",
" layoutviz.plot_turbine_rotors(fmodel, ax=axes)"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Freestream Linear Superposition (FLS)\n",
"\n",
"FLS uses freestream linear superposition to apply the wake velocity deficits to the freestream\n",
"flow field."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"combination_plot(\"fls\")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Max\n",
"\n",
"The MAX model incorporates the velocity deficits into the base flow field by selecting the\n",
"maximum of the two for each point. For more information, refer to {cite:t}`gunn2016limitations`."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"combination_plot(\"max\")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Sum of Squares Freestream Superposition (SOSFS)\n",
"\n",
"This model combines the wakes via a sum of squares of the new wake to add and the existing\n",
"flow field."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"combination_plot(\"sosfs\")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"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.6"
},
"orig_nbformat": 4
},
"nbformat": 4,
"nbformat_minor": 2
}