diff --git a/.gitignore b/.gitignore index 851ce23c4510ab8b5e0bdc8639ae37d36ec28346..7296ab97742774a3a9367947f8382414773e51a1 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ /cost_models/fuga/pascal_test /topfarm.egg_info snopt +/topfarm.egg-info diff --git a/examples/__init__.py b/examples/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/examples/irp_wind/3tb.yml b/examples/irp_wind/3tb.yml new file mode 100644 index 0000000000000000000000000000000000000000..0da0d247ac528b3c91e0e310ef48abbfe96a965b --- /dev/null +++ b/examples/irp_wind/3tb.yml @@ -0,0 +1,143 @@ +layout: + - name: WT01 + row: 1 + position: [0, 160] + turbine_type: V80 + - name: WT02 + row: 2 + position: [0, 0] + turbine_type: V80 + - name: WT03 + row: 3 + position: [0, -160] + turbine_type: V80 + +metmasts: + - name: M2 + position: [423412, 6153342] # <- wrong location!! please find the right one and make a pull request + - name: M6 + position: [431255, 6149504] + - name: M7 + position: [435252, 6149494] + +plant_data: + utm: + code: 32 + letter: U + name: Horns Rev + owner: DONG Energy, Vattenfall + +turbine_types: + # Sources: + # [1] https://windbench.net/system/files/hornsrev_v80_0.pdf + # [2] http://www.thewindpower.net/turbine_en_30_vestas_2000.php + # [3] http://en.wind-turbine-models.com/turbines/668-vestas-v-80-offshore + # [4] WAsP wind turbine library (distributed as part of the WAsP software) + - name: V80 + hub_height: 70.0 # [1] + rotor_diameter: 80.0 # [1] + rated_power: 2000.0 # [1] + cut_in_wind_speed: 4.0 # [1] + cut_out_wind_speed: 25.0 # [1] + rated_wind_speed: 16.0 # [1] + wind_class: IEC Ia (DIBt III) # [2] + air_density: 1.225 # guess + gear_box: + speed_number: 3 + ratio: 1:100,5 # [2] + type: spur/planetary # [3] + nacelle: # [2] + weight: 68000.0 # kg [2], 69000.0 kg according to [3] + rotor: # [2, 3] + weight: 37000.0 # kg [2] + tip_speed: 80.0 # m/s [3] + min_speed: 9 # rd/min [2] + max_speed: 19 # rd/min [2] + manufacturer: Vestas + hub: + weight: 18000 # kg [3] + tower: # [2] + weight: 198000.0 #kg [2], max 148000.0 kg according to [3] + material: steel + manufacturer: Welcon + control: # [1] + type: Active Pitch, Variable Speed + generator: # [2] + type: ASYNC + number: 1 + max_output_speed: 1909 #rounds/minute + output_voltage: 690 #V + grid_frequency: 50/60 # Hz + power_curve: # [1] + - [3.0, 0.0] + - [4.0, 66.6] + - [5.0, 154.0] + - [6.0, 282.0] + - [7.0, 460.0] + - [8.0, 696.0] + - [9.0, 996.0] + - [10.0, 1341.0] + - [11.0, 1661.0] + - [12.0, 1866.0] + - [13.0, 1958.0] + - [14.0, 1988.0] + - [15.0, 1997.0] + - [16.0, 1999.0] + - [17.0, 2000.0] + - [18.0, 2000.0] + - [19.0, 2000.0] + - [20.0, 2000.0] + - [21.0, 2000.0] + - [22.0, 2000.0] + - [23.0, 2000.0] + - [24.0, 2000.0] + - [25.0, 2000.0] + c_t_curve: + - [3.0, 0.00] + - [4.0, 0.818] + - [5.0, 0.806] + - [6.0, 0.804] + - [7.0, 0.805] + - [8.0, 0.806] + - [9.0, 0.807] + - [10.0, 0.793] + - [11.0, 0.739] + - [12.0, 0.709] + - [13.0, 0.409] + - [14.0, 0.314] + - [15.0, 0.249] + - [16.0, 0.202] + - [17.0, 0.167] + - [18.0, 0.140] + - [19.0, 0.119] + - [20.0, 0.102] + - [21.0, 0.088] + - [22.0, 0.077] + - [23.0, 0.067] + - [24.0, 0.060] + - [25.0, 0.053] + c_t_idle: 0.053 # [4] + blade: + geometry: # [1] + # [radius [m], c [m], twist [deg], airfoil ] + - [2.563, 2.004, 9.50, 'Cylinder 1'] + - [4.389, 2.523, 9.50, 'Cylinder 1'] + - [6.216, 3.015, 9.50, 'FFA W3-301'] + - [8.042, 3.278, 9.50, 'FFA W3-301'] + - [9.868, 3.309, 9.50, 'FFA W3-301'] + - [11.694, 3.195, 9.50, 'FFA W3-301'] + - [13.520, 3.039, 9.22, 'FFA W3-241'] + - [15.346, 2.863, 7.81, 'FFA W3-211'] + - [17.173, 2.687, 6.40, 'FFA W3-211'] + - [18.999, 2.511, 5.11, 'FFA W3-211'] + - [20.825, 2.334, 3.83, 'FFA W3-211'] + - [22.651, 2.158, 2.61, 'NACA 63-221'] + - [24.477, 1.982, 1.48, 'NACA 63-221'] + - [26.304, 1.806, 0.42, 'NACA 63-221'] + - [28.130, 1.630, 0.49, 'NACA 63-221'] + - [29.956, 1.454, 1.23, 'NACA 63-218'] + - [31.782, 1.278, 1.79, 'NACA 63-218'] + - [33.608, 1.102, 2.24, 'NACA 63-218'] + - [35.435, 0.926, 2.61, 'NACA 63-218'] + - [37.261, 0.749, 2.84, 'NACA 63-218'] + - [39.087, 0.573, 2.97, 'NACA 63-218'] \ No newline at end of file diff --git a/examples/irp_wind/Hornsrev_aep.py b/examples/irp_wind/Hornsrev_aep.py new file mode 100644 index 0000000000000000000000000000000000000000..47d6c5e30d8d39c221ae5112bc9cfa61eca8d4ea --- /dev/null +++ b/examples/irp_wind/Hornsrev_aep.py @@ -0,0 +1,39 @@ +import os +from fusedwake import fusedwake +import numpy as np +from topfarm.cost_models.fuga import py_fuga +from topfarm.cost_models.fuga.py_fuga import PyFuga +from topfarm.cost_models.fused_wake_wrappers import FusedWakeGCLWakeModel +from topfarm.cost_models.utils.aep_calculator import AEPCalculator +from topfarm.cost_models.utils.wind_resource import WindResource + + +def HornsrevAEP_FUSEDWake_GCL(): + # wind parameters from "Horns Rev 1\hornsrev2.lib + wdir_freq = [0.035972, 0.039487, 0.051674, 0.070002, 0.083645, 0.064348, 0.086432, 0.117705, 0.151576, 0.147379, 0.10012, 0.05166] + weibull_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] + weibull_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 = np.zeros_like(wdir_freq) + .1 + wr = WindResource(wdir_freq, weibull_A, weibull_k, ti) + hornsrev_yml = os.path.dirname(fusedwake.__file__) + "/../examples/hornsrev.yml" + wm = FusedWakeGCLWakeModel(hornsrev_yml) + aep_calc = AEPCalculator(wr, wm) + return aep_calc(wm.windFarm.pos) + + +def HornsrevAEP_Fuga(): + fuga_path = os.path.abspath(os.path.dirname(py_fuga.__file__)) + '/Colonel/' + hornsrev_yml = os.path.dirname(fusedwake.__file__) + "/../examples/hornsrev.yml" + wm = FusedWakeGCLWakeModel(hornsrev_yml) + tb_x, tb_y = wm.windFarm.pos + pyFuga = PyFuga(farm_name='Horns Rev 1', + turbine_model_path=fuga_path + 'LUT/', turbine_model_name='Vestas_V80_(2_MW_offshore)[h=67.00]', + tb_x=tb_x, tb_y=tb_y, + mast_position=(0, 0, 70), z0=0.0001, zi=400, zeta0=0, + farms_dir=fuga_path + 'LUT/Farms/', wind_atlas_path='Horns Rev 1\hornsrev2.lib') + return pyFuga.get_aep(tb_x, tb_x)[0] + + +if __name__ == '__main__': + print(HornsrevAEP_FUSEDWake_GCL()) + print(HornsrevAEP_Fuga()) diff --git a/examples/irp_wind/__init__.py b/examples/irp_wind/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/examples/irp_wind/optimization_3tb.py b/examples/irp_wind/optimization_3tb.py new file mode 100644 index 0000000000000000000000000000000000000000..67adc61a37bc04dd600c60a684f1367238867919 --- /dev/null +++ b/examples/irp_wind/optimization_3tb.py @@ -0,0 +1,56 @@ +import os +import numpy as np +from topfarm.cost_models.fuga import py_fuga +from topfarm.cost_models.fuga.py_fuga import PyFuga +from topfarm.cost_models.fused_wake_wrappers import FusedWakeGCLWakeModel +from topfarm.cost_models.utils.aep_calculator import AEPCalculator +from topfarm.cost_models.utils.wind_resource import WindResource +from topfarm.plotting import PlotComp +from topfarm.topfarm import TopFarm + + +D = 80.0 +D2 = 2 * D +initial_position = np.array([(0, D2), (0, 0), (0, -D2)]) +boundary = [(-D2, D2), (D2, D2), (D2, -D2), (-D2, -D2)] +minSpacing = 2.0 + + +def optimize_AEP_FusedWake_GCL(): + plot_comp = PlotComp() + f = [1, 0, 0, 0] + A = [9.176929, 9.782334, 9.531809, 9.909545] + k = [2.392578, 2.447266, 2.412109, 2.591797] + + wr = WindResource(f, A, k, ti=np.zeros_like(f) + .1) + + wm = FusedWakeGCLWakeModel(os.path.dirname(__file__) + "/3tb.yml") + aep_calc = AEPCalculator(wr, wm) + aep_calc(np.array([[0, 0, 100], [160, 0, -160]]).T) + init_pos = initial_position.copy() + init_pos[:, 0] += [-50, 0, 50] + print(aep_calc(np.array([[-160, 0, 160], [0, 0, 0]]).T)) + tf = TopFarm(init_pos, aep_calc.get_TopFarm_cost_component(), minSpacing * D, boundary=boundary, plot_comp=plot_comp) + tf.optimize() + plot_comp.show() + + +def optimize_AEP_Fuga(): + plot_comp = PlotComp() + fuga_path = os.path.abspath(os.path.dirname(py_fuga.__file__)) + '/Colonel/' + pyFuga = PyFuga(farm_name='Horns Rev 1', + turbine_model_path=fuga_path + 'LUT/', turbine_model_name='Vestas_V80_(2_MW_offshore)[h=67.00]', + tb_x=initial_position[:, 0], tb_y=initial_position[:, 1], + mast_position=(0, 0, 70), z0=0.0001, zi=400, zeta0=0, + farms_dir=fuga_path + 'LUT/Farms/', wind_atlas_path='Horns Rev 1\hornsrev_north_only.lib') + + init_pos = initial_position.copy() + init_pos[:, 0] += [-20, 0, 20] + tf = TopFarm(init_pos, pyFuga.get_TopFarm_cost_component(), minSpacing * D, boundary=boundary, plot_comp=plot_comp) + tf.optimize() + plot_comp.show() + + +if __name__ == '__main__': + optimize_AEP_Fuga() + optimize_AEP_FusedWake_GCL() diff --git a/topfarm/cost_models/fused_wake_wrappers.py b/topfarm/cost_models/fused_wake_wrappers.py new file mode 100644 index 0000000000000000000000000000000000000000..2ed053f2080bb5b6bdcbaf5d0610501f4c4ff66a --- /dev/null +++ b/topfarm/cost_models/fused_wake_wrappers.py @@ -0,0 +1,51 @@ +''' +Created on 20. apr. 2018 + +@author: mmpe +''' +from fusedwake.WindFarm import WindFarm +from fusedwake.gcl.interface import GCL +import numpy as np +from topfarm.cost_models.cost_model_wrappers import AEPCostModelComponent + + +class FusedWakeGCLWakeModel(object): + + def __init__(self, yml): + """Description + + Parameters + ---------- + yml: str, optional + A WindIO `yml` file containing the description of the farm + """ + self.windFarm = WindFarm(yml=yml) + self.gcl = GCL(WF=self.windFarm, version='fort_gcl') + + def __call__(self, turbine_positions, no_wake_wdir, no_wake_wsp, no_wake_ti): + self.gcl.update_position(turbine_positions.T) + + WD, WS, TI = [np.mean(np.atleast_3d(v), 0) for v in [no_wake_wdir, no_wake_wsp, no_wake_ti]] + self.gcl(WS=WS.flatten(), WD=WD.flatten(), TI=TI.flatten()) + p = self.gcl.p_wt + p = p.reshape(WD.shape + (self.windFarm.nWT,)) + return p.sum(2) # sum over all turbines + + + +if __name__ == '__main__': + from fusedwake import fusedwake + import os + + hornsrev_yml = os.path.dirname(fusedwake.__file__) + "/../examples/hornsrev.yml" + wm = FusedWakeGCLWakeModel(hornsrev_yml) + tb_pos = wm.windFarm.pos + + print(wm(tb_pos, no_wake_wdir=270, no_wake_wsp=8, no_wake_ti=0.1).shape) + + WS_cases = np.arange(4, 12) + WD_cases = np.arange(0, 360, 10) + WS_ms, WD_ms = np.meshgrid(WS_cases, WD_cases) + p = wm(tb_pos, WD_ms, WS_ms, np.zeros_like(WS_ms) + .1) + print(p.shape) + print(p)