Skip to content
Snippets Groups Projects
test_deficit_models.py 16.6 KiB
Newer Older
Mads M. Pedersen's avatar
Mads M. Pedersen committed
from builtins import issubclass
import inspect
import os
import pkgutil

import pytest

import matplotlib.pyplot as plt
Mads M. Pedersen's avatar
Mads M. Pedersen committed
from py_wake import deficit_models
from py_wake.deficit_models.deficit_model import DeficitModel, ConvectionDeficitModel
from py_wake.deficit_models.fuga import FugaDeficit, Fuga
from py_wake.deficit_models.gaussian import BastankhahGaussianDeficit, IEA37SimpleBastankhahGaussianDeficit,\
    ZongGaussianDeficit, NiayifarGaussianDeficit, BastankhahGaussian, IEA37SimpleBastankhahGaussian, ZongGaussian,\
    NiayifarGaussian
from py_wake.deficit_models.gcl import GCLDeficit, GCL
from py_wake.deficit_models.no_wake import NoWakeDeficit
from py_wake.deficit_models.noj import NOJDeficit, NOJ
from py_wake.examples.data.iea37 import iea37_path
from py_wake.examples.data.iea37._iea37 import IEA37_WindTurbines, IEA37Site
from py_wake.examples.data.iea37.iea37_reader import read_iea37_windfarm
from py_wake.flow_map import HorizontalGrid
from py_wake.rotor_avg_models.rotor_avg_model import RotorAvgModel
Mads M. Pedersen's avatar
Mads M. Pedersen committed
from py_wake.superposition_models import SquaredSum, WeightedSum
from py_wake.tests import npt
from py_wake.tests.test_files import tfp
from py_wake.turbulence_models.gcl import GCLTurbulence
from py_wake.wind_farm_models.engineering_models import PropagateDownwind


def get_all_deficit_models():

    all_deficit_modules = []
    for loader, module_name, _ in pkgutil.walk_packages([os.path.dirname(deficit_models.__file__)]):

        _module = loader.find_module(module_name).load_module(module_name)
        for n in dir(_module):
            v = _module.__dict__[n]
            if inspect.isclass(v) and issubclass(v, DeficitModel) and \
Mads M. Pedersen's avatar
Mads M. Pedersen committed
                    v not in [DeficitModel, ConvectionDeficitModel] and not issubclass(v, RotorAvgModel):
                all_deficit_modules.append(v())
    return all_deficit_modules


@pytest.mark.parametrize(
    'deficitModel,aep_ref',
    # test that the result is equal to last run (no evidens that  these number are correct)
    [(NOJDeficit(), (367205.0846866496, [9833.86287, 8416.99088, 10820.37673, 13976.26422, 22169.66036,
                                         25234.9215, 37311.64388, 42786.37028, 24781.33444, 13539.82115,
                                         14285.22744, 31751.29488, 75140.15677, 17597.10319, 11721.21226,
                                         7838.84383])),
     (BastankhahGaussianDeficit(), (355971.9717035484,
                                    [9143.74048, 8156.71681, 11311.92915, 13955.06316, 19807.65346,
                                        25196.64182, 39006.65223, 41463.31044, 23042.22602, 12978.30551,
                                        14899.26913, 32320.21637, 67039.04091, 17912.40907, 12225.04134,
                                        7513.75582])),
     (IEA37SimpleBastankhahGaussianDeficit(), read_iea37_windfarm(iea37_path + 'iea37-ex16.yaml')[2]),
     (FugaDeficit(LUT_path=tfp + 'fuga/2MW/Z0=0.00014617Zi=00399Zeta0=0.00E+0/'),
      (398833.4594353128, [9630.32321, 9731.27952, 12459.23261, 15329.04663, 22194.04385,
                           27677.4453, 42962.87107, 49467.33757, 24268.41448, 15412.81128,
                           16675.27526, 35497.95388, 75248.37632, 19673.5648, 13682.27714,
                           8923.20653])),
Mads M. Pedersen's avatar
Mads M. Pedersen committed
     (GCLDeficit(), (370863.6246093183,
                     [9385.75387, 8768.52105, 11450.13309, 14262.42186, 21178.74926,
                      25751.59502, 39483.21753, 44573.31533, 23652.09976, 13924.58752,
                      15106.11692, 32840.02909, 71830.22035, 18200.49805, 12394.7626,
Mads M. Pedersen's avatar
Mads M. Pedersen committed
                      8061.6033])),
     (ZongGaussianDeficit(),
      (354263.1062694012, [8960.58672, 8095.48341, 11328.51572, 13924.50408, 19938.78428,
                           25141.4657, 39063.84731, 41152.04065, 22580.67854, 12944.55424,
                           14778.95385, 32294.41749, 66540.62665, 17898.1109, 12126.32111,
                           7494.21561])),
     (NiayifarGaussianDeficit(),
      (362228.4003016239, [9210.01349, 8330.06642, 11392.04851, 14082.13366, 20643.45247,
                           25426.07466, 39282.9259, 42344.50431, 23209.23399, 13390.08421,
                           14807.66824, 32535.89096, 69640.3283, 18031.93957, 12149.88163,
                           7752.15401])),

     ])
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,
Mads M. Pedersen's avatar
Mads M. Pedersen committed
                                 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=.1)
    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)


@pytest.mark.parametrize(
    'deficitModel,ref',
    # test that the result is equal to last run (no evidens that  these number are correct)
    [(NOJDeficit(),
      [3.27, 3.27, 9.0, 7.46, 7.46, 7.46, 7.46, 7.31, 7.31, 7.31, 7.31, 8.3, 8.3, 8.3, 8.3, 8.3, 8.3]),
     (BastankhahGaussianDeficit(),
      [0.18, 3.6, 7.27, 8.32, 7.61, 6.64, 5.96, 6.04, 6.8, 7.69, 8.08, 7.87, 7.59, 7.46, 7.55, 7.84, 8.19]),
     (IEA37SimpleBastankhahGaussianDeficit(),
      [3.32, 4.86, 7.0, 8.1, 7.8, 7.23, 6.86, 6.9, 7.3, 7.82, 8.11, 8.04, 7.87, 7.79, 7.85, 8.04, 8.28]),
     (FugaDeficit(LUT_path=tfp + 'fuga/2MW/Z0=0.00014617Zi=00399Zeta0=0.00E+0/'),
      [6.91, 7.87, 8.77, 8.88, 8.55, 7.88, 7.24, 7.32, 8.01, 8.62, 8.72, 8.42, 8.05, 7.85, 8., 8.37, 8.69]),
Mads M. Pedersen's avatar
Mads M. Pedersen committed
     (GCLDeficit(),
Mads M. Pedersen's avatar
Mads M. Pedersen committed
      [2.39, 5.01, 7.74, 8.34, 7.95, 7.58, 7.29, 7.32, 7.61, 7.92, 8.11, 8.09, 7.95, 7.83, 7.92, 8.1, 8.3]),
     (ZongGaussianDeficit(),
      [6.34, 7.08, 8.09, 8.36, 7.5, 6.2, 5.23, 5.34, 6.43, 7.64, 8.08, 7.75, 7.36, 7.19, 7.32, 7.69, 8.14]),
     (NiayifarGaussianDeficit(),
      [0.18, 3.6, 7.27, 8.32, 7.61, 6.64, 5.97, 6.04, 6.8, 7.69, 8.08, 7.87, 7.59, 7.46, 7.56, 7.84, 8.19]),
     ])
def test_deficitModel_wake_map(deficitModel, ref):
    site = IEA37Site(16)
    x, y = site.initial_position.T
    windTurbines = IEA37_WindTurbines()

    wf_model = PropagateDownwind(site, windTurbines, wake_deficitModel=deficitModel, superpositionModel=SquaredSum(),
Mads M. Pedersen's avatar
Mads M. Pedersen committed
                                 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])
        plt.plot(ref, label='ref')
        plt.plot(mean_ref, label='Mean ref')
        plt.legend()

    # 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)


@pytest.mark.parametrize(
    # test that the result is equal to last run (no evidens that  these number are correct)
    [(NOJDeficit(), [100., 75., 150., 100., 100.]),
     (BastankhahGaussianDeficit(),
      [83.336286, 57.895893, 115.791786, 75.266662, 83.336286]),
     (IEA37SimpleBastankhahGaussianDeficit(),
      [83.336286, 57.895893, 115.791786, 75.266662, 83.336286]),
     (FugaDeficit(LUT_path=tfp + 'fuga/2MW/Z0=0.00014617Zi=00399Zeta0=0.00E+0/'),
      [100, 50, 100, 100, 100]),
Mads M. Pedersen's avatar
Mads M. Pedersen committed
     (GCLDeficit(),
Mads M. Pedersen's avatar
Mads M. Pedersen committed
      [156.949964, 97.763333, 195.526667, 113.225695, 111.340236]),
     (ZongGaussianDeficit(), [91.15734, 66.228381, 132.456762, 94.90156, 79.198215]),
     (NiayifarGaussianDeficit(), [92.880786, 67.440393, 134.880786, 84.811162, 73.880786]),
     ])
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()
Mads M. Pedersen's avatar
Mads M. Pedersen committed
    wfm = PropagateDownwind(site, windTurbines, wake_deficitModel=deficitModel, turbulenceModel=GCLTurbulence())
    wfm(x=[0, 500], y=[0, 0], wd=[30], ws=[10])


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(),
Mads M. Pedersen's avatar
Mads M. Pedersen committed
                            turbulenceModel=GCLTurbulence())
    with pytest.raises(NotImplementedError, match="wake_radius not implemented for NoWakeDeficit"):
        wfm(x, y)
Mads M. Pedersen's avatar
Mads M. Pedersen committed


@pytest.mark.parametrize(
    'deficitModel,aep_ref',
    # test that the result is equal to last run (no evidens that  these number are correct)
    [(BastankhahGaussianDeficit(), (338848.4232271521,
                                    [8618.62236, 7615.23967, 10959.13371, 13390.20853, 19094.25425,
                                     24176.7654, 37790.11624, 38710.80165, 21718.92835, 12368.03698,
                                     14319.69596, 30618.7735, 63588.46932, 16969.44073, 11749.49412,
                                     7160.44246])),
     (ZongGaussianDeficit(),
      (336631.72472958744, [8459.16014, 7605.17823, 11029.97614, 13381.64219, 18550.00538,
                            24161.2984, 38034.40047, 38659.656, 21317.08356, 12322.54712,
                            14244.59289, 30532.23574, 62590.49112, 16921.48005, 11687.87109,
                            7134.10623])),
     (NiayifarGaussianDeficit(),
      (342508.81971496233, [8689.19059, 7822.04451, 11019.98716, 13481.81082, 19141.69434,
                            24342.15843, 37999.95572, 39762.05958, 21896.76029, 12703.77497,
                            14218.12659, 30742.83051, 64629.25874, 17038.19522, 11666.15515,
                            7354.81709])),

     ])
def test_IEA37_ex16_convection(deficitModel, aep_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())

    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)


@pytest.mark.parametrize(
    'deficitModel,ref',
    # test that the result is equal to last run (no evidens that  these number are correct)
    [(BastankhahGaussianDeficit(),
      [3.25, 5.43, 7.66, 8.15, 7.48, 6.45, 5.69, 5.7, 6.38, 7.15, 7.54, 7.49, 7.27, 7.16, 7.28, 7.61, 8.04]),
     (ZongGaussianDeficit(),
      [6.22, 6.97, 7.91, 8.16, 7.5, 6.3, 5.35, 5.4, 6.32, 7.29, 7.67, 7.51, 7.17, 7., 7.14, 7.55, 8.05]),
     (NiayifarGaussianDeficit(),
      [3.29, 5.45, 7.67, 8.15, 7.49, 6.47, 5.72, 5.72, 6.4, 7.17, 7.55, 7.5, 7.29, 7.17, 7.29, 7.62, 8.05]),
     ])
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)


@pytest.mark.parametrize(
    'windFarmModel,aep_ref',
    # test that the result is equal to last run (no evidens that  these number are correct)
    [(NOJ,
      (367205.0846866496, [9833.86287, 8416.99088, 10820.37673, 13976.26422, 22169.66036,
                           25234.9215, 37311.64388, 42786.37028, 24781.33444, 13539.82115,
                           14285.22744, 31751.29488, 75140.15677, 17597.10319, 11721.21226,
                           7838.84383])),
     (BastankhahGaussian, (355971.9717035484,
                           [9143.74048, 8156.71681, 11311.92915, 13955.06316, 19807.65346,
                            25196.64182, 39006.65223, 41463.31044, 23042.22602, 12978.30551,
                            14899.26913, 32320.21637, 67039.04091, 17912.40907, 12225.04134,
                            7513.75582])),
     (IEA37SimpleBastankhahGaussian, read_iea37_windfarm(iea37_path + 'iea37-ex16.yaml')[2]),
     (lambda *args, **kwargs: Fuga(tfp + 'fuga/2MW/Z0=0.00014617Zi=00399Zeta0=0.00E+0/', *args, **kwargs),
      (398833.4594353128, [9630.32321, 9731.27952, 12459.23261, 15329.04663, 22194.04385,
                           27677.4453, 42962.87107, 49467.33757, 24268.41448, 15412.81128,
                           16675.27526, 35497.95388, 75248.37632, 19673.5648, 13682.27714,
                           8923.20653])),
     (GCL, (370863.6246093183,
            [9385.75387, 8768.52105, 11450.13309, 14262.42186, 21178.74926,
             25751.59502, 39483.21753, 44573.31533, 23652.09976, 13924.58752,
             15106.11692, 32840.02909, 71830.22035, 18200.49805, 12394.7626,
             8061.6033])),
     (ZongGaussian,
      (354263.1062694012, [8960.58672, 8095.48341, 11328.51572, 13924.50408, 19938.78428,
                           25141.4657, 39063.84731, 41152.04065, 22580.67854, 12944.55424,
                           14778.95385, 32294.41749, 66540.62665, 17898.1109, 12126.32111,
                           7494.21561])),
     (NiayifarGaussian,
      (362228.4003016239, [9210.01349, 8330.06642, 11392.04851, 14082.13366, 20643.45247,
                           25426.07466, 39282.9259, 42344.50431, 23209.23399, 13390.08421,
                           14807.66824, 32535.89096, 69640.3283, 18031.93957, 12149.88163,
                           7752.15401])),
     ])
def test_IEA37_ex16_windFarmModel(windFarmModel, aep_ref):
    site = IEA37Site(16)
    x, y = site.initial_position.T
    windTurbines = IEA37_WindTurbines()
    wf_model = windFarmModel(site, windTurbines, turbulenceModel=GCLTurbulence())
    wf_model.superpositionModel = SquaredSum()

    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=.1)
    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)