A bit of imports to render the notebook
In [1]:
%load_ext autoreload
%autoreload 2
import numpy as np
from IPython.display import HTML, Latex, Markdown, Pretty
We will plot with the Plot.ly library
In [2]:
from plotly import __version__
from plotly.offline import download_plotlyjs, init_notebook_mode, iplot
from plotly.graph_objs import *
print( __version__) # requires version >= 1.9.0
init_notebook_mode() # run at the start of every ipython notebook to use plotly.offline
# this injects the plotly.js source files into the notebook
WindIO is a package that can read .yml files describing the plants and turbines. It's hopefully going to be integrated in FUSED-Wind in a close future.
In [3]:
from windIO.Plant import WTLayout
We have 3 offshore plants to play with. You can change the plant by running the corresponding cell before running the rest of the notebook.
In [4]:
filename = 'middelgrunden.yml'
In [5]:
filename = 'lillgrund.yml'
In [6]:
filename = 'hornsrev.yml'
In [7]:
wtl = WTLayout(filename)
The WindIO YML format is a human readable file format. It is meant to be easy to edit as a collaborative document. We plan to also include standards to add external data files for the mode data-heavy I/O needs.
In [8]:
!cat $filename|head
In [9]:
wtl.plot_location()
In [10]:
wtl.plot_layout()
In [11]:
wtl.WT01
Out[11]:
In [12]:
from fusedwake.WindFarm import WindFarm
In [13]:
wf = WindFarm(yml=filename)
The WindFarm object representation returns in Jupyter notebooks a little summary of what the wind farm is about
In [14]:
iplot({'data':[{'x':wf.WT.pc[:,0],
'y':wf.WT.pc[:,1]}],
'layout':{'xaxis':{'title':'Wind Speed [m/s]'},
'yaxis':{'title': 'Power [kW]'},
'title':'The {} Power Curve'.format(wf.WT.turbine_type)}})
Right now the WindIO WTLayout object is monkey-patched inside the WindFarm object. We might integrate it more tightly inside the WindFarm class later on.
The GCL model
In [15]:
from fusedwake.gcl.interface import GCL
gcl = GCL(WF=wf, TI=0.1)
The NOJ model
In [16]:
from fusedwake.noj.interface import NOJ
noj = NOJ(WF=wf, kj=0.04)
The Gaussian shape wake model from Bastankhah & Porté-Agel$^*$
$^*$ Bastankhah, M., & Porté-Agel, F. (2014). **A new analytical model for wind-turbine wakes**. *Renewable Energy, 70, 116-123*.
In [17]:
from fusedwake.gau.interface import GAU
gau = GAU(WF=wf, ks=0.04)
The GCL class is wrapper class for different python and fortran functions. When the __init__ function is called, you can pass as parameters all the inputs you want. Typically that would be the inputs you are not planning to change afterwards.
In [18]:
# Inputs
WS=10.0 # m/s
WD=270 # deg
version = 'fort_gcl' #
# Run the models
gcl(WS=WS, WD=WD, version='fort_gcl')
noj(WS=WS, WD=WD, version='fort_noj')
gau(WS=WS, WD=WD, version='fort_gau_s')
Out[18]:
In [19]:
WS_cases=np.arange(4,6)
WD_cases=np.arange(0,360,20)
WS_ms,WD_ms=np.meshgrid(WS_cases,WD_cases)
WS=WS_ms.flatten()
WD=WD_ms.flatten()
TI=0.1*np.ones_like(WD)
In [20]:
%%timeit
gcl(WF=wf, WS=WS, WD=WD, TI=TI, version='fort_gcl')
In [21]:
WS_ms,WD_ms=np.meshgrid(WS_cases,WD_cases)
WS=WS_ms.reshape(-1,1)*np.ones([1,wf.nWT])
WS=WS+np.random.normal(0,0.5,size=WS.shape)
WD=WD_ms.reshape(-1,1)*np.ones([1,wf.nWT])
WD=WD+np.random.normal(0,3,size=WS.shape)
TI=0.1*np.ones_like(WD)
In [22]:
%%timeit
gcl(WF=wf, WS=WS, WD=WD, TI=TI, version='fort_gclm')
In [23]:
noj(WS=WS, WD=WD, version='fort_noj')
Out[23]:
The models can be run by calling the instance directly. It returns itself so you can also call it like that to calculate the total power produced by the plant:
In [22]:
gcl(WS=WS, WD=WD).p_wt.sum()
Out[22]:
In [23]:
noj(WS=WS, WD=WD).p_wt.sum()
Out[23]:
In [24]:
gau(WS=WS, WD=WD).p_wt.sum()
Out[24]:
Once it has been run, all the inputs and outputs of the model are stored as object variable in the gcl instance.
In [25]:
iplot({'data':[{'x': [wt.name for wt in wf.WT],
'y': noj(version=v).p_wt, 'type': 'scatter',
'name':v} for v in noj.versions ] +
[{'x': [wt.name for wt in wf.WT],
'y': gcl(WS=WS,WD=WD,TI=0.1,version=v).p_wt, 'type': 'scatter',
'name':v} for v in gcl.versions] +
[{'x': [wt.name for wt in wf.WT],
'y': gau(version=v).p_wt, 'type': 'scatter',
'name':v} for v in gau.versions],
'layout':{'xaxis':{'title':'Turbine Name'},
'yaxis':{'title':'Power [W]'},
'title': '%s Power at WD=%2.1f deg and WS=%2.1f m/s'%(wf.name, WD, WS)}})
Latex("""The total production of %s in this flow case is:
$P_{GCL}(u=%2.1f, \\theta=%3.1f)=%8.2f$ MW,
$P_{NOJ}(u=%2.1f, \\theta=%3.1f)=%8.2f$ MW"""%(
wf.name, WS, WD, gcl.p_wt.sum()/1.0E6, WS, WD, noj.p_wt.sum()/1.0E6))
Out[25]:
In [26]:
iplot({'data':[{'x': [wt.name for wt in wf.WT],
'y': noj(version=v).u_wt, 'type': 'scatter',
'name':v} for v in noj.versions ]+
[{'x': [wt.name for wt in wf.WT],
'y': gcl(WS=WS,WD=WD,TI=0.1,version=v).u_wt, 'type': 'scatter',
'name':v} for v in gcl.versions] +
[{'x': [wt.name for wt in wf.WT],
'y': gau(version=v).u_wt, 'type': 'scatter',
'name':v} for v in gau.versions],
'layout':{'xaxis':{'title':'Turbine Name'},
'yaxis':{'title':'AVG Wind Speed [m/s]'},
'title': '%s Average wind speed over the rotor at WD=%2.1f deg and WS=%2.1f m/s'%(wf.name, WD, WS)}})
There are several versions of the GCLarsen model implemented in the FUSED-Wake. They are all accessible through the GCL wrapper class using the same I/Os.
In [27]:
Markdown('The different version s of GCL currently available are: `{}`'.format('`, `'.join(gcl.versions)))
Out[27]:
Checkout the docs online to see what is the difference.
In [28]:
powers = [gcl(WS=WS,WD=WD,TI=0.1,version=v).p_wt.sum() for v in gcl.versions] + [noj(version=v).p_wt.sum() for v in noj.versions] + [gau(version=v).p_wt.sum() for v in gau.versions]
# The plot
iplot({'data':[{'x': gcl.versions+noj.versions+gau.versions,
'y':powers, 'type':'bar'}],
'layout':{'xaxis':{'title':'GCL version'},
'yaxis':{'title':'Total power [W]',
'range':[min(powers)*0.99, max(powers)*1.01]},
'title':'Difference between the power predicted by different implementations of GCL and NOJ'}})
# A bit of text
Markdown("""The different versions of GCL, NOJ and GAU give different results for the
{WF.name} case at WS $={WS[0]: .1f}$ m/s, WD $={WD[0]: .1f} ^o$:""".format(**gcl.__dict__)
+ "\n * " + "\n * ".join(["**{0}**: P = {1: .3f} MW".format(v, p/1E6) for v, p in zip(gcl.versions + noj.versions + gau.versions, powers)]))
Out[28]:
In [29]:
[min(powers), max(powers)]
Out[29]:
The GCL has been developed using several different algorithms.
In [ ]:
WDs = range(0,360,1)
# The plot
iplot({'data':[{'x': WDs,
'y':[gcl(WS=8.0, version=v, WD=wd, TI=0.1).p_wt.sum() for wd in WDs],
'name': v} for v in gcl.versions]+
[{'x': WDs,
'y':[noj(WS=8.0, version=v, WD=wd).p_wt.sum() for wd in WDs],
'name': v} for v in noj.versions] +
[{'x': WDs,
'y':[gau(WS=8.0, version=v, WD=wd).p_wt.sum() for wd in WDs],
'name': v} for v in gau.versions],
'layout':{'xaxis':{'title':'Wind Direction [deg]'},
'yaxis':{'title':'Total power [W]'},
'title':'Difference between the power predicted by different implementations of GCL'}})
In [ ]:
In [ ]: