scipy.optimize с несколькими границами, ограничениями и непрерывными полями - PullRequest
0 голосов
/ 12 октября 2018

Я хочу оптимизировать работу ТЭЦ по требуемому профилю мощности.Поэтому я определяю профиль мощности, которому должна следовать ТЭЦ.Для представления реалистичной работы ТЭЦ необходимо применить несколько границ и ограничений.Это, например, включает в себя то, что CHP может включаться или выключаться, а при включении его модуляция мощности может быть установлена ​​только на определенный процентный диапазон.

Вот минимальный рабочий пример с краткими пояснениями:

import scipy.optimize as opt
import numpy as np

x = np.arange(200)  # dummy x vector
poly_profile = np.array(  # 7th degree polynome fit of profile
    [-2.14104340e-11,  1.85108903e-08, -6.66697810e-06,  1.29239710e-03,
     -1.45110876e-01,  9.40324129e+00, -3.24548750e+02,  4.60006330e+03])
poly_fun = np.poly1d(poly_profile)  # make poly fun
profile = poly_fun(x[65:196])
x0 = np.zeros_like(profile)  # all zeros as starting values

def optifun(x, profile):  # define minimization fun
    return - np.sum(profile * x)

bnds_hi = opt.Bounds(0.3, 1)  # upper bounds
bnds_lo = opt.Bounds(0, 0)  # lower bounds

res = opt.minimize(
    optifun, x0, args=(profile), bounds=bnds_hi,
    constraints={'type': 'eq', 'fun': lambda x:  np.sum(x*40) - 2000},
    method='SLSQP')
plt.plot(res.x)
plt.plot(profile)

Итак, вот границы, которые я хочу использовать:

  • (x == 0) or (0.3 <= x <= 1), для любого значения в массиве x
    Это означает, что x, степень модуляцииОбщая мощность ТЭЦ может быть 0 (выключено) или >0.3 и <= 1.Но я могу либо указать нижние границы ИЛИ верхние границы.Только указание верхних границ делает невозможным «выключение ТЭЦ», в то время как установка нижних границ на bnds_lo = opt.Bounds(0, 1)
    позволит ТЭЦ работать в нереалистичной рабочей точке (от 0% до 30% отмодуляция мощности).
    Есть ли способ заставить эту работу работать с границами, указанными в минимальном рабочем примере?В частности: Могу ли я установить два вида границ одновременно, например bounds=[bnds_lo, bnds_hi]?
    Полагаю, это смешанная целочисленная задача линейного программирования, но разве COBYLA или SLSQP не должны справиться с этим?Если нет: есть ли обходной путь?

И ограничения, которые я хочу использовать:

  • np.sum(x*40) - 450
    Ограничивает тепловую мощность до некоторой емкости накопления тепла.Здесь 40 - это тепловая мощность, а 450 - оставшаяся емкость.Это довольно просто реализовать.
  • Ограничить количество пусков ТЭЦ.В качестве примера давайте предположим,

    bnds_lo = opt.Bounds(0, 1)  # lower bounds
    res = opt.minimize(
        optifun, x0, args=(profile), bounds=bnds_lo,
        constraints={'type': 'eq', 'fun': lambda x:  np.sum(x*40) - 1000},
        method='SLSQP')
    

    Это приводит к 3 периодам работы ТЭЦ.Есть ли способ ограничить это?Я думал о добавлении определенной функции ограничений, которая подсчитывает положительные различия после 0, но я не смог сделать что-то подобное (например, поскольку большинство x не совсем 0, поскольку границы установлены на (0, 1). Но причиной могут быть и другие проблемы) ...

  • Установите минимальное время последовательной работы ТЭЦ.Это означает, что было бы выгодно иметь хотя бы, скажем, 5 подряд x != 0.Я думал о том, чтобы попробовать нечто подобное, как в моем последнем пункте (ограничение количества пусков), но также не смог выработать что-то полезное.Это, безусловно, наименее важная проблема.

Для решения этих проблем я также пытался использовать scipy.optimize.LinearConstraings и NonlinearConstraings Но method='trust-constr' требует jac (насколько я читал на github, это кажетсябыть ошибкой) и поэтому я не смог заставить его работать.

Есть ли способ, которым я могу заставить эту работу?Особенно важно указать несколько границ.

Заранее спасибо!

С уважением, Скотти

1 Ответ

0 голосов
/ 12 ноября 2018

profile * x0 в вашем коде дает
"ValueError: операнды не могут быть переданы вместе с фигурами (131,) (200,)".

Просто угадайте, является ли x_t продуктом onoff_t * xon_t
с onoff_t = 0 или 1
и 0.3 <= xon_t <= 1 на каждом t в 0 .. T?
Т.е.для T = 5 существует 2 ^ 5 возможных onoff последовательностей, 00000 00001 00010 .. 11111?

Если это так, maximizng sum 0:T w_t * onoff_t * xon_t с функцией фиксированного веса w_t тривиален:
гдеw_t <= 0: onoff_t = 0, выкл.
, где w_t > 0: onoff_t = 1, вкл. И xon_t = 1, макс.
Так что это не может быть ваш вопрос - уточните.

Если onoff_t дополнительно вынужден переключаться только дважды, 0 ... 1 ... 0 ..., то число возможных последовательностей достаточно мало, чтобы просто попробовать их все по строкам:

def pulse_generator( T=200, minwidth=5 ):
    """ -> arrays of T floats, 0... 1... 0... """
    for t0 in xrange( 1, T ):
        for t1 in xrange( t0 + minwidth, T ):
            pulse = np.zeros( T )
            pulse[t0:t1] = 1
            yield pulse

for pulse in pulse_generator( T ):
    print "pulse:", pulse
    optimize myfunction( pulse * xon ), 0.3 <= xon <= 1

Переключение 4 раза, 0 ... 1 ... 0 ... 1 ... 0 ..., аналогично.(Сколько таких импульсов существует для данного T? См. Википедию Звезды и столбцы - удивительно.)


Добавлено: Я не эксперт, но неt-on aka управление bang-bang очень чувствительно к малым изменениям, немного раньше или позже?Программа (mer) может тратить много времени на сглаживание, подавляя шум.Как насчет двух фаз: грубая-сетка, а затем точная-сетка -
  1. разделить время 0: T, скажем, на 10 частей, выполнить все 2 ^ 10 = 1024 последовательностей включения-выключения
    1a.присмотритесь к лучшим из них - какой-нибудь шаблон?
  2. переместите их края на полшага, T / 20.

См. также: google "дискретная оптимизация" multigrid ... и Поиск по сетке .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...