Skip to content
Snippets Groups Projects
Commit 7341c2ac authored by Mikkel Friis-Møller's avatar Mikkel Friis-Møller
Browse files

problems example elaborated. its noted however that make_notebooks is not...

problems example elaborated. its noted however that make_notebooks is not working due to a bug in the drivers.ipynb
parent 685c8566
No related branches found
No related tags found
1 merge request!138Workshop
Pipeline #11577 passed
Showing
with 2272 additions and 274 deletions
......@@ -112,3 +112,6 @@ venv.bak/
# mypy
.mypy_cache/
/.githooks
*.DS_Store
__pycache__
*.egg-info
Source diff could not be displayed: it is too large. Options to address this: view the blob.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
%% Cell type:markdown id: tags:
# Problems
In Topfarm the problem is the collection/container of components and drivers. Optimizing a problem is executing a workflow - finding the best feasible solution under a series of constraints using a specific driver, and initial conditions. To the problem is also attached a plotting routine so one can follow the optimization path as well as a recorder so that intermediate variable values are accessible after the optimization is finished. The Topfarm Problem inherits its fundamental nature from the OpenMDAO problem, and is being adapted so it can connect the given workflow with the driver. For example if the user specifies a boundary constraint and this is not supported by the driver, the Problem is equipped with a penalty component that will deter the driver giving unfeasible solutions. Or if your workflow does not have gradients for all components and a gradient based driver is specified, finite differencing is applied to obtain the gradients.
%% Cell type:markdown id: tags:
**Jump to example in this notebook**:
* [Turbine location optimization](#Turbine-location-optimization)
Make sure you first run the code below in order to set up and initialize needed variables.
%% Cell type:markdown id: tags:
**First we install topfarm and import supporting libraries in Python numpy and matplotlib**
%% Cell type:code id: tags:
``` python
%%capture
try: # install Topfarm if needed
import topfarm
except ModuleNotFoundError:
!pip install topfarm
import numpy as np
```
%% Cell type:code id: tags:
``` python
import matplotlib.pyplot as plt
# non-updating, inline plots
#%matplotlib inline
# ...or updating plots in new window
%matplotlib qt
```
from topfarm import TopFarmProblem
from topfarm.easy_drivers import EasyScipyOptimizeDriver
from topfarm.examples.iea37 import get_iea37_initial, get_iea37_constraints, get_iea37_cost
from topfarm.plotting import NoPlot, XYPlotComp
%% Cell type:code id: tags:
``` python
import matplotlib.pyplot as plt
```
%% Cell type:markdown id: tags:
**Next we import and initialize several functions and classes from Topfarm to set up the problem including:**
* **TopFarmProblem - overall topfarm problem class to which the objectives, design variables, and constraints are added**
* **EasyScipyOptimizeDriver - a subclass of ScipyOptimizeDriver which is configured for the given workflow**
* **get_iea37_initial, get_iea37_constraints, get_iea37_cost - functions to get the initial layout, the constraints and the cost function for the IEA task 37 benchmark example**
* **NoPlot, XYPlotComp - plotting components to visualize the results**
%% Cell type:code id: tags:
``` python
# non-updating, inline plots
%matplotlib inline
# ...or updating plots in new window
# %matplotlib qt
from topfarm import TopFarmProblem
from topfarm.easy_drivers import EasyScipyOptimizeDriver
from topfarm.examples.iea37 import get_iea37_initial, get_iea37_constraints, get_iea37_cost
from topfarm.plotting import NoPlot, XYPlotComp
```
%% Cell type:markdown id: tags:
## Turbine location optimization
This example optimizes the locations of the 9-turbine benchmark wind farm from IEA Task 37 using the provided initial locations and the `EasyScipyOptimizeDriver`. Details on the benchmark can be found in the following reference:
* Baker et al. (2019) "Best Practices for Wake Model and Optimization Algorithm Selection in Wind Farm Layout Optimization". AIAA 2019.
%% Cell type:code id: tags:
``` python
def optimize_iea37_locs(n_wt, driver):
from topfarm.examples.iea37 import *
def get_iea37_cost(n_wt=9, n_wd=16):
"""Cost component that wraps the IEA 37 AEP calculator"""
wd = np.linspace(0., 360., n_wd, endpoint=False)
site = IEA37Site(n_wt)
wind_turbines = IEA37_WindTurbines()
wake_model = IEA37SimpleBastankhahGaussian(site, wind_turbines)
aep_calc = PyWakeAEP(wake_model)
return aep_calc.get_TopFarm_cost_component(n_wt, wd=wd)
```
%% Cell type:code id: tags:
``` python
def optimize_iea37_locs(n_wt, n_wd, driver, state=None):
"""
Parameters
----------
- n_wt: int
Number of wind turbines
- n_wd: int
Number of wind directions to consider for the AEP
- driver: TopfarmDriver instance
The optimization algorithm to use
- state: dict(x=[], y=[]) [default=None]
State to start from the optimization
Returns
-------
- state: The state after the optimization
"""
initial = get_iea37_initial(n_wt)
design_vars = dict(zip('xy', (initial[:, :2]).T))
tf = TopFarmProblem(
dict(zip('xy', (initial[:, :2]).T)),
get_iea37_cost(n_wt),
design_vars,
get_iea37_cost(n_wt, n_wd=n_wd),
constraints=get_iea37_constraints(n_wt),
driver=driver,
plot_comp=XYPlotComp())
tf.optimize()
if not state:
_, state = tf.evaluate()
_, state, _ = tf.optimize(state)
return state,
```
%% Cell type:code id: tags:
``` python
optimize_iea37_locs(9, EasyScipyOptimizeDriver(disp=False))
state = optimize_iea37_locs(9, n_wd=16, EasyScipyOptimizeDriver(disp=False))
```
%% Output
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-5-df00a1eefbe1> in <module>
----> 1 optimize_iea37_locs(9, EasyScipyOptimizeDriver(disp=False))
<ipython-input-4-c9615be881ef> in optimize_iea37_locs(n_wt, driver)
3 tf = TopFarmProblem(
4 dict(zip('xy', (initial[:, :2]).T)),
----> 5 get_iea37_cost(n_wt),
6 constraints=get_iea37_constraints(n_wt),
7 driver=driver,
c:\mmpe\programming\python\topfarm\topfarm2\topfarm\examples\iea37\_iea37.py in get_iea37_cost(n_wt)
50 site = IEA37Site(n_wt)
51 wind_turbines = IEA37_WindTurbines()
---> 52 wake_model = IEA37SimpleBastankhahGaussian(wind_turbines)
53 aep_calc = PyWakeAEP(site, wind_turbines, wake_model)
54 return aep_calc.get_TopFarm_cost_component(n_wt, wd=wd)
TypeError: __init__() missing 1 required positional argument: 'windTurbines'
%% Cell type:markdown id: tags:
The final optimized output is much lower than the value reported in Baker et al. (2019), which is 257.790 GWh. Moreover, the layout does not match the figures given in Appendix A in the same reference. This is due to the fact that the SLSQP optimizer was attracted to a local minimum. To find the global optimum, more advanced optimization procedures should be used. This benchmark is discussed in more detail in the validation report linked in TOPFARM's documentation.
%% Cell type:code id: tags:
``` python
state
```
%% Output
{'x': array([-680.80198103, -854.32314245, -695.66777104, 89.8040452 ,
11.54426432, -64.22919445, 708.93660969, 860.87425685,
677.89454027]),
'y': array([-588.64986431, 283.07590553, 571.00468684, -895.50836595,
9.60717992, 897.70519161, -554.44466226, -262.4795496 ,
591.9957705 ])}
......
%% Cell type:code id: tags:
``` python
%%capture
try:
!git clone https://gitlab.windenergy.dtu.dk/TOPFARM/workshop-december-2019.git
except:
pass
# !cd workshop-december-2019
!pip install -e ./workshop-december-2019
# !cd wind2loads
!pip install ./workshop-december-2019/wind2loads
```
%% Cell type:markdown id: tags:
In colab, use the "inline" backend
%% Cell type:code id: tags:
``` python
# non-updating, inline plots
%matplotlib inline
# ...or updating plots in new window
#%matplotlib qt
```
%% Cell type:markdown id: tags:
Let's import a few classes
%% Cell type:code id: tags:
``` python
import numpy as np
# from openmdao.api import view_model
# from py_wake.examples.data.iea37._iea37 import IEA37_WindTurbines, IEA37Site
from topfarm.cost_models.cost_model_wrappers import CostModelComponent
from topfarm import TopFarmGroup, TopFarmProblem
from topfarm.easy_drivers import EasyRandomSearchDriver, EasyScipyOptimizeDriver
from topfarm.drivers.random_search_driver import RandomizeTurbinePosition_Circle
# from topfarm.constraint_components.boundary import CircleBoundaryConstraint
from topfarm.constraint_components.spacing import SpacingConstraint
from topfarm.constraint_components.boundary import XYBoundaryConstraint
from topfarm.cost_models.electrical.simple_msp import ElNetLength, ElNetCost
import matplotlib.pylab as plt
# Convenient plot component for plotting the cables from simple_msp in jupyter notebooks
from workshop.cabling import XYCablePlotComp, get_site
```
%% Cell type:markdown id: tags:
### Setting up the site to optimize
We will use the IEA-37 site, using the DTU 10MW reference turbine
%% Cell type:code id: tags:
``` python
from workshop.cabling import get_site
from py_wake.examples.data.DTU10MW_RWT import DTU10MW
site = get_site()
n_wt = len(site.initial_position)
windTurbines = DTU10MW()
Drotor_vector = [windTurbines.diameter()] * n_wt
power_rated_vector = [float(windTurbines.power(20)/1000)] * n_wt
hub_height_vector = [windTurbines.hub_height()] * n_wt
rated_rpm_array = 12. * np.ones([n_wt])
print('Number of turbines:', n_wt)
```
%% Cell type:markdown id: tags:
### Setting up the AEP calculator
- Using the Gaussian wake model from Bastankhah & Porté Agel
- Based on 16 wind direction to speed things up (not critical here because we will be using the RandomSearch algorithm)
%% Cell type:code id: tags:
``` python
from py_wake.wake_models.gaussian import IEA37SimpleBastankhahGaussian
from py_wake.aep_calculator import AEPCalculator
## We use the Gaussian wake model
wake_model = IEA37SimpleBastankhahGaussian(site, windTurbines)
AEPCalc = AEPCalculator(wake_model)
## The AEP is calculated using n_wd wind directions
n_wd = 16
wind_directions = np.linspace(0., 360., n_wd, endpoint=False)
def aep_func(x, y, **kwargs):
"""A simple function that takes as input the x,y position of the turbines and return the AEP"""
return AEPCalc.calculate_AEP(x_i=x, y_i=y, wd=wind_directions).sum(-1).sum(-1)*10**6
```
%% Cell type:markdown id: tags:
### Setting up the NREL IRR cost model
Based on the 2006 NREL report
%% Cell type:code id: tags:
``` python
from topfarm.cost_models.economic_models.turbine_cost import economic_evaluation as EE_NREL
def irr_nrel(aep, electrical_connection_cost, **kwargs):
return EE_NREL(Drotor_vector, power_rated_vector, hub_height_vector, aep, electrical_connection_cost).calculate_irr()
```
%% Cell type:markdown id: tags:
### Setting up the DTU IRR cost model
Based on Witold's recent work
%% Cell type:code id: tags:
``` python
from topfarm.cost_models.economic_models.dtu_wind_cm_main import economic_evaluation as EE_DTU
distance_from_shore = 10.0 # [km]
energy_price = 0.2 / 7.4 # [DKK/kWh] / [DKK/EUR] -> [EUR/kWh]
project_duration = 20 # [years]
water_depth_array = 20 * np.ones([n_wt])
Power_rated_array = np.array(power_rated_vector)/1.0E3 # [MW]
ee_dtu = EE_DTU(distance_from_shore, energy_price, project_duration)
def irr_dtu(aep, electrical_connection_cost, **kwargs):
ee_dtu.calculate_irr(
rated_rpm_array,
Drotor_vector,
Power_rated_array,
hub_height_vector,
water_depth_array,
aep,
electrical_connection_cost)
return ee_dtu.IRR
```
%% Cell type:markdown id: tags:
### Setting up the Topfarm problem
%% Cell type:code id: tags:
``` python
## Some user options
#@markdown Which IRR Cost model to use
IRR_COST = 'DTU' #@param ["DTU", "NREL"]
#@markdown Minimum spacing between the turbines
min_spacing = 2 #@param {type:"slider", min:2, max:10, step:1}
#@markdown Minimum spacing between the turbines
cable_cost_per_meter = 750. # {type:"slider", min:0, max:10000, step:1}
## Electrical grid cable components (Minimum spanning tree from Topfarm report 2010)
elnetlength = ElNetLength(n_wt=n_wt)
elnetcost = ElNetCost(n_wt=n_wt, output_key='electrical_connection_cost', cost_per_meter=cable_cost_per_meter)
# The Topfarm IRR cost model components
irr_dtu_comp = CostModelComponent(input_keys=['aep', ('electrical_connection_cost', 0.0)], n_wt=n_wt,
cost_function=irr_dtu, output_key="irr", output_unit="%", objective=True, income_model=True)
irr_nrel_comp = CostModelComponent(input_keys=['aep', ('electrical_connection_cost', 0.0)], n_wt=n_wt,
cost_function=irr_nrel, output_key="irr", output_unit="%", objective=True, income_model=True)
irr_cost_models = {'DTU': irr_dtu_comp, 'NREL': irr_nrel_comp}
## The Topfarm AEP component
aep_comp = CostModelComponent(input_keys=['x','y'], n_wt=n_wt, cost_function=aep_func,
output_key="aep", output_unit="GWh", objective=False, output_val=np.zeros(n_wt))
## Plotting component
plot_comp = XYCablePlotComp(memory=0, plot_improvements_only=False, plot_initial=False)
## The group containing all the components
group = TopFarmGroup([aep_comp, elnetlength, elnetcost, irr_cost_models[IRR_COST]])
problem = TopFarmProblem(
design_vars=dict(zip('xy', site.initial_position.T)),
cost_comp=group,
driver=EasyRandomSearchDriver(randomize_func=RandomizeTurbinePosition_Circle(), max_iter=1000),
constraints=[SpacingConstraint(min_spacing * windTurbines.diameter(0)),
XYBoundaryConstraint(site.boundary)],
expected_cost=1.0,
plot_comp=plot_comp)
cost, state, recorder = problem.optimize()
```
%% Cell type:markdown id: tags:
### Exercises
- Try to see what is the effect of increasing or decreasing the cost of the cable
- Change between IRR cost model. Ask Witold about the difference between DTU and NREL models
%% Cell type:code id: tags:
``` python
```
......@@ -5,7 +5,7 @@ DTU Cost Model
.. autoclass:: topfarm.cost_models.dtu_wind_cm.dtu_wind_cm_main.economic_evaluation
.. autoclass:: topfarm.cost_models.economic_models.dtu_wind_cm_main.economic_evaluation
.. autosummary::
__init__
......
......@@ -61,7 +61,8 @@ DTU Cost Model
called individually. It generally relies on curve fitting using input parameters such as
rated power or water depth, and was tuned using data obtained from the
industry. It supports three types of drivetrains: high-speed, medium-speed and direct-drive.
It also supports two types of offshore foundations: monopile and jacket.
It also supports two types of offshore foundations: monopile and jacket. The model was originally created in Excel by Kenneth Thomse and
Søren Oemann Lind and Rasmus (publication forthcoming). The monetary units are 2017 Euro.
Drivers
......
......@@ -52,7 +52,7 @@ def main(obj=False, max_con_on=True):
# ---------------- DEFINE SITE & SELECT WAKE MODEL -------------------
# site = UniformWeibullSite(p_wd=[50, 50], a=[9, 9], k=[2.3, 2.3], ti=.1, alpha=0, h_ref=100)
site = UniformWeibullSite(p_wd=[100], a=[9], k=[2.3], ti=.1, alpha=0, h_ref=100)
site = UniformWeibullSite(p_wd=[100], a=[9], k=[2.3], ti=.1)
site.default_ws = [9] # reduce the number of calculations
site.default_wd = [0] # reduce the number of calculations
......
import numpy as np
from openmdao.api import view_model
from py_wake.examples.data.iea37._iea37 import IEA37_WindTurbines, IEA37Site
from py_wake.wake_models.gaussian import IEA37SimpleBastankhahGaussian
from py_wake.aep_calculator import AEPCalculator
from topfarm.cost_models.economic_models.turbine_cost import economic_evaluation
from topfarm.cost_models.cost_model_wrappers import CostModelComponent
from topfarm import TopFarmGroup, TopFarmProblem
from topfarm.easy_drivers import EasyRandomSearchDriver
from topfarm.drivers.random_search_driver import RandomizeTurbinePosition_Circle
from topfarm.constraint_components.boundary import CircleBoundaryConstraint
from topfarm.plotting import XYPlotComp, NoPlot
from topfarm.constraint_components.spacing import SpacingConstraint
from py_wake.examples.data.DTU10MW_RWT import DTU10MW
from py_wake.site import WaspGridSiteBase, UniformWeibullSite
from py_wake.site.shear import PowerShear
from topfarm.constraint_components.boundary import XYBoundaryConstraint
def get_site():
f = [0.035972, 0.039487, 0.051674, 0.070002, 0.083645, 0.064348,
0.086432, 0.117705, 0.151576, 0.147379, 0.10012, 0.05166]
A = [9.176929, 9.782334, 9.531809, 9.909545, 10.04269, 9.593921,
9.584007, 10.51499, 11.39895, 11.68746, 11.63732, 10.08803]
k = [2.392578, 2.447266, 2.412109, 2.591797, 2.755859, 2.595703,
2.583984, 2.548828, 2.470703, 2.607422, 2.626953, 2.326172]
ti = 0.001
h_ref = 100
alpha = .1
site = UniformWeibullSite(f, A, k, ti, shear=PowerShear(h_ref=h_ref, alpha=alpha))
spacing = 2000
N = 5
theta = 76 # deg
dx = np.tan(np.radians(theta))
x = np.array([np.linspace(0,(N-1)*spacing,N)+i*spacing/dx for i in range(N)])
y = np.array(np.array([N*[i*spacing] for i in range(N)]))
initial_positions = np.column_stack((x.ravel(),y.ravel()))
eps = 2000
delta = 5
site.boundary = np.array([(0-delta, 0-delta),
((N-1)*spacing+eps, 0-delta),
((N-1)*spacing*(1+1/dx)+eps*(1+np.cos(np.radians(theta))), (N-1)*spacing+eps*np.sin(np.radians(theta))-delta),
((N-1)*spacing/dx+eps*np.cos(np.radians(theta)), (N-1)*spacing+eps*np.sin(np.radians(theta)))])
site.initial_position = initial_positions
return site
def main():
if __name__ == '__main__':
plot_comp = XYPlotComp()
site = get_site()
n_wt = len(site.initial_position)
windTurbines = DTU10MW()
min_spacing = 2 * windTurbines.diameter(0)
wake_model = IEA37SimpleBastankhahGaussian(site, windTurbines)
Drotor_vector = [windTurbines.diameter()] * n_wt
power_rated_vector = [float(windTurbines.power(20)/1000)] * n_wt
hub_height_vector = [windTurbines.hub_height()] * n_wt
AEPCalc = AEPCalculator(wake_model)
def aep_func(x, y, **kwargs):
return AEPCalc.calculate_AEP(x_i=x, y_i=y).sum(-1).sum(-1)*10**6
def irr_func(aep, **kwargs):
return economic_evaluation(Drotor_vector, power_rated_vector, hub_height_vector, aep).calculate_irr()
aep_comp = CostModelComponent(input_keys=['x','y'], n_wt=n_wt, cost_function=aep_func, output_key="aep", output_unit="GWh", objective=False, output_val=np.zeros(n_wt))
irr_comp = CostModelComponent(input_keys=['aep'], n_wt=n_wt, cost_function=irr_func, output_key="irr", output_unit="%", objective=True, income_model=True)
group = TopFarmGroup([aep_comp, irr_comp])
problem = TopFarmProblem(
design_vars=dict(zip('xy', site.initial_position.T)),
cost_comp=group,
driver=EasyRandomSearchDriver(randomize_func=RandomizeTurbinePosition_Circle(), max_iter=50),
constraints=[SpacingConstraint(min_spacing),
XYBoundaryConstraint(site.boundary),],
plot_comp=plot_comp)
cost, state, recorder = problem.optimize()
main()
This diff is collapsed.
......@@ -29,7 +29,7 @@ setup(name='topfarm',
'openmdao==2.6', # for optimization
'pytest', # for testing
'pytest-cov', # for calculating coverage
'py_wake', # for calculating AEP
'py_wake>=1.0.11', # for calculating AEP
'scipy', # constraints
'sphinx', # generating documentation
'sphinx_rtd_theme', # docs theme
......
......@@ -15,14 +15,13 @@ class economic_evaluation():
# Initialize dictionaries
self.project_costs = {}
self.project_costs_sums = {}
self.discount_rate = discount_rate # [-]
self.distance_from_shore = distance_from_shore # [km]
self.energy_price = energy_price # [Euro/kWh]
self.project_duration = project_duration # [years]
def calculate_npv(self, rated_rpm_array, D_rotor_array, Power_rated_array,
hub_height_array, water_depth_array, aep_array):
hub_height_array, water_depth_array, aep_array, cabling_cost=None):
'''
Calculate Net Present Value [Euro]
......@@ -41,7 +40,8 @@ class economic_evaluation():
Power_rated_array,
hub_height_array,
aep_array,
water_depth_array)
water_depth_array,
cabling_cost)
self.calculate_cash_flow()
......@@ -50,7 +50,7 @@ class economic_evaluation():
return self.NPV
def calculate_irr(self, rated_rpm_array, D_rotor_array, Power_rated_array,
hub_height_array, water_depth_array, aep_array):
hub_height_array, water_depth_array, aep_array, cabling_cost=None):
'''
Calculate Internal Rate of Return [%]
......@@ -69,7 +69,8 @@ class economic_evaluation():
Power_rated_array,
hub_height_array,
aep_array,
water_depth_array)
water_depth_array,
cabling_cost)
self.calculate_cash_flow()
......@@ -97,7 +98,7 @@ class economic_evaluation():
self.project_costs_sums["OPEX"]))
def calculate_expenditures(self, rated_rpm, rotor_diameter, rated_power,
hub_height, aep_array, water_depth):
hub_height, aep_array, water_depth, cabling_cost=None):
'''Calculate DEVEX, CAPEX, OPEX and ABEX [Euro]'''
# Ensure that the variables are numpy arrays
......@@ -107,7 +108,7 @@ class economic_evaluation():
self.calculate_devex(rated_power)
self.calculate_capex(rated_rpm, rotor_diameter, rated_power,
hub_height, water_depth)
hub_height, water_depth, cabling_cost)
self.calculate_opex(rated_power)
......@@ -132,7 +133,7 @@ class economic_evaluation():
self.project_costs[myName].values())
def calculate_capex(self, rated_rpm, rotor_diameter, rated_power,
hub_height, water_depth):
hub_height, water_depth, cabling_cost=None):
'''Calculate CAPEX [Euro]'''
rated_rpm = np.array(rated_rpm)
......@@ -166,6 +167,9 @@ class economic_evaluation():
50.0 *
np.sum(rated_power) ** 2.0}
if cabling_cost:
self.project_costs[myName]["array_of_cables"] = cabling_cost
self.project_costs_sums[myName] = sum(
self.project_costs[myName].values())
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment