Scipy 中的优化

Posted

技术标签:

【中文标题】Scipy 中的优化【英文标题】:Optimization in Scipy 【发布时间】:2022-01-11 00:43:52 【问题描述】:

我想在下面的代码中添加一些约束,我想在其中使用 scipy 优化输出。

    """
References:
    https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html
    https://github.com/DTUWindEnergy/PyWake
"""


import time

from py_wake.examples.data.hornsrev1 import V80 
from py_wake.examples.data.hornsrev1 import Hornsrev1Site # We work with the Horns Rev 1 site, which comes already set up with PyWake.
from py_wake import BastankhahGaussian

from scipy.optimize import minimize
import numpy as np


def funC(x, y, c):
    """
    Turns on/off the use of wind turbine depending on the value of c.
    scipy generates c real values in the range [0, 1] as specified by the bounds including 0.2 etc.
    If c is 0.5 or more turbine will be used otherwise turbine will not be used.
    """
    x_selected = x[c >= 0.5]
    y_selected = y[c >= 0.5]
    
    return (x_selected, y_selected)


def wt_simulation(c):
    """
    This is our objective function. It will return the aep=annual energy production in GWh.
    We will maximize aep.
    """
    site = Hornsrev1Site()
    x, y = site.initial_position.T
    windTurbines = V80()
    
    wf_model = BastankhahGaussian(site, windTurbines)
    x_new, y_new = funC(x, y, c)

    # run wind farm simulation
    sim_res = wf_model(
        x_new, y_new, # wind turbine positions
        h=None, # wind turbine heights (defaults to the heights defined in windTurbines)
        type=0, # Wind turbine types
        wd=None, # Wind direction (defaults to site.default_wd (0,1,...,360 if not overriden))
        ws=None, # Wind speed (defaults to site.default_ws (3,4,...,25m/s if not overriden))
    )
    
    aep_output = sim_res.aep().sum()  # we maximize aep
    
    return -float(aep_output)  # negate because of scipy minimize


def solve():
    t0 = time.perf_counter()
    
    wt = 80  # for V80

    x0 = np.ones(wt)  # initial value
    bounds = [(0, 1) for _ in range(wt)]

    res = minimize(wt_simulation, x0=x0, bounds=bounds)
    
    print(f'success status: res.success')
    print(f'aep: -res.fun')  # negate to get the true maximum aep
    print(f'c values: res.x\n')

    print(f'elapse: round(time.perf_counter() - t0)s')  


# start
solve()

现在我想添加一个约束,其中湍流强度:sim_res_TI_eff 每个风力涡轮机(wt)的每个风速(was)和每个风向(wd)必须低于某个值(例如 0.2 )。我必须添加 sim_res.TI_eff.sel(wt=1) 例如给出每个 wd 的 TI,并且是风力涡轮机 #1。问题是我需要使用函数 wt_simulation ,其中我有另一个必须优化的返回,所以我不知道如何返回不受优化影响的 TI。

【问题讨论】:

这个ti_eff只有在仿真后才可用。我们不能在模拟之前输入 ti_eff。我们的目标是最大 aep。在我看来,scipy 无法做到这一点。但是 optuna 超参数调谐器可以。我们的想法是我们首先模拟,然后获取所有涡轮机的 ti_eff,如果甚至有一个涡轮机的 ti_eff 为 0.2 及以上,我们告诉 optuna aep 为零,这样它将尝试找到所有涡轮机的最大 aep,其中 ti_eff低于 0.2。 如果您在函数评估中有模拟步骤,则需要将求解器限制为“无导数优化”(DFO)类。进行有限差分不是一个好主意。 @ferdy 所以你的意思是我必须使用 Optuna 来处理这种类型的约束?如果是,则在模拟后的每次迭代中,如果 TI 大于 0.2,则使该 wt=0 的 aep 因此该特定 wt 的 c 必须更改为 0,它将作为下一次迭代的初始猜测( c(wt=i)=0)。但是 optuna 可以这样做吗? @ErwinKalvelagen 你能解释一下吗?因为我没有太多的编程和优化经验,所以有些概念是新的 使用模拟你不会得到渐变。大多数非线性求解器依赖于梯度。如果不可用,则可以使用有限差分来近似它们。这假设函数评估很便宜。在模拟的情况下,情况并非如此。所以 DFO 求解器是可以使用的求解器类型:它们不需要梯度,也不做有限差分。一个很好的参考是:link.springer.com/article/10.1007%252Fs10898-012-9951-y。我不完全理解你的约束。首先制定一个数学模型有很大帮助。 【参考方案1】:

这是处理 ti_eff 的一种方法。

"""
References:
    https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html
    https://github.com/DTUWindEnergy/PyWake
"""


import time

from py_wake.examples.data.hornsrev1 import V80 
from py_wake.examples.data.hornsrev1 import Hornsrev1Site # We work with the Horns Rev 1 site, which comes already set up with PyWake.
from py_wake import BastankhahGaussian

from scipy.optimize import minimize
import numpy as np


TIEFF_THRESHOLD = 0.2


def ok_tieff_constraint(tieff):
    """
    Returns True if tieff is below threshold otherwise False.
    """
    if np.any(tieff >= TIEFF_THRESHOLD):
        return False
    return True


def funC(x, y, c):
    """
    Turns on/off the use of wind turbine depending on the value of c.
    scipy generates c real values in the range [0, 1] as specified by the bounds including 0.2 etc.
    If c is 0.5 or more turbine will be used otherwise turbine will not be used.
    """
    x_selected = x[c >= 0.5]
    y_selected = y[c >= 0.5]
    
    return (x_selected, y_selected)


def wt_simulation(c):
    """
    This is our objective function. It will return the aep=annual energy production in GWh.
    We will maximize aep.
    """
    islogging = True
    site = Hornsrev1Site()
    x, y = site.initial_position.T
    windTurbines = V80()
    
    wf_model = BastankhahGaussian(site, windTurbines)
    x_new, y_new = funC(x, y, c)

    # run wind farm simulation
    sim_res = wf_model(
        x_new, y_new, # wind turbine positions
        h=None, # wind turbine heights (defaults to the heights defined in windTurbines)
        type=0, # Wind turbine types
        wd=None, # Wind direction (defaults to site.default_wd (0,1,...,360 if not overriden))
        ws=None, # Wind speed (defaults to site.default_ws (3,4,...,25m/s if not overriden))
    )
    
    if islogging:
        print(sim_res)
    
    aep_output = float(sim_res.aep().sum())  # we maximize aep
    
    # Constraint on ti_eff, if constraint is violated we set aep to zero.
    if not ok_tieff_constraint(sim_res.TI_eff):
        aep_output = 0
    
    return -aep_output  # negate because of scipy minimize


def solve():
    t0 = time.perf_counter()
    
    wt = 80  # for V80

    x0 = np.ones(wt)  # initial value
    bounds = [(0, 1) for _ in range(wt)]

    res = minimize(wt_simulation, x0=x0, bounds=bounds)
    
    print(f'success status: res.success')
    print(f'aep: -res.fun')  # negate to get the true maximum aep
    print(f'c values: res.x\n')

    print(f'elapse: round(time.perf_counter() - t0)s')  


# start
solve()

输出:

...

<xarray.SimulationResult>
Dimensions:           (wt: 80, wd: 360, ws: 23)
Coordinates:
  * wt                (wt) int32 0 1 2 3 4 5 6 7 8 ... 72 73 74 75 76 77 78 79
  * wd                (wd) int32 0 1 2 3 4 5 6 7 ... 353 354 355 356 357 358 359
  * ws                (ws) int32 3 4 5 6 7 8 9 10 11 ... 18 19 20 21 22 23 24 25
    x                 (wt) float64 4.24e+05 4.24e+05 ... 4.294e+05 4.295e+05
    y                 (wt) float64 6.151e+06 6.151e+06 ... 6.148e+06 6.148e+06
    h                 (wt) float64 70.0 70.0 70.0 70.0 ... 70.0 70.0 70.0 70.0
    type              (wt) int32 0 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0 0
Data variables: (12/15)
    WS_eff            (wt, wd, ws) float64 3.0 4.0 5.0 6.0 ... 22.87 23.88 24.89
    TI_eff            (wt, wd, ws) float64 0.1 0.1 0.1 0.1 ... 0.1 0.1 0.1 0.1
    Power             (wt, wd, ws) float64 0.0 6.66e+04 1.54e+05 ... 2e+06 2e+06
    CT                (wt, wd, ws) float64 0.0 0.818 0.806 ... 0.06084 0.05377
    WS                (ws) int32 3 4 5 6 7 8 9 10 11 ... 18 19 20 21 22 23 24 25
    WD                (wd) int32 0 1 2 3 4 5 6 7 ... 353 354 355 356 357 358 359
    ...                ...
    Weibull_A         (wd) float64 9.177 9.177 9.177 9.177 ... 9.177 9.177 9.177
    Weibull_k         (wd) float64 2.393 2.393 2.393 2.393 ... 2.393 2.393 2.393
    Sector_frequency  (wd) float64 0.001199 0.001199 ... 0.001199 0.001199
    P                 (wd, ws) float64 6.147e-05 8.559e-05 ... 2.193e-08
    tilt              (wt, wd, ws) float64 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0
    yaw               (wt, wd, ws) float64 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0
Attributes:
    wd_bin_size:  1
success status: True
aep: 682.0407252944838
c values: [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.
 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.
 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.
 1. 1. 1. 1. 1. 1. 1. 1.]

elapse: 273s

【讨论】:

以上是关于Scipy 中的优化的主要内容,如果未能解决你的问题,请参考以下文章

数学工具scipy中的优化方法

SciPy 科学计算基础

Scipy优化算法--scipy.optimize.fmin_tnc()/minimize()

python中的离散优化

scipy 中的旅行推销员

使用 Scipy 在 Python 中进行约束优化