def __init__(self, site, windTurbines, k=.1, superpositionModel=SquaredSum(), deflectionModel=None, turbulenceModel=None): """ Parameters ---------- site : Site Site object windTurbines : WindTurbines WindTurbines object representing the wake generating wind turbines k : float, default 0.1 wake expansion factor superpositionModel : SuperpositionModel, default SquaredSum Model defining how deficits sum up blockage_deficitModel : DeficitModel, default None Model describing the blockage(upstream) deficit deflectionModel : DeflectionModel, default None Model describing the deflection of the wake due to yaw misalignment, sheared inflow, etc. turbulenceModel : TurbulenceModel, default None Model describing the amount of added turbulence in the wake """ PropagateDownwind.__init__(self, site, windTurbines, wake_deficitModel=NOJDeficit(k), superpositionModel=superpositionModel, deflectionModel=deflectionModel, turbulenceModel=turbulenceModel)
def __init__(self, site, windTurbines, a=[0.38, 4e-3], superpositionModel=SquaredSum(), deflectionModel=None, turbulenceModel=None, groundModel=None): """ Parameters ---------- site : Site Site object windTurbines : WindTurbines WindTurbines object representing the wake generating wind turbines superpositionModel : SuperpositionModel, default SquaredSum Model defining how deficits sum up deflectionModel : DeflectionModel, default None Model describing the deflection of the wake due to yaw misalignment, sheared inflow, etc. turbulenceModel : TurbulenceModel, default None Model describing the amount of added turbulence in the wake """ PropagateDownwind.__init__(self, site, windTurbines, wake_deficitModel=ZongGaussianDeficit(a=a), superpositionModel=superpositionModel, deflectionModel=deflectionModel, turbulenceModel=turbulenceModel, groundModel=groundModel)
def __init__(self, LUT_path, site, windTurbines, rotorAvgModel=RotorCenter(), deflectionModel=None, turbulenceModel=None): """ Parameters ---------- LUT_path : str path to look up tables site : Site Site object windTurbines : WindTurbines WindTurbines object representing the wake generating wind turbines rotorAvgModel : RotorAvgModel Model defining one or more points at the down stream rotors to calculate the rotor average wind speeds from.\n Defaults to RotorCenter that uses the rotor center wind speed (i.e. one point) only deflectionModel : DeflectionModel Model describing the deflection of the wake due to yaw misalignment, sheared inflow, etc. turbulenceModel : TurbulenceModel Model describing the amount of added turbulence in the wake """ PropagateDownwind.__init__(self, site, windTurbines, wake_deficitModel=FugaDeficit(LUT_path), rotorAvgModel=rotorAvgModel, superpositionModel=LinearSum(), deflectionModel=deflectionModel, turbulenceModel=turbulenceModel)
def __init__(self, site, windTurbines, rotorAvgModel=RotorCenter(), superpositionModel=SquaredSum(), deflectionModel=None, turbulenceModel=None): """ Parameters ---------- site : Site Site object windTurbines : WindTurbines WindTurbines object representing the wake generating wind turbines rotorAvgModel : RotorAvgModel Model defining one or more points at the down stream rotors to calculate the rotor average wind speeds from.\n Defaults to RotorCenter that uses the rotor center wind speed (i.e. one point) only superpositionModel : SuperpositionModel, default SquaredSum Model defining how deficits sum up deflectionModel : DeflectionModel, default None Model describing the deflection of the wake due to yaw misalignment, sheared inflow, etc. turbulenceModel : TurbulenceModel, default None Model describing the amount of added turbulence in the wake """ PropagateDownwind.__init__( self, site, windTurbines, wake_deficitModel=IEA37SimpleBastankhahGaussianDeficit(), rotorAvgModel=rotorAvgModel, superpositionModel=superpositionModel, deflectionModel=deflectionModel, turbulenceModel=turbulenceModel)
def __init__(self, site, windTurbines, rotorAvgModel=RotorCenter(), superpositionModel=LinearSum(), deflectionModel=None, turbulenceModel=None): PropagateDownwind.__init__(self, site, windTurbines, wake_deficitModel=GCLDeficit(use_effective_ws=True, use_effective_ti=True), rotorAvgModel=rotorAvgModel, superpositionModel=superpositionModel, deflectionModel=deflectionModel, turbulenceModel=turbulenceModel)
def test_double_wind_farm_model(): """Check that a new wind farm model does not change results of previous""" site = IEA37Site(16) x, y = site.initial_position.T windTurbines = IEA37_WindTurbines() wfm = PropagateDownwind(site, windTurbines, wake_deficitModel=IEA37SimpleBastankhahGaussianDeficit()) aep_ref = wfm(x, y).aep().sum() PropagateDownwind(site, windTurbines, wake_deficitModel=NoWakeDeficit()) aep = wfm(x, y).aep().sum() npt.assert_array_equal(aep, aep_ref)
def test_wake_radius(deficitModel, wake_radius_ref): mean_ref = [105, 68, 135, 93, 123] # check that ref is reasonable npt.assert_allclose(wake_radius_ref, mean_ref, rtol=.5) npt.assert_array_almost_equal(deficitModel.wake_radius( D_src_il=np.reshape([100, 50, 100, 100, 100], (5, 1)), dw_ijlk=np.reshape([500, 500, 1000, 500, 500], (5, 1, 1, 1)), ct_ilk=np.reshape([.8, .8, .8, .4, .8], (5, 1, 1)), TI_ilk=np.reshape([.1, .1, .1, .1, .05], (5, 1, 1)), TI_eff_ilk=np.reshape([.1, .1, .1, .1, .05], (5, 1, 1)))[:, 0, 0, 0], wake_radius_ref) # Check that it works when called from WindFarmModel site = IEA37Site(16) windTurbines = IEA37_WindTurbines() wfm = PropagateDownwind(site, windTurbines, wake_deficitModel=deficitModel, turbulenceModel=GCLTurbulence()) wfm(x=[0, 500], y=[0, 0], wd=[30], ws=[10]) if 0: sim_res = wfm([0], [0], wd=[270], ws=10) sim_res.flow_map(HorizontalGrid(x=np.arange(-100, 1500, 10))).WS_eff.plot() x = np.arange(0, 1500, 10) wr = deficitModel.wake_radius( D_src_il=np.reshape([130], (1, 1)), dw_ijlk=np.reshape(x, (1, len(x), 1, 1)), ct_ilk=sim_res.CT.values, TI_ilk=np.reshape(sim_res.TI.values, (1, 1, 1)), TI_eff_ilk=sim_res.TI_eff.values)[0, :, 0, 0] plt.title(deficitModel.__class__.__name__) plt.plot(x, wr) plt.plot(x, -wr) plt.axis('equal') plt.show()
def test_fuga_deflection_time_series_gradient_evaluation(): p = Path(tfp) / "fuga/v80_wake_center_x.csv" x, notebook_wake_center = np.array([v.split(",") for v in p.read_text().strip().split("\n")], dtype=float).T powerCtFunction = PowerCtTabular([0, 100], [0, 0], 'w', [0.850877, 0.850877]) wt = WindTurbine(name='', diameter=80, hub_height=70, powerCtFunction=powerCtFunction) path = tfp + 'fuga/2MW/Z0=0.00001000Zi=00400Zeta0=0.00E+00' site = UniformSite([1, 0, 0, 0], ti=0.075) wfm = PropagateDownwind( site, wt, wake_deficitModel=FugaYawDeficit(path), deflectionModel=FugaDeflection(path, 'input_par') ) WS = 10 yaw_ref = np.full((10, 1), 17) yaw_step = np.eye(10, 10) * 1e-6 + yaw_ref yaw = np.concatenate([yaw_step, yaw_ref], axis=1) sim_res = wfm(np.arange(10) * wt.diameter() * 4, [0] * 10, yaw=yaw, wd=[270] * 11, ws=[WS] * 11, time=True) print(sim_res)
def test_fuga_wake_center_vs_notebook(): p = Path(tfp) / "fuga/v80_wake_center_x.csv" x, notebook_wake_center = np.array([v.split(",") for v in p.read_text().strip().split("\n")], dtype=float).T powerCtFunction = PowerCtTabular([0, 100], [0, 0], 'w', [0.850877, 0.850877]) wt = WindTurbine(name='', diameter=80, hub_height=70, powerCtFunction=powerCtFunction) path = tfp + 'fuga/2MW/Z0=0.00001000Zi=00400Zeta0=0.00E+00' site = UniformSite([1, 0, 0, 0], ti=0.075) wfm = PropagateDownwind( site, wt, wake_deficitModel=FugaYawDeficit(path), deflectionModel=FugaDeflection(path, 'input_par') ) WS = 10 sim_res = wfm([0], [0], yaw=[17.4493], wd=270, ws=[WS]) y = wfm.wake_deficitModel.mirror(wfm.wake_deficitModel.y, anti_symmetric=True) fm = sim_res.flow_map(XYGrid(x=x[1:], y=y[240:271])) fuga_wake_center = [np.interp(0, InterpolatedUnivariateSpline(ws.y, ws.values).derivative()(ws.y), ws.y) for ws in fm.WS_eff.squeeze().T] if 0: plt.plot(x, notebook_wake_center, label='Notebook deflection') plt.plot(x[1:], fuga_wake_center) plt.show() plt.close('all') npt.assert_allclose(fuga_wake_center, notebook_wake_center[1:], atol=.14)
def test_interpolation(): wts = HornsrevV80() path = tfp + 'fuga/2MW/Z0=0.00408599Zi=00400Zeta0=0.00E+00/' site = UniformSite([1, 0, 0, 0], ti=0.075) plot = 0 if plot: ax1 = plt.gca() ax2 = plt.twinx() for wdm, n_d_values in ( (FugaDeficit(path, method='linear'), 4), (FugaDeficit(path, method='spline'), 20), (FugaYawDeficit(path, method='linear'), 4), (FugaYawDeficit(path, method='spline'), 20), ): wfm = PropagateDownwind(site, wts, wdm) sim_res = wfm(x=[0], y=[0], wd=[270], ws=[10], yaw=[[[10]]]) fm = sim_res.flow_map(XYGrid(x=[200], y=np.arange(-10, 11))) fm = sim_res.flow_map( XYGrid(x=np.arange(-100, 800, 10), y=np.arange(-10, 11))) # linear has 4 line segments with same gradient, while spline has 20 different gradient values npt.assert_equal( len(np.unique(np.round(np.diff(fm.WS_eff.sel(x=500).squeeze()), 6))), n_d_values) if plot: ax1.plot(fm.y, fm.WS_eff.sel(x=500).squeeze()) ax2.plot(fm.y[:-1], np.diff(fm.WS_eff.sel(x=500).squeeze()), '--') if plot: plt.show() plt.close('all')
def test_fuga_wriggles(): wts = HornsrevV80() path = tfp + 'fuga/2MW/Z0=0.03000000Zi=00401Zeta0=0.00E+00/' site = hornsrev1.Hornsrev1Site() fuga = PropagateDownwind(site, wts, FugaDeficit(path, remove_wriggles=True)) D = 80 flow_map_cw = fuga([0], [0], wd=270, ws=10).flow_map( HorizontalGrid([0], np.arange(-20 * D, 20 * D))) y = np.linspace(-5 * D, 5 * D, 100) dw_lst = range(10) flow_map_cw_lst = np.array([ fuga([0], [0], wd=270, ws=10).flow_map(HorizontalGrid([dw * D], y)).WS_eff.squeeze() for dw in dw_lst ]) if 0: for flow_map_cw, dw in zip(flow_map_cw_lst, dw_lst): plt.plot(y, flow_map_cw, label="%dD" % dw) plt.xlabel('y [m]') plt.ylabel('ws [m/s') plt.ylim([9.9, 10.1]) plt.grid() plt.legend(loc=1) plt.show() assert np.all(flow_map_cw_lst > 0)
def test_deficitModel_wake_map_convection(deficitModel, ref): site = IEA37Site(16) x, y = site.initial_position.T windTurbines = IEA37_WindTurbines() wf_model = PropagateDownwind(site, windTurbines, wake_deficitModel=deficitModel, superpositionModel=WeightedSum(), turbulenceModel=GCLTurbulence()) x_j = np.linspace(-1500, 1500, 200) y_j = np.linspace(-1500, 1500, 100) flow_map = wf_model(x, y, wd=0, ws=9).flow_map(HorizontalGrid(x_j, y_j)) X, Y = flow_map.X, flow_map.Y Z = flow_map.WS_eff_xylk[:, :, 0, 0] mean_ref = [3.2, 4.9, 8., 8.2, 7.9, 7.4, 7., 7., 7.4, 7.9, 8.1, 8.1, 8., 7.8, 7.9, 8.1, 8.4] if 0: flow_map.plot_wake_map() plt.plot(X[49, 100:133:2], Y[49, 100:133:2], '.-') windTurbines.plot(x, y) plt.figure() plt.plot(Z[49, 100:133:2], label='Actual') plt.plot(ref, label='Reference') plt.plot(mean_ref, label='Mean ref') plt.legend() plt.show() # check that ref is reasonable npt.assert_allclose(ref[2:], mean_ref[2:], atol=2.6) npt.assert_array_almost_equal(Z[49, 100:133:2], ref, 2)
def test_fuga_downwind(): wts = HornsrevV80() path = tfp + 'fuga/2MW/Z0=0.00408599Zi=00400Zeta0=0.00E+00' site = UniformSite([1, 0, 0, 0], ti=0.075) wfm_UL = Fuga(path, site, wts) wfm_ULT = PropagateDownwind(site, wts, FugaYawDeficit(path)) (ax1, ax2), (ax3, ax4) = plt.subplots(2, 2)[1] def plot(wfm, yaw, ax, min_ws): levels = np.arange(6.5, 10.5, .5) sim_res = wfm([0], [0], wd=270, ws=10, yaw=[[[yaw]]]) fm = sim_res.flow_map(XYGrid(x=np.arange(-100, 500, 5))) npt.assert_almost_equal(fm.WS_eff.min(), min_ws) fm.plot_wake_map(ax=ax, levels=levels) fm.min_WS_eff(fm.x, 70).plot(ax=ax, color='r') plt.axhline(0, color='k') plot(wfm_UL, 0, ax1, 7.15853738) plot(wfm_UL, 30, ax2, 7.83219266) plot(wfm_ULT, 0, ax3, 7.15853738) plot(wfm_ULT, 30, ax4, 8.12261872) if 0: plt.show() plt.close('all')
def test_fuga_downwind_vs_notebook(): powerCtFunction = PowerCtTabular([0, 100], [0, 0], 'w', [0.850877, 0.850877]) wt = WindTurbine(name='', diameter=80, hub_height=70, powerCtFunction=powerCtFunction) path = tfp + 'fuga/2MW/Z0=0.00001000Zi=00400Zeta0=0.00E+00' site = UniformSite([1, 0, 0, 0], ti=0.075) wfm_ULT = PropagateDownwind(site, wt, FugaYawDeficit(path)) WS = 10 p = Path(tfp) / "fuga/v80_wake_4d_y_no_deflection.csv" y, notebook_deficit_4d = np.array( [v.split(",") for v in p.read_text().strip().split("\n")], dtype=float).T sim_res = wfm_ULT([0], [0], wd=270, ws=WS, yaw=[[[17.4493]]]) fm = sim_res.flow_map(XYGrid(4 * wt.diameter(), y=y)) npt.assert_allclose(fm.WS_eff.squeeze() - WS, notebook_deficit_4d, atol=1e-6) if 0: plt.plot(y, notebook_deficit_4d, label='Notebook deficit 4d') plt.plot(y, fm.WS_eff.squeeze() - WS) plt.show() plt.close('all')
def test_GCL_ex80(): site = Hornsrev1Site() x, y = site.initial_position.T windTurbines = V80() wfm = PropagateDownwind(site, windTurbines, wake_deficitModel=GCLDeficit(), superpositionModel=LinearSum()) if 0: windTurbines.plot(x, y) plt.show() sim_res = timeit(wfm.__call__, line_profile=0, profile_funcs=[get_dU], verbose=0)(x, y, ws=np.arange(10, 15))[0] # test that the result is equal to previuos runs (no evidens that these number are correct) aep_ref = 1055.956615887197 npt.assert_almost_equal( sim_res.aep_ilk(normalize_probabilities=True).sum(), aep_ref, 5) sim_res = wfm(x, y, ws=np.arange(3, 10)) npt.assert_array_almost_equal( sim_res.aep_ilk(normalize_probabilities=True).sum(), 261.6143039016946, 5)
def test_IEA37_ex16(deficitModel, aep_ref): site = IEA37Site(16) x, y = site.initial_position.T windTurbines = IEA37_WindTurbines() wf_model = PropagateDownwind(site, windTurbines, wake_deficitModel=deficitModel, superpositionModel=SquaredSum(), turbulenceModel=GCLTurbulence()) aep_ilk = wf_model(x, y, wd=np.arange(0, 360, 22.5), ws=[9.8]).aep_ilk(normalize_probabilities=True) aep_MW_l = aep_ilk.sum((0, 2)) * 1000 # check if ref is reasonable aep_est = 16 * 3.35 * 24 * 365 * .8 # n_wt * P_rated * hours_pr_year - 20% wake loss = 375628.8 npt.assert_allclose(aep_ref[0], aep_est, rtol=.11) npt.assert_allclose(aep_ref[1], [ 9500, 8700, 11500, 14300, 21300, 25900, 39600, 44300, 23900, 13900, 15200, 33000, 72100, 18300, 12500, 8000 ], rtol=.15) npt.assert_almost_equal(aep_MW_l.sum(), aep_ref[0], 5) npt.assert_array_almost_equal(aep_MW_l, aep_ref[1], 5)
def test_wake_radius_not_implemented(): site = IEA37Site(16) x, y = site.initial_position.T windTurbines = IEA37_WindTurbines() wfm = PropagateDownwind(site, windTurbines, wake_deficitModel=NoWakeDeficit(), turbulenceModel=GCLTurbulence()) with pytest.raises(NotImplementedError, match="wake_radius not implemented for NoWakeDeficit"): wfm(x, y)
def __init__(self, LUT_path, site, windTurbines, deflectionModel=None, turbulenceModel=None): """ Parameters ---------- LUT_path : str path to look up tables site : Site Site object windTurbines : WindTurbines WindTurbines object representing the wake generating wind turbines deflectionModel : DeflectionModel Model describing the deflection of the wake due to yaw misalignment, sheared inflow, etc. turbulenceModel : TurbulenceModel Model describing the amount of added turbulence in the wake """ PropagateDownwind.__init__(self, site, windTurbines, wake_deficitModel=FugaDeficit(LUT_path), superpositionModel=LinearSum(), deflectionModel=deflectionModel, turbulenceModel=turbulenceModel)
def test_huge_flow_map(wake_deficitModel, deflectionModel, superpositionModel): site = IEA37Site(16) windTurbines = IEA37_WindTurbines() wake_model = PropagateDownwind(site, windTurbines, wake_deficitModel=wake_deficitModel, superpositionModel=superpositionModel, deflectionModel=deflectionModel, turbulenceModel=STF2005TurbulenceModel()) n_wt = 2 flow_map = wake_model(*site.initial_position[:n_wt].T, wd=0).flow_map(HorizontalGrid(resolution=1000)) # check that deficit matrix > 10MB (i.e. it enters the memory saving loop) assert (np.prod(flow_map.WS_eff_xylk.shape) * n_wt * 8 / 1024**2) > 10 assert flow_map.WS_eff_xylk.shape == (1000, 1000, 1, 1)