{ "cells": [ { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "# Timeseries data" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "In this example we calculate the data of a wind farm with 67 turbines in a time series containing 8000 uniform inflow states.\n", "\n", "The required imports are:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%matplotlib inline\n", "import matplotlib.pyplot as plt\n", "\n", "import foxes\n", "import foxes.variables as FV\n", "import foxes.constants as FC\n", "from foxes.utils.runners import DaskRunner" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "First, we create the `model book`, making sure it contains the desired turbine type model:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# we are only using models that are provided by default, hence\n", "# no addition to the model book is required.\n", "mbook = foxes.ModelBook()\n", "\n", "# if you wish to add a model based on a specific file, do as follows:\n", "#mbook.turbine_types[\"NREL5\"] = foxes.models.turbine_types.PCtFile(\n", "# \"NREL-5MW-D126-H90.csv\"\n", "#)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Next, we create the `states`. The `data_source` can be any csv-type file (or `pandas` readable equivalent), or a `pandas.DataFrame` object. If it is a file path, then it will first be searched in the file system, and if not found, in the static data. If it is also not found there, an error showing the available static data file names is displayed.\n", "\n", "In this example the static data file `timeseries_8000.csv.gz` will be used, with content\n", "```\n", "Time,ws,wd,ti\n", "2017-01-01 00:00:00,15.62,244.06,0.0504\n", "2017-01-01 00:30:00,15.99,243.03,0.0514\n", "2017-01-01 01:00:00,16.31,243.01,0.0522\n", "2017-01-01 01:30:00,16.33,241.26,0.0523\n", "...\n", "```\n", "Notice the column names, and how they appear in the `Timeseries` constructor:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "states = foxes.input.states.Timeseries(\n", " data_source=\"timeseries_8000.csv.gz\",\n", " output_vars=[FV.WS, FV.WD, FV.TI, FV.RHO],\n", " var2col={FV.WS: \"ws\", FV.WD: \"wd\", FV.TI: \"ti\"},\n", " fixed_vars={FV.RHO: 1.225},\n", ")" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "We can visualize the wind distribution via the `StatesRosePlotOutput`. Here we display the ambient wind speed in a wind rose with 16 wind direction sectors and 5 wind speed bins: " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "o = foxes.output.StatesRosePlotOutput(states, point=[0., 0., 100.])\n", "fig = o.get_figure(16, FV.AMB_WS, [0, 3.5, 6, 10, 15, 20], figsize=(6, 6))\n", "plt.show()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "For the time series with uniform data, any choice of the `point` argument will produce the same figure.\n", "\n", "Next, we create the example wind farm with 67 turbines from static data. The file `test_farm_67.csv` has the following structure:\n", "```\n", "index,label,x,y\n", "0,T0,101872.70,1004753.57\n", "1,T1,103659.97,1002993.29\n", "2,T2,100780.09,1000779.97\n", "3,T3,100290.42,1004330.88\n", "...\n", "```\n", "For more options, check the API section `foxes.input.farm_layout`. \n", "\n", "We consider two turbine models in this example: the wind turbine type `NREL5MW` and the turbine model `kTI_02`, both from the default model book. The latter model adds the variable `k` for each state and turbine, calculated as `k = kTI * TI`, with constant `kTI = 0.2`. The parameter `k` will later be used by the wake model." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "farm = foxes.WindFarm()\n", "foxes.input.farm_layout.add_from_file(\n", " farm, \"test_farm_67.csv\", turbine_models=[\"kTI_02\", \"NREL5MW\"], verbosity=0\n", ")" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Next, we create the `algorithm`, with further model selections. In particular, two wake models are invoked, the model `Bastankhah_quadratic` for wind speed deficits and the model `CrespoHernandez_max` for turbulence intensity:" ] }, { "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_quadratic\", \"CrespoHernandez_max\"],\n", " wake_frame=\"rotor_wd\",\n", " partial_wakes_model=\"auto\",\n", " chunks={FC.STATE: 1000},\n", " verbosity=0,\n", ")" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Also notice the `chunks` parameter, specifying that always 1000 states should be considered in vectorized form during calculations. The progress is automatically visualized when invoking the `DaskRunner`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": false }, "outputs": [], "source": [ "with DaskRunner() as runner:\n", " farm_results = runner.run(algo.calc_farm)\n", "\n", "fr = farm_results.to_dataframe()\n", "print(\"\\n\", fr[[FV.WD, FV.AMB_REWS, FV.REWS, FV.AMB_P, FV.P]])" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Let's evaluate the results:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# add capacity and efficiency to farm_results:\n", "o = foxes.output.FarmResultsEval(farm_results)\n", "o.add_capacity(algo)\n", "o.add_capacity(algo, ambient=True)\n", "o.add_efficiency()\n", "\n", "# print results by turbine:\n", "turbine_results = o.reduce_states(\n", " {\n", " FV.AMB_P: \"mean\",\n", " FV.P: \"mean\",\n", " FV.AMB_CAP: \"mean\",\n", " FV.CAP: \"mean\",\n", " FV.EFF: \"mean\",\n", " }\n", ")\n", "turbine_results[FV.AMB_YLD] = o.calc_turbine_yield(algo=algo, annual=True, ambient=True)\n", "turbine_results[FV.YLD] = o.calc_turbine_yield(algo=algo, annual=True)\n", "print(\"\\nResults by turbine:\\n\")\n", "print(turbine_results)\n", "\n", "# print power 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\")\n", "print(f\"Farm ambient power: {P0/1000:.1f} MW\")\n", "print(f\"Farm efficiency : {o.calc_farm_efficiency():.2f}\")\n", "print(f\"Annual farm yield : {turbine_results[FV.YLD].sum():.2f} GWh\")" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "We can visualize the mean rotor equivalent wind speed as seen by each turbine and also the mean efficiency with respect to the time series data as colored layout plots:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, axs = plt.subplots(1,2,figsize=(14,5))\n", "o = foxes.output.FarmLayoutOutput(farm, farm_results)\n", "o.get_figure(fig=fig, ax=axs[0], color_by=\"mean_REWS\", title=\"Mean REWS [m/s]\", s=150, annotate=0)\n", "o.get_figure(fig=fig, ax=axs[1], color_by=\"mean_EFF\", title=\"Mean efficiency [%]\", s=150, annotate=0)\n", "plt.show()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "For the fun of it, we can also run this example in parallel, on a local cluster. Depending on the system and the problem size, this is not neccessarily faster than the above implicitely used default dask scheduler, and it comes with overhead. But for complex calculations it is extremely useful and can really save the day. Read the [docs](https://docs.dask.org/en/stable/deploying.html) for more details and parameters. The following invokes the default settings for the local cluster:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "with DaskRunner(scheduler=\"distributed\") as runner:\n", " farm_results = runner.run(algo.calc_farm)\n", "\n", "fr = farm_results.to_dataframe()\n", "print(\"\\n\", fr[[FV.WD, FV.AMB_REWS, FV.REWS, 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\"\\nMean farm power: {P/1000:.1f} MW, Efficiency = {P/P0*100:.2f} %\")" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Notice the `Dashboard` link, which is only valid during runtime and in this case is a `localhost` address. The dashboard gives plenty of information of the progress during the run and is a very useful tool provided by dask." ] } ], "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.10.9" }, "vscode": { "interpreter": { "hash": "49e77450ac3b792529b8ec53c4156102ea20ff425b704a19d2a6ff376cdbc220" } } }, "nbformat": 4, "nbformat_minor": 2 }