Перечисления, ветвления, циклы и модули языка физического моделирования Engee
|
Страница в процессе разработки. |
В этой статье собраны конструкции языка физического моделирования Engee для управления логикой и структурой компонентов: перечисления, ветвления (статические и динамические), циклы, модульная организация кода, функции и настройка внешнего вида блоков через иконки.
Перечисления
Перечисления используются, когда параметр должен принимать одно из заранее определенных значений. Они удобны для задания режимов работы, выбора моделей или конфигураций компонента.
@descriptive_enumeration CustomEnum begin
first = 1, "One"
second = 2, "Two"
third = 3, "Three"
end
В примере создается перечисление CustomEnum с тремя возможными вариантами:
-
CustomEnum.first— значение 1, отображается как"One"; -
CustomEnum.second— значение 2, отображается как"Two"; -
CustomEnum.third— значение 3, отображается как"Three".
Чтобы параметр отображался в интерфейсе Engee (окне настроек блока) как выпадающий список с вариантами перечисления, нужно явно указать его тип:
@engeemodel CustomComponent begin
@parameters begin
math_model::CustomEnum = CustomEnum.first
end
end
Здесь параметр math_model имеет тип CustomEnum и по умолчанию равен CustomEnum.first. В интерфейсе Engee такой параметр будет представлен выпадающим списком, где доступны только значения из перечисления.
Типы результатов функций
Иногда параметры или переменные получают значения не напрямую, а через вызов пользовательской функции. Если такая функция возвращает массив, логическое значение (Bool) или перечисление (Enum), то для переменной или параметра обязательно нужно указать явный тип.
make_vector(n) = [i for i in 1:n]
check_positive(x) = x > 0
@engeemodel Component begin
@parameters begin
a[:] = make_vector(3) # функция вернула вектор [1, 2, 3]
flag::Bool = check_positive(-5) # функция вернула false
end
end
Здесь:
-
Перед компонентом объявлены две функции:
-
make_vector(n)создает массив чисел от 1 доn. -
check_positive(x)проверяет, является ли число положительным и возвращаетtrueилиfalse.
-
-
В конструкции
@parameters:-
a[:] = make_vector(3)— параметрaполучает массив[1, 2, 3]. Тип массива задан квадратными скобками[:]. -
flag::Bool = check_positive(-5)— параметрflagполучает результат проверкиfalse, и тип явно указан какBool.
-
Без явного указания типов (например, [:] для массива или ::Bool для логической переменной) модель не будет работать корректно.
Ветвления
Ветвление — это способ описать разные варианты поведения компонента в зависимости от значения параметров. Внутри ветвлений задается правило: «если выполняется условие — используется один блок кода, иначе — другой».
В языке физического моделирования Engee ветвления бывают двух видов:
-
Статические ветвления (
if … elseif … else … end) — выполняются один раз при запуске модели. Они зависят только от параметров. Такой код разворачивается на этапе сборки модели и не меняется во время моделирования. Применяются для выбора структуры, формул или числа элементов еще до запуска расчетов. -
Динамические ветвления (
ifelse(…)) — проверяют условия и меняют результат в процессе расчета. Ветка выбирается в зависимости от текущих значений переменных, и результат может меняться на каждом шаге интеграции. Такие ветки позволяют описывать режимы работы, которые переключаются «на лету».
В итоге статические ветвления нужны для настройки модели перед запуском, а динамические — для описания поведения системы во времени.
Статические ветвления
Статические ветвления применяются, когда условие зависит от значения параметров. Ветка выбирается один раз при запуске модели и дальше не меняется.
@descriptive_enumeration Model begin
simple = 1, "Simple"
adv = 2, "Advanced"
end
@engeemodel Example begin
@parameters begin
has_option::Bool = true
model::Model = Model.simple
end
@variables begin
x = 0
y = 0
end
if has_option
@equations begin
y ~ 2 * x
end
else
if model == Model.simple
@equations begin
y ~ 5 * x
end
else
@equations begin
y ~ 5 * x^2
end
end
end
end
Условие has_option и model == Model.simple отрабатывает при запуске модели и выбирает соответствующую систему уравнений для переменной x.
Использовать if … elseif … else при наличии переменных в условиях вне блока @equations нельзя — такие ветки фиксируются по начальному значению и не меняются во время моделирования. Для переменных применяйте ifelse(…) или if … elseif … else внутри блока @equations.
|
Динамические ветвления
Динамические ветвления работают во время моделирования и зависят от значений переменных. Для этого используется функция ifelse(condition, expr1, expr2). Она позволяет описывать переключение уравнений «на лету». Пример:
@variables begin
x = 0
y = 0
end
@equations begin
y ~ ifelse(x < 10, x, -x)
end
Здесь:
-
Если
x < 10, тоy = x; -
Если
x >= 10, тоy = -x.
В отличие от статического if, такое условие проверяется на каждом шаге моделирования.
При использовании ifelse включено обнаружение событий: решатель пытается как можно точнее определить момент, когда результат условия изменяется с true на false и наоборот. В этот момент происходит реинициализация системы.
|
Чтобы избежать обнаружения событий, используют специальные функции:
-
gt— больше; -
lt— меньше; -
ge— больше или равно; -
le— меньше или равно; -
eq— равно; -
neq— не равно.
Используйте ifelse(…), когда нужен точный момент переключения состояния, и gt/lt/ge/le/eq/neq, когда важно избежать событий и лишних пересчетов системы.
|
Использование ветвления if … elseif … else … end внутри конструкции @equations эквивалентно функции ifelse:
@equations begin
# Переключение по условию с событием
y ~ ifelse(x < 10, x, -x)
# Сравнение без события
if gt(x, 0.5)
u ~ 1
v ~ 2
elseif x < 0 # это условие создаст событие
if lt(y, 1) # это условие не создаст событие
u ~ 3
else
u ~ 4
end
v ~ 5
else
u ~ 6
v ~ 7
end
end
-
ifelse(x < 10, x, -x)переключает выражение при измененииx. -
Функции
gt,lt,ge,le,eq,neqпозволяют писать условия без обнаружения событий. -
Количество уравнений в каждой ветке должно совпадать. Проверки
assertне учитываются при проверке баланса.
Циклы
Циклы позволяют сразу объявлять несколько портов, подкомпонентов, уравнений, что удобно для массивов однотипных элементов. Пример:
@structural_parameters begin
n::Int = 3
end
@variables begin
v[:] = zeros(n)
end
@nodes begin
pins = [EngeePhysicalFoundation.Electrical.Pin() for i in 1:n]
end
@equations begin
[v[i] ~ pins[i].v for i in 1:n]
end
Здесь:
-
В конструкции
@structural_parametersобъявляется параметрn = 3. Он определяет размерность массива. -
В конструкции
@variablesобъявляется векторная переменная напряжениями с размерностьюn. -
В конструкции
@nodesсоздается массивpinsиз 3 электрических портов (Pin). Каждый элемент массива соответствует отдельному порту, а имена портов будутpins_1, pins_2, … pins_n. -
В конструкции
@equationsс помощью цикла формируется массив уравнений: напряжениеv[i]связывается с напряжением соответствующего портаpins[i].v.
Так, одним блоком кода описано сразу 3 одинаковых соединений. Если изменить значение n, то количество портов и уравнений автоматически изменится.
Массивы подкомпонентов
Тот же прием можно использовать и для подкомпонентов. Как мы только что сделали массив портов и уравнений, так же можно создавать массивы из однотипных компонентов. Это позволяет, например, быстро собрать схему из нескольких резисторов, конденсаторов или диодов.
@engeemodel ArrayExample begin
@components begin
res = [EngeePhysicalFoundation.Electrical.Elements.Resistor() for i in 1:5]
cap = [EngeePhysicalFoundation.Electrical.Elements.Capacitor() for i in 1:3]
end
end
Здесь создается:
-
Массив из 5 резисторов;
-
Массив из 3 конденсаторов.
Снаружи элементы будут иметь имена res_1, res_2, … и cap_1, cap_2, … . К каждому элементу можно обращаться по индексу, а также использовать их в соединениях и уравнениях.
Циклы в @components работают по тому же принципу, что и для портов и уравнений: один шаблон — несколько экземпляров.
Векторные уравнения
Часто удобно задавать уравнения в векторной форме, а не по одному. Пример:
@variables begin
x_vector[:] = [0, 0, 0]
c[:] = [1, 2, 3]
end
@equations begin
x_vector ~ c
end
Такое объявление равносильно трем уравнениям: x_vector[1] ~ c[1], x_vector[2] ~ c[2], и т.д.
Также поддерживается точечный синтаксис для поэлементных операций с массивами:
@equations begin
x_vector .~ c .* 2
y .~ sin.(u .+ v)
end
Здесь в x_vector .~ c .* 2 каждый элемент x_vector равен соответствующему элементу c умноженному на 2, а в y .~ sin.(u .+ v) происходит поэлементное сложение массивов u и v с последующим вычислением синуса для каждого элемента.
Модульная структура
Компоненты, коннекторы и перечисления можно объединять в модули. Модули позволяют группировать связанные элементы, а также экспортировать их и использовать в других частях проекта по полному квалифицированному пути, также как в обычном коде Julia.