Сообщество Engee

Оптимизируем параметры трубопровода

作者
avatar-nkapyrinnkapyrin
Notebook

Оптимизация геометрии трубопровода

В этом проекте мы оптимизируем параметры геометрии трубопровода для достижения нужных параметров потока.

Рассмотрим простую модель, где в трубопроводе происходит Т-образное разветвление, после которого идут две изогнутые трубы. Меняя их угол изгиба в диапазоне от 0 до 120 градусов мы будем добиваться заданной разницы расходов через каждое ответвление системы.

Описание модели

Загрузим несколько библиотек:

In [ ]:
Pkg.add("Optim")
using DataFrames, CSV, Optim

Откроем нашу модель.

In [ ]:
engee.open( "$(@__DIR__)/" * "pipe_circuit_optimization.engee");
image.png

Когда изгиб обеих труб одинаковый, через каждую трубу идет одинаковый расход жидкости, равный половине входного потока.

Оптимизация параметров модели

Сформулируем задачу довольно простым образом: мы заранее запустили модель, в которой изгиб труб был равен 30 и 60 градусов. Полученные значения выставим в качестве целевых критериев оптимизации (расход 150.6 и 136.6 кг/с) и установим начальные значения изгиба каждой трубы равными 90 градусов.

Задача оптимизации потребует определения свободных переменных ("независимых", значения которых мы будем постепенно менять) и зависимых переменных (с целевыми значениями).

Свободные переменные – это некоторые внутренние свойства некоторых блоков с целевыми значениями и физическими ограничениями.

In [ ]:
cd( @__DIR__ )
adj = CSV.read("adjustable_parameters.csv", DataFrame)
Out[0]:
2×6 DataFrame
Rowblocksparametersunitsx0lowerupper
StringString15String3Float64Float64Float64
1pipe_circuit_optimization/Изгиб трубы (ИЖ)bend_angledeg90.00.0120.0
2pipe_circuit_optimization/Изгиб трубы (ИЖ)-1bend_angledeg90.00.0120.0

Зависимые переменные – это желаемые значения некоторых сигналов в финальный момент времени.

In [ ]:
tgt = CSV.read("target_parameters.csv", DataFrame)
Out[0]:
2×2 DataFrame
Rownamesvalues
String7Float64
1Outlet1150.6
2Outlet2136.6

Запустим процесс оптимизации, в ходе которого мы будем искать значения свободных параметров adj (adjustable), позволяющие добиться нужных значений зависимых параметров tgt (target).

In [ ]:
using Optim

function f(x)
    # Выставим значения параметров блока (свободные переменные)
    for (i, (block, param, unit, _)) in enumerate(eachrow(adj))
        engee.set_param!( String(block), String(param) => Dict("value" => string(x[i]), "unit" => String(unit)) )
    end
    # Выполним модель с новыми параметрами
    data = engee.run("pipe_circuit_optimization");
    # Получим значения зависимых переменных в последний момент времени
    out_vector = [data[String(name)].value[end] for name in tgt.names]
    # Вычислим критерий оптимизации
    return sum((out_vector .- tgt.values) .^ 2)
end

# Оптимизатор граничных условий
inner_optimizer = GradientDescent()

# Общие настройки процесса оптимизации
options = Optim.Options(iterations = 4, outer_iterations = 4,
                        x_abstol = 5.0, outer_x_abstol = 5.0,
                        store_trace=true, trace_simplex=true, extended_trace=true )

# Запускаем оптимизацию
result = optimize(f, adj.lower, adj.upper, adj.x0, Fminbox(inner_optimizer), options)
Out[0]:
 * Status: success

 * Candidate solution
    Final objective value:     3.838696e-01

 * Found with
    Algorithm:     Fminbox with Gradient Descent

 * Convergence measures
    |x - x'|               = 3.83e+00 ≤ 5.0e+00
    |x - x'|/|x'|          = 6.26e-02 ≰ 0.0e+00
    |f(x) - f(x')|         = 2.61e+00 ≰ 0.0e+00
    |f(x) - f(x')|/|f(x')| = 6.80e+00 ≰ 0.0e+00
    |g(x)|                 = 6.09e+01 ≰ 1.0e-08

 * Work counters
    Seconds run:   189  (vs limit Inf)
    Iterations:    2
    f(x) calls:    10
    ∇f(x) calls:   10
    ∇f(x)ᵀv calls: 0
model_optimization.gif

Напоследок убедимся, что поиск решения действительно шёл в правильном направлении:

In [ ]:
gr()
loss_value = getfield.( result.trace, :value )
xs = first.( trace_point.metadata["x"] for trace_point in Optim.trace(result) );
ys = last.( trace_point.metadata["x"] for trace_point in Optim.trace(result) );
plot(
    plot(loss_value, c=:Black, left_margin=10Plots.mm, bottom_margin=10Plots.mm, xlabel="Итерации", ylabel="Критерий оптимизации", title="График хода оптимизации (критерий)"),
    plot( xs, ys, c=:Black, markershape=:+, mc=:Red, bottom_margin=10Plots.mm, xlabel=adj.blocks[1], ylabel=adj.blocks[2], title="Траектория процесса оптимизации" ),
    leg=false, size=(900,300), guidefont=font(6), titlefont=font(9)
)
Out[0]:
No description has been provided for this image

Заключение

Мы изучили, каким образом можно обернуть модель в высокоуровневую функцию оптимизации и теперь можем перебирать переменные для оптимизации и выбирать оптимизаторы.