Среда моделирования ORPHYS 2.0 — Руководство пользователя
← На сайт
Раздел 1

Введение и базовые понятия

Комплекс программ «Среда моделирования ORPHYS 2.0» (ORPHYS) предназначен для формирования многоуровневых компьютерных моделей технических объектов и систем управления, проведения численных экспериментов, анализа динамических процессов и визуализации результатов.

Пользовательское руководство

Назначение и возможности среды

Среда ORPHYS объединяет в едином графическом интерфейсе средства структурного схемотехнического моделирования, развитую библиотеку электротехнических, гидравлических и тепловых аппаратов, блоки алгоритмического управления и визуальные панели приборов.

Область применения: Инженерное проектирование, учебные и исследовательские эксперименты Режимы анализа: Установившийся режим (статика), переходные процессы (динамика), реальное время

Быстрая навигация

Руководство ориентировано на последовательное освоение среды: от размещения первых компонентов до построения замкнутых контуров управления и генерации собственных расчетных блоков.

Для быстрого поиска по тексту и библиотеке компонентов нажмите Ctrl+K.

Три слоя модели

В основу архитектуры ORPHYS положен принцип многослойного описания исследуемой системы. Расчетная схема разделяется на три функциональных уровня:

Слой модели Моделируемые процессы и элементы Принцип передачи связей Результат расчета
Объектный слой (C-слой) Физические процессы: электрические цепи, гидравлические сети, теплообменные аппараты, технологические коммуникации. Ненаправленные (акаузальные) связи между физическими выводами. Балансы токов, расходов и давлений формируются средой автоматически. Векторы физического состояния: потенциалы узлов, токи и расходы ветвей, температуры и давления.
Логический слой (L-слой) Алгоритмы автоматического управления: ПИД-регуляторы, математические операции, логические автоматы, фильтры сигналов. Строго направленная передача сигналов по связям: от выходных портов (out) ко входным портам (in). Временные ряды выходных переменных (series) и текущие значения управляющих сигналов.
Визуальный слой (V-слой) Интерфейс оператора: временные графики, числовые табло, мнемосхемы, стрелочные индикаторы, интерактивные органы ввода. Информационная привязка к переменным схемы (binding) без вмешательства в физические уравнения. Наглядное графическое представление результатов и ручной ввод управляющих воздействий.

Интерфейс пользователя и элементы рабочего пространства

Палитра компонентов (Библиотека)

Расположена в левой части экрана. Содержит структурированный каталог готовых моделей, сгруппированных по предметным разделам (электротехника, гидравлика, теплотехника, системы управления, индикация).

Холст схемы (Рабочее поле)

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

Панель свойств (Инспектор атрибутов)

Отображает параметры и настройки выделенного блока (номинальные значения, размерности, начальные условия, конфигурацию портов-отводов).

Панель управления экспериментом

Панель в верхней части окна. Содержит кнопки запуска («Старт»), приостановки и сброса моделирования, а также поля настройки времени расчета ($t_{\text{end}}$) и шага ($h$).

Раздел 2

Создание и редактирование схем

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

Порядок сборки схемы

  1. 1
    Выбор слоя и размещение компонентов
    Перейдите на требуемый слой модели (вкладки «C-слой» или «L-слой» над холстом). Выберите компонент в палитре слева и перетащите его на рабочее поле. В панели свойств задайте компоненту понятную функциональную метку (параметр label).
  2. 2
    Прокладка соединений (коммутация)
    Наведите курсор на контактный вывод компонента, нажмите левую кнопку мыши и протяните линию связи к выводу смежного элемента. На логическом слое соединение допускается только от выхода (out) ко входу (in).
  3. 3
    Установка узлов соединения (Junction)
    При разветвлении линий на физическом слое используйте специальный компонент «Узел соединения» (Junction). Простое геометрическое пересечение линий без узла не образует контакта.
  4. 4
    Задание опорного узла цепи
    В обязательном порядке подключите к физической цепи элемент, фиксирующий базисный потенциал (компонент «Земля» для электротехники или «Источник среды / Сток» для гидравлики).
  5. 5
    Параметризация элементов
    Выделите каждый компонент и в панели свойств введите требуемые физические величины и начальные условия.

Правила коммутации и типы соединений

Тип соединения Допустимые полюсы Ограничения и правила
Физическая связь (C-слой) Однородные выводы физического домена (электрические клеммы, фланцы трубопроводов). Направление произвольное. При объединении трех и более связей в одной точке обязательна установка блока Junction.
Информационная связь (L-слой) Строго от выходного порта (out) ко входному порту (in). Запрещено замыкать выходы на выходы или входы на входы. Сигнал передается направленно.
Беспроводная передача (Goto / From) Блок Goto принимает сигнал на вход; блок From передает сигнал со своего выхода. Связывание выполняется по совпадению текстового тега (tag). Действует исключительно в пределах одного и того же слоя.

Беспроводная передача сигналов блоками Goto и From

Для предотвращения нагромождения длинных линий связей на логическом слое используются парные блоки:

  • Блок Goto: подключается к источнику сигнала и публикует его в локальном пространстве схемы под символьным именем tag (например, PID_Output).
  • Блок From: настраивается на идентичное имя тега и передает значение на вход приемного компонента. Один блок Goto может обслуживать произвольное количество блоков From.
Раздел 3

Связь физического и логического уровней (Адаптеры и отводы)

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

Объектный слой (C) Аппарат АВО Метрика температуры T_prod_out
Команда приводу fan_cmd
Метрика → Выходной отвод → l_device ⇄ ПИД.u → l_device → Входной отвод
Логический слой (L) ПИД-регулятор Уставка · Измерение → Сигнал управления u

Порты-отводы технологических аппаратов (Taps)

Для подключения физического аппарата к системе управления на его графическом корпусе формируются порты-отводы:

  • Выходной отвод (Телеметрия): Снимает вычисленную величину физического состояния аппарата (температуру среды, перепад давлений, массовый расход) для передачи в измерительные датчики или индикаторы.
  • Входной отвод (Управление): Принимает внешнее командное воздействие от регулятора (степень открытия регулирующего клапана opening_cmd, задание частоты вращения вентилятора АВО fan_cmd).

Порядок добавления порта-отвода:

  1. 1
    Выделение аппарата
    На объектном слое выделите курсором требуемый аппарат (например, аппарат воздушного охлаждения avo).
  2. 2
    Формирование отвода
    В панели свойств найдите секцию «Порты-отводы», нажмите кнопку «+ выход» (для измерения) или «+ вход» (для команды управления).
  3. 3
    Выбор физической величины
    В выпадающем списке выберите регистрируемую переменную (например, T_prod_out для температуры продукта или fan_cmd для привода). На корпусе блока появится контактный порт.

Согласующий компонент l_device

Для взаимодействия логического алгоритма с физическим оборудованием на логическом слое устанавливается блок «Адаптер объекта» (l_device). В его свойствах указываются:

  • target_device: метка (label) целевого физического аппарата на объектном слое (например, AVO_1).
  • variable: наименование переменной, совпадающее с именем сформированного отвода (например, T_prod_out при чтении или fan_cmd при записи).

Сквозной пример: контур стабилизации температуры АВО

Схема контура: ПИД-регулятор (L-слой) → аппарат воздушного охлаждения (C-слой) → измерение температуры
ПИД-регулятор (L) Вход: Уставка, Kp / Ki / Kd Выход: сигнал u (управление) Аппарат АВО (C) Модель теплообмена Вход: Обороты привода Датчик T на выходе Вход: T продукта (с C) Выход: T продукта (на L) u → Обороты привода Поток вещества Обратная связь: T_prod_out → ошибка рассогласования e = T_уст - T_изм Адаптер L -> V Передача: T продукта Табло индикации (V) T, °C
Раздел 4

Запуск расчетов и режимы моделирования

Среда ORPHYS поддерживает проведение расчетов установившегося состояния, динамических переходных процессов и интерактивное моделирование в реальном времени.

Режимы расчета

Режим Обозначение Назначение и особенности
Рабочая точка (Статика) DC / Static Мгновенный расчет статического равновесия системы при постоянных внешних воздействиях. Позволяет оценить начальное состояние до запуска динамики. Возвращает вектор установившихся узловых потенциалов и ветвевых токов (last, state).
Переходный процесс (Динамика) Transient Основной режим моделирования. Численно рассчитывает поведение всех переменных во времени от начала и до конца. Позволяет наблюдать пусковые режимы и переходные кривые. Возвращает массивы временных рядов (series), конечные состояния (state) и параметры сходимости.
Частотный анализ AC / Боде Линеаризация системы в рабочей точке и расчет амплитудно-фазовых частотных характеристик (АФЧХ). Возвращает массивы комплексных коэффициентов передачи, логарифмических АЧХ и ФЧХ по спектру частот.

Настройка параметров времени и шага интегрирования

В панели управления экспериментом задаются основные параметры расчета:

  • Время окончания расчета ($t_{\text{end}}$): Длительность исследуемого физического процесса в секундах (например, $100\text{ с}$).
  • Базовый шаг ($h$): Временной интервал между расчетными точками.

Автоматический реактивный пересчет

После того как схема была однократно успешно рассчитана нажатием кнопки «Старт», активируется реактивный режим: любое последующее изменение параметров компонентов в панели свойств либо перемещение проводников автоматически инициирует фоновый пересчет схемы. Графики и цифровые табло обновляются без дополнительных действий со стороны пользователя.

Раздел 5

Визуализация и анализ результатов

Для наблюдения за ходом технологического процесса и анализа результатов моделирования на визуальном слое (V) размещаются контрольно-измерительные приборы и органы управления.

Структуры данных: ряды (series) и текущие значения (last)

Вычислительное ядро среды возвращает результаты в двух форматах:

  • Временной ряд (series): Полный массив пар «время — значение» за весь период расчета. Предназначен для построения непрерывных кривых переходных процессов в компоненте «График» (Chart).
  • Конечное значение (last): Точечное скалярное значение переменной на последнем вычисленном шаге. Предназначено для отображения текущих показаний в компонентах «Числовое табло» (Digital Display), сигнальных индикаторах и мнемосхемах.

Настройка виртуальных приборов

Компонент «График» (Chart)

1. Перетащите блок графика на визуальный слой.
2. В панели свойств найдите поле Привязка (binding).
3. Выберите из выпадающего списка требуемый сигнал (например, выход регулятора PID1.out или температуру аппарата AVO.T_prod_out).
4. Включите опцию «Автомасштабирование» для автоматической подгонки границ осей.

Компонент «Числовое табло» (Digital Display)

1. Разместите табло на визуальном слое.
2. В поле привязки выберите источник данных.
3. В строке «Единица измерения» введите текстовую подпись (например, кПа, °C).
4. Настройте количество десятичных знаков после запятой (параметр format).

Интерактивные органы управления

Для ручного воздействия на систему в режиме реального времени (Live) используются элементы ввода:

  • Ползунковый задатчик (Slider): Обеспечивает плавное изменение численного параметра в заданных границах (минимум, максимум, шаг приращения).
  • Переключатель (Toggle Switch): Служит для дискретной подачи логических сигналов (0/1) и переключения технологических блокировок.
Раздел 6

Создание собственных компонентов (Генератор моделей)

Встроенный интерактивный мастер позволяет инженеру создать новый расчетный блок через визуальный интерфейс без программирования.

Пошаговый алгоритм создания компонента

  1. 1
    Запуск редактора компонентов и задание метаданных
    Откройте инструмент «Редактор компонентов» из панели инструментов среды. Укажите уникальное системное имя (латиницей без пробелов, например custom_valve), понятное наименование для палитры (например, «Клапан») и выберите целевой слой (физический или логический).
  2. 2
    Конфигурация портов (внешних выводов)
    Добавьте контакты компонента. Для логического блока выберите направление: in (вход) или out (выход). Для физического блока выберите домен (электрика, гидравлика) и сторону размещения на корпусе. Имена портов будут использоваться в формулах.
  3. 3
    Объявление параметров
    Сформируйте список настроек, которые пользователь сможет изменять через панель свойств: символьное обозначение (например, K_loss), текстовая подпись, единицы измерения и базовое значение по умолчанию.
  4. 4
    Математическое описание: логика vs физика
    Воспользуйтесь редактором формул для ввода уравнений поведения компонента. Важно учитывать принципиальное различие слоев:
    • Для логического блока: Задается формула прямого расчета выхода: $$\text{out} = K \cdot (\text{in}_1 - \text{in}_2)$$ • Для физического блока: Физический закон должен быть приведен к форме Невязки (все переносится в левую часть, чтобы справа остался ноль).
    Пример (закон Ома): Вместо $U = I \cdot R$ в редактор вводится невязка: $$(V_{\text{in}} - V_{\text{out}}) - I \cdot R = 0$$
  5. 5
    Оформление условного графического обозначения (УГО)
    На вкладке глифа задайте габариты блока и нарисуйте пиктограмму. Равномерно распределите порты по сторонам прямоугольника для обеспечения удобной коммутации на схеме.
  6. 6
    Валидация и регистрация
    Нажмите кнопку «Проверить» (Validate) для синтаксической проверки уравнений, затем нажмите кнопку «Зарегистрировать компонент» (Register Component). Новый блок автоматически появится в палитре компонентов.
Раздел 7

Интеграция пользовательских математических моделей в БМК

Настоящая инструкция устанавливает регламент разработки, конфигурации и внедрения внешних математических моделей в составе Библиотеки Моделей Компонентов (БМК) при использовании исполняемой (EXE) версии программного комплекса ORPHYS 2.0 в операционной системе Windows.

1. Расположение директории хранения пользовательских компонентов

В структуре исполняемой сборки ORPHYS 2.0 предусмотрена изолированная директория для хранения пользовательских компонентов. Данное хранилище обладает свойством персистентности: его содержимое не подвергается удалению или перезаписи при автоматическом обновлении программного обеспечения.

Локализация пользовательского хранилища:
%APPDATA%\Mars EM Simulator\bmk\
(Абсолютный путь, как правило, имеет вид: C:\Users\<Имя_Пользователя>\AppData\Roaming\Mars EM Simulator\bmk\)

2. Архитектура каталога компонента

Каждый разрабатываемый компонент должен быть размещен в отдельной поддиректории. Наименование поддиректории должно состоять из символов латинского алфавита и строго соответствовать идентификатору компонента.

Структура исходного кода (язык Python):

%APPDATA%\Mars EM Simulator\bmk\
  └── my_pump\
        ├── my_pump.py         # Исходный код математической модели (язык Python)
        ├── catalog.json       # Декларативное описание графического интерфейса (определение портов, параметров)
        └── helper.py          # (Опционально) Вспомогательные модули, содержащие дополнительную логику

Для интегрирования проприетарных (скомпилированных) компонентов вместо файла с расширением .py применяется бинарный модуль .pyd:

%APPDATA%\Mars EM Simulator\bmk\
  └── my_pump\
        ├── my_pump.cp313-win_amd64.pyd  # Бинарный модуль расширения (компиляция C++/Cython)
        └── catalog.json

В случае одновременного присутствия файлов с расширениями .pyd и .py в одной директории, вычислительное ядро автоматически отдает приоритет загрузке модуля .pyd.

3. Среда исполнения (Интерпретатор Python)

  1. Выполнение кода посредством встроенного интерпретатора: В состав исполняемой версии ORPHYS 2.0 интегрирован автономный интерпретатор CPython версии 3.13 (64-битная архитектура). Системный интерпретатор Python (установленный в операционной системе пользователя) не задействуется.
  2. Ограничения стандартной библиотеки Python: Вследствие компиляции бэкенда ORPHYS, модули, не задействованные в базовом функционале, исключены из сборки с целью минимизации объема дискового пространства (например, отсутствуют модули tkinter, unittest, pytest).
  3. Модули, доступные в базовой конфигурации:
    • Пакет разработки (SDK) симулятора: mars_models_sdk (включающий элементы @component, ComponentSpec, Port, Param).
    • Ключевые модули стандартной библиотеки: math, json, random, re, os, sys, dataclasses, time и прочие.
    • Библиотеки для проведения математических вычислений: numpy, scipy, sympy.

4. Порядок импорта библиотек и смежных модулей

4.1 Импорт вспомогательных модулей (например, helper.py)

Симулятор осуществляет загрузку компонентов напрямую, используя механизм importlib. Следует учитывать, что директория компонента не добавляется в системный путь sys.path автоматически. Попытка прямого или относительного импорта приведет к генерации исключения ModuleNotFoundError.

Корректный синтаксис импорта смежных файлов в коде компонента:

import sys
from pathlib import Path

# Обеспечение доступности директории текущего компонента для механизма импорта:
_COMP_DIR = str(Path(__file__).parent.resolve())
if _COMP_DIR not in sys.path:
    sys.path.insert(0, _COMP_DIR)

# Выполнение импорта смежного модуля:
import helper

4.2 Использование внешних сторонних библиотек (pip)

Ввиду независимости от системного интерпретатора Python, пакеты, установленные глобально через менеджер pip, недоступны для автоматической загрузки.

  • Библиотека должна быть полностью совместима с Python 3.13 (AMD64 / ОС Windows).
  • Для библиотек, написанных исключительно на Python (pure-python), необходимо скопировать директорию библиотеки в каталог разрабатываемого компонента (или в поддиректорию libs/) и зарегистрировать путь в sys.path, аналогично пункту 4.1.
  • Бинарные C-расширения (файлы .pyd / .dll) должны быть строго скомпилированы под среду CPython 3.13 64-bit. Все динамически подключаемые DLL-библиотеки должны располагаться в той же директории.

5. Жизненный цикл и процедура регистрации компонентов

  • Автоматическое сканирование каталога: Симулятор производит сканирование директории %APPDATA%\Mars EM Simulator\bmk исключительно в момент инициализации (запуска) программы.
  • Отсутствие динамической перезагрузки (Hot-Reload): При внесении изменений в файлы во время работы программы симулятор не применяет модификации в реальном времени. Вычислительное ядро использует версию модуля, загруженную в память при старте.
  • Регламент обновления: После каждого сохранения изменений в файлах модели необходимо выполнить перезапуск среды моделирования. При последующей инициализации компонент будет повторно считан с диска и зарегистрирован.

6. ВАЖНО: Отсутствие двунаправленной синхронизации с утилитой "Генератор компонентов"

В состав ORPHYS входит утилита «Генератор компонентов» (визуальный конструктор). Необходимо учитывать критический принцип архитектуры:

Практические следствия:

  1. Безусловная перезапись файлов: При активации функций «Сгенерировать» или «Сохранить» в интерфейсе Генератора компонентов, программа формирует исходный код на базе визуально заданных параметров и полностью перезаписывает файлы <name>.py и catalog.json в целевой директории.
  2. Игнорирование ручных модификаций: Если вы модифицировали файл .py во внешнем редакторе, Генератор компонентов не выполнит импорт этих изменений. Повторное открытие компонента отобразит лишь состояние, зафиксированное в визуальных формах при последнем сохранении.
  3. Риск утраты исходного кода: Если после ручного редактирования файла .py пользователь повторно нажмет «Сохранить» в Генераторе, файл будет сформирован заново, что приведет к безвозвратной утрате всех ручных модификаций кода.

Регламент организации рабочего процесса (Рекомендуемый для сложных моделей):

  1. Создание каркаса компонента в Генераторе (декларация портов, параметров и УГО).
  2. Генерация базовой структуры файлов.
  3. Последующее прекращение использования Генератора для данного компонента (запрет на сохранение в конструкторе).
  4. Дальнейшая разработка файла .py во внешней среде разработки (IDE).

7. Эталонный пример минимального компонента

Декларативное описание catalog.json:

{
  "my_resistor": {
    "kind": "Пользовательский резистор",
    "sym": "R_USR",
    "body": "R_USR",
    "folder": "Пользовательские",
    "layer": "logic",
    "render": "block",
    "nodes": [
      { "name": "in_val", "dir": "in", "side": "left" },
      { "name": "out_val", "dir": "out", "side": "right" }
    ],
    "attrs": [
      { "name": "resistance", "value": "100.0", "unit": "Ом", "type": "number" }
    ],
    "bus": "my_resistor"
  }
}

Исходный код математической модели my_resistor.py:

import sys
from pathlib import Path

# 1. Интеграция директории компонента в системный путь
_COMP_DIR = str(Path(__file__).parent.resolve())
if _COMP_DIR not in sys.path:
    sys.path.insert(0, _COMP_DIR)

# 2. Импорт SDK симулятора
from mars_models_sdk import component

# 3. Применение декоратора для регистрации функции
@component("my_resistor", inp="in_val", out="out_val")
def my_resistor_step(io):
    # Извлечение значения из входного порта
    u_in = io.read("in_val", 0.0)
    
    # Считывание конфигурационного параметра
    r = io.param("resistance", 100.0)
    
    # Внедрение защиты от деления на ноль
    if r == 0:
        r = 1e-6  
        
    # Вычисление выходной величины
    i_out = u_in / r
    
    # Трансляция вычисленного значения в выходной порт
    io.write("out_val", i_out)

8. Проверочный регламент (Чек-лист) валидации компонента

  • ✓
    Путь сохранения
    Директория компонента размещена строго по пути %APPDATA%\Mars EM Simulator\bmk\<идентификатор>\.
  • ✓
    Именования
    Наименование директории и основного исполняемого файла (.py или .pyd) полностью совпадают и состоят из латинских символов.
  • ✓
    Каталог интерфейса
    В наличии синтаксически корректный файл catalog.json, содержащий определение интерфейсов (nodes) и параметров (attrs).
  • ✓
    Импорты
    При использовании импорта смежных модулей реализована модификация sys.path в заголовочной части.
  • ✓
    Перезапуск
    Процедура перезапуска среды ORPHYS выполнена после фиксации последних изменений в файлах.
  • ✓
    Защита кода
    Файл исходного кода не подвергался перезаписи со стороны утилиты "Генератор компонентов" после внесения ручных модификаций.
Раздел 8

Диагностика частых ошибок (ЧаВо / FAQ)

Сводная таблица типовых нештатных ситуаций, возникающих при формировании и расчете схем, с указанием причин и способов устранения.

Проблема / Сообщение среды Вероятная причина Рекомендуемое решение
«Нет опоры: добавьте Землю / Источник / Приёмник» В физической цепи отсутствует опорный узел, фиксирующий точку отсчета. Добавьте в схему компонент «Земля» (Ground) для электрической цепи либо аппараты «Источник среды» (FEED) / «Сток» (SINK) для гидравлической сети. ВАЖНО: Данные компоненты связаны с соответствующими граничными условиями своего домена.
График остается пустым после пуска 1. Ошибка в имени переменной в поле привязки (binding).
2. Значения сигнала находятся за пределами видимого масштаба осей.
1. Выберите имя переменной заново из выпадающего списка, а не вводите вручную.
2. Активируйте в свойствах графика флажок «Автомасштабирование».
Схема не пересчитывается при изменении параметров Среда не перешла в реактивный режим, так как схема не была рассчитана первично. Выполните однократный ручной запуск расчета кнопкой «Старт». После этого автоматический фоновый пересчет будет активирован.
Проводники пересекаются, но сигнал не передается Пересечение линий на чертеже не создает топологического контакта. Установите в точке разветвления компонент «Узел соединения» (Junction) и подключите проводники непосредственно к нему.
Сигнал на логическом слое равен нулю или константе Нарушена направленность связи (выход соединен с выходом либо вход со входом). Проверьте ориентацию: на логическом слое соединение всегда выполняется строго от правого выходного порта (out) к левому входному порту (in).
Раздел 9

Создание компонента логического уровня

Практическое руководство по разработке функциональных компонентов логического слоя (L-слоя) в среде моделирования ORPHYS 2.0 с помощью визуального «Генератора компонентов».

Расширенное практическое руководство по разработке функциональных компонентов логического слоя (L-слоя) в среде моделирования ORPHYS 2.0 с помощью визуального «Генератора компонента».

1. Принцип работы логического компонента

В отличие от физического компонента, который формирует невязку F(X, dX/dt, t) = 0, логический компонент реализует один тик (шаг) вычислений — обычную чистую функциональную программу:

Исходный код / фрагмент конфигурации
выход = f(входы, параметры, состояние)

Ключевые особенности:

  1. Вычисление явное, а не совместное с соседями. Логический блок не «договаривается» с ядром об общем решении — он просто получает значения входов и возвращает значения выходов.
  2. Исполнение шаг за шагом по тикам временной сетки. Шаг dt фиксирован (по умолчанию согласован с шагом интегрирования объектного слоя, но может быть задан отдельно).
  3. Состояние (io.state) живёт между тиками, обеспечивает храниеие и накопление значения переменной.
  4. Связь направленная — out → in. В отличие от объектного слоя, нет акаузального объединения узлов; каждый провод отдает скаляр от одного выхода строго одному входу.
  5. Реактивное исполнение. После успешного первичного расчёта ComponentBus реагирует на любое изменение параметра или связи в инспекторе мгновенно в фоновом режиме.

Эта особенность диктует три практических следствия для разработчика:

  • Тело компонента должно быть чистым: без побочных эффектов, без print, без мутаций глобальных переменных. Шина может вызывать компонент параллельно по разным веткам графа.
  • Персистентная память организуется через io.state, а не через глобальные переменные Python-модуля — иначе состояние «протекает» между экземплярами одного класса компонентов.
  • Если нужен явный интегратор с фиксированным шагом, всегда используется io.integrate(name, rate, x0) — не собственная реализация метода Эйлера.

2. Архитектурный контракт: ComponentBus, io.integrate, io.state

Полный путь от определения модели до видимости блока в логической палитре состоит из тех же двух уровней регистрации, что и для объектного слоя.

УровеньНазначениеАртефакт
Палитра/UIМетаданные в палитре, инспекторе свойств, подсказках.data/catalog.json + generated_catalog.js.
Шина/ядроИсполняемый код шага, регистрируется декоратором @component или @registered(SPEC).Скомпилированный файл в bmk/<lgroup>/<name>/....

2.1. Декораторы регистрации

Из архитектурного руководства (раздел «Выбор слоя и математической парадигмы»):

СлойДекораторСигнатура функции шага
Логический@registered(SPEC) или @component (через mars_models_sdk.component)def step(io): ...
Объектный@physical(SPEC)def residual(elem): ... (через Elem) или def f(c, V, I, dV, dI, t, lam): ... (напрямую)

В генераторе эквивалент декоратора «спрятан» в служебном коде, который формируется автоматически при нажатии «Зарегистрировать». Вручную править его не нужно.

2.2. Сервис исполнения

Шина компонентов — ComponentBusService.EvaluateGraph — топологически сортирует блоки логического графа и выполняет их в нужном порядке.

В одном проекте могут сосуществовать Python- и C++-реализации одного и того же компонента. Хост автоматически выбирает более быстрый бэкенд (C++), если он доступен, иначе выполняет Python-версию — при условии идентичности формул в обоих файлах.

2.3. Хранилище памяти: io.state

io.state — это словарь персистентной памяти компонента. Он сохраняется:

  • между тиками внутри одного запуска;
  • между повторными пересчётами той же схемы;
  • между сессиями в режиме реального времени (Live), пока не вызван явный сброс.

Поведение словаря описано в таблице IO (см. раздел 9.2 ниже):

Код (python)
io.state["acc"] = ...       # запись
io.state.get("acc", 0.0)   # безопасное чтение с дефолтом
io.state                    # прямой доступ к словарю

2.4. Интегрирование: io.integrate

Шина применяет явный метод Эйлера с фиксированным шагом io.dt. Метод io.integrate(name, rate, x0) извлекает значение из io.state, делает инкремент x_{n+1} = x_n + rate * dt и сохраняет результат обратно. Этот метод — единственный способ гарантированно сохранить состояние интегратора между тиками и не потерять пересчёты при изменении схемы.

2.5. Реактивность

Логический слой реактивен непрерывно: после первого расчёта любое изменение связей или параметров блока в инспекторе мгновенно пересчитывает функциональные ряды в фоновом режиме.

3. Сравнение с объектным слоем

ПризнакЛогический компонент (L-слой)Физический компонент (C-слой)
Декоратор@registered(SPEC) / @component@physical(SPEC)
Математическая задачаВычислить функциональный шаг: выход = f(входы, параметры)Сформулировать невязки: F(X, dX/dt) = 0
Метод интегрированияЛокальный явный (метод Эйлера) через io.integrateГлобальный совместный (метод Эйлера/BDF/Radau) внутри решателя
Внутренние состоянияПроизвольный словарь io.stateВектор `states=[(name, "diff"
Топологическая связьНаправленная: out → inАкаузальная: полюса объединяются в узлы цепи по KCL
РеактивностьМгновенная, без debounceDebounce 300 мс или патчи в реальном времени
Канонический файл<name>.py + опционально <name>.cpp<name>.py (один язык)

Когда выбирать L-слой, а не C-слой

ЗадачаЦелевой слой
ПИД-регулятор, фильтр, логический автомат, математические преобразованияЛогический
Электрическая цепь, гидравлическая сеть, теплообмен, механикаОбъектный

Главный критерий: если закон описывается как «преобразовать скаляр в скаляр» — это требует компонента L-слоя. Если как «связать давление и поток ветви физическим законом» — это требует компонента C-слоя. Если модель содержит уравнения непрерывности, баланса или нелинейной характеристики физической величины — это всегда C-слой.

4. Подготовка: что нужно знать до запуска генератора

Прежде чем нажимать «Запустить генератор», зафиксируйте на бумаге:

  1. Машинное имя (bus id): латиница, без пробелов, уникальное в библиотеке. Используется в реестре и при сборке каталога.
  2. Слой компонента: в генераторе выбирается переключателем «Логический» в правом верхнем углу генератора (см. п. 6).
  3. Список входов (inp=) и выходов (out=) — направленные скалярные порты. У одного блока может быть несколько входов и несколько выходов.
  4. Список параметров: имя, тип, значение по умолчанию, единица измерения, границы (min/max/step).
  5. Алгоритм: формула шага выход = f(входы, параметры), или последовательность операций с использованием пресетов (read, write, integrate, state).
  6. Состояния памяти (если есть): какие переменные нужно хранить между шагами расчета. Описываются как «дифференцируемые параметры» на вкладке «Параметры».
  7. Строковые режимы (если есть): например, мультирежимный источник (const, ramp, sine, square, step).
  8. Зависимости для кода Python: используются ли numpy, scipy, sympy, scikit-learn, pandas (встроены) или нужны сторонние пакеты (тогда потребуется .pyd).

5. Шаг 1. Запуск «Генератора компонента»

Запускаем
Запускаем

Запуск производится из меню «Сервис» основного интерфейса ORPHYS. После открытия генератора появляется окно с тем же набором вкладок, что и для объектного слоя, но с одним важным отличием: вкладка «Невязки» заменена на вкладку «Алгоритм», а на вкладке «Код» доступны специальные XML-теги <mars:user-*>.

Рекомендуемый порядок заполнения:

Исходный код / фрагмент конфигурации
Порты → Параметры → Алгоритм → Код → Внешний вид → Документация

Именно в таком порядке поля более поздних вкладок получают автозаполненные ссылки на ранние сущности.

6. Шаг 2. Вкладка «Порты»

Заполняем порты
Заполняем порты

Что здесь задаётся: интерфейс компонента — список направленных портов (in/out), которые будут видны на холсте логического слоя для подключения проводов. С точки зрения архитектуры это поля ports= дескриптора ComponentSpec с layer="logic".

6.1. Главный переключатель слоя

В верхней части окна генератора находится переключатель «Объектный»/«Логический». Для компонента логического уровня выбирается «Логический». После этого выбора:

  • во вкладке «Невязки» вкладка переименовывается в «Алгоритм»;
  • становятся доступны пресеты алгоритма (read, write, integrate, state, param, text, и т.п.);
  • в сгенерированном коде используется декоратор @component (а не @physical).

6.2. Минимальные поля одной записи порта

ПолеНазначениеПример
nameМашинное имя порта. Используется в io.read("name", …) и io.write("name", …).x, y, setpoint, T_prod_out
dirНаправление: in (вход) или out (выход). В отличие от объектного слоя, dir влияет не только на визуализацию, но и на логику исполнения графа.in
sideСторона блока: left, right, top, bottom.left
descriptionТекстовое описание.Например, «Измеренное значение температуры»

6.3. Количество портов и направление

  • Минимум для функционального блока — один вход или один выход. Генератор, источник сигнала или константа могут иметь только выход; индикатор или запись — только вход.
  • Компонент может иметь Несколько входов (ПИД-регулятор, например, имеет setpoint и meas, сумматор — a и b); а также может иметь несколько выходов (например, ПИД-регулятор выдаёт u и last_error, блок сравнения выдаёт gt, eq, lt).
  • Соединение только out → in: связь отдает значение переменной (объект) от одного выхода ко всем присоединённым связями входам.

6.4. Конфликт имён

Если два логических выхода случайно соединены с одним входом — это ошибка топологии, регистрируемая валидатором. Маркер «нет опоры» / «соединение запрещено» — именно то, что блокирует такую конфигурацию.

6.5. Расширенные поля

ПолеНазначение
unitЕдиница измерения (влияет на отображение в инспекторе, не на вычисления).
defaultЗначение по умолчанию для входов без подключения.
publishФлаг публикации значений из порта на визуальный слой (для графиков и табло).

7. Шаг 3. Вкладка «Параметры»

Заполняем параметры
Заполняем параметры

Что здесь задаётся: коэффициенты, режимы, начальные условия и состояния памяти. В коде это поля params= дескриптора ComponentSpec и словарь io.state после регистрации.

7.1. Состав полей одной записи параметра

ПолеНазначение
nameМашинное имя. Читается в коде через io.param("name", default).
valueЗначение по умолчанию.
unitЕдиница измерения.
typeТип данных (см. ниже).
descriptionОписание.
publishПубликация на визуальный слой.

7.2. Поддерживаемые типы

typeНазначениеПример
numberВещественный скаляр.Коэффициент усиления gain = 2.5974.
intЦелое число.Индекс режима mode_id = 2.
stringСтроковый режим (mode = "sine").
boolЛогический флаг (enabled = True).
comboВыбор из фиксированного списка.Режимы работы источника.
arrayМассив коэффициентов.Полином фильтра.
pairКортеж (min, max).Диапазон допуска.
tableТабличная кусочно-линейная характеристика.Внешняя Q-H насоса.

7.3. Состояния (stateful-параметры)

Для интеграторов, фильтров и ПИД-регуляторов добавляются записи состояний: это переменные, которые хранятся в io.state и запоминаются между шагами. На вкладке «Параметры» они имеют признак «Дифференцируемый» или просто маркируются как состояние.

Пример для ПИД-регулятора:

  • параметры: Kp = 0.8, Ki = 0.05, Kd = 0.1, setpoint = 50.0;
  • состояния: integral = 0.0, prev_error = 0.0 (память интегратора и предыдущей ошибки).

7.4. Тепловая модель (не применимо)

В логическом слое секция «Тепловая модель» не появляется: это понятие физического уровня. Если логический блок участвует в тепловом контуре (например, ПИД управляет АВО), он работает только с числовыми значениями, а тепловое моделирование выполняется в объектном слое через блок АВО.

8. Шаг 4. Вкладка «Алгоритм» (главный шаг)

Заполняем уравнения
Заполняем уравнения

Центральный шаг разработки. Здесь формулируется функциональный алгоритм шага — тело будущей функции def step(io): .... Вкладка отличается от объектного слоя тем, что:

  • используются пресеты команд из боковой панели (чтение входа, запись выхода, доступ к состоянию и т.п.);
  • формула имеет вид присваивания (выход = …), а не невязки (F = 0);
  • проверка и сборка переводит алгоритм в питоновский скелет с заглушками для всех внутренних переменных.

8.1. Пресеты команд

В правой панели вкладки доступны кнопки-вставки готовых конструкций. Наиболее употребительные:

ПресетВставляемая строкаСемантика
read("x")x = io.read("x", 0.0)Считать значение входного порта x.
read (default)… , 0.0)С дефолтом, если провод не подключен.
param("K")K = io.param("K", 1.0)Считать числовой параметр.
text("mode")mode = io.text("mode", "")Считать строковый режим.
flag("on")on = io.flag("on", False)Считать логический флаг.
state.get("acc", 0)acc = io.state.get("acc", 0.0)Безопасное чтение памяти.
state["acc"] = …io.state["acc"] = …Запись в память.
integrate("x", dx_dt, x0)x = io.integrate("x", dx_dt, x_prev)Шаг явного Эйлера.
write("y", …)io.write("y", value)Записать значение выхода.
ts, dtt = io.ts, dt = io.dtТекущий такт и шаг по времени.

Пресеты избавляют от опечаток в строковых литералах и обеспечивают единообразие кода: генератор сам подставляет правильное имя порта/параметра из соответствующих вкладок.

8.2. Правила записи алгоритма

  1. Форма — присваивание, а не невязка. Это y = gain * x + offset, а не y - gain * x - offset == 0.
  2. Финальная строка должна быть io.write("выход", value) для каждого выходного порта. Если выходов несколько, делается несколько io.write (для каждого свой).
  3. Все идентификаторы должны быть объявлены либо как порт (вкладка «Порты»), либо как параметр (вкладка «Параметры»). Свободные имена не допускаются.
  4. Все состояния читаются через io.state.get(..., default) на каждом тике. При первом тике io.state пуст — и без default вылетит KeyError.
  5. Допускается несколько строк и if/elif/else — алгоритм логический, не обязан укладываться в одну формулу.

8.3. Конструкции алгоритма

Простое преобразование (линейная функция)
Исходный код / фрагмент конфигурации
y = io.read("x", 0.0) * io.param("gain", 1.0) + io.param("offset", 0.0)
io.write("y", y)
Безусловный интегратор
Исходный код / фрагмент конфигурации
u = io.read("u", 0.0)
x_prev = io.state.get("acc", 0.0)
x_next = io.integrate("acc", u, x_prev)
io.write("x", x_next)
EMA-фильтр первого порядка
Исходный код / фрагмент конфигурации
x = io.read("x", 0.0)
alpha = io.param("alpha", 0.2)
y_prev = io.state.get("y", x)
y_curr = y_prev + alpha * (x - y_prev)
io.state["y"] = y_curr
io.write("y", y_curr)

Здесь io.integrate не нужен — это алгебраический фильтр, а не интегратор. Состояние запоминается через io.state явно.

Мультирежимный источник сигнала
Исходный код / фрагмент конфигурации
mode = io.text("mode", "")
base = io.param("base", 25.0)
amp = io.param("amplitude", 20.0)
freq = io.param("freq", 0.1)

if mode == "sine":
    v = base + amp * math.sin(2 * math.pi * freq * io.ts)
elif mode == "step":
    v = base + (amp if io.ts >= io.param("t0", 0.0) else 0.0)
elif mode == "const":
    v = base
else:
    v = base

io.write("value", v)
Логический автомат (гистерезис)
Исходный код / фрагмент конфигурации
x = io.read("x", 0.0)
hi = io.param("hi", 1.0)
lo = io.param("lo", 0.0)
state = io.state.get("tripped", False)

if state and x < lo:
    state = False
elif (not state) and x > hi:
    state = True
io.state["tripped"] = state
io.write("y", 1.0 if state else 0.0)

8.4. Проверка и сборка

После заполнения алгоритма нажмите «Проверить и собрать». Генератор:

  1. Парсит текст алгоритма через встроенный парсер Python AST.
  2. Проверяет, что все идентификаторы разрешены (объявлены в портах, параметрах или состояниях).
  3. Подставляет default-значения и проверяет синтаксическую корректность.
  4. Генерирует питоновский скелет компонента с заглушками для всех внутренних переменных.
  5. Открывает доступ к редактированию сгенерированного кода на вкладке «Код».

Если хотя бы один шаг возвращает диагностику, файл не сохранится до устранения проблем.

9. Шаг 5. Вкладка «Код» — Python в тегах mars:user-*

Дописываем код
Дописываем код

Вкладка «Код» для логического слоя устроена также как и для объектного. Здесь доступны три специальных XML-тега, в которые вставляется пользовательский Python-код (не доступен тег блока невязок):

ТегНазначениеКогда использовать
<mars:user-imports>Блок импортов сторонних пакетов.Если используются numpy, scipy, sympy, pandas, scikit-learn, либо собственные модули.
<mars:user-helpers>Блок вспомогательных функций и констант.Длинные табличные коэффициенты, формулы физики, утилиты.
<mars:user-compute>Вычислительный блок (тело функции шага).Сложные расчёты, локальные if, вложенные циклы, генераторы.

Генератор сам формирует вокруг этих блоков всю обвязку:

Код (python)
# Автоматически сгенерированная шапка:
from mars_core.component_sdk import component
from mars_core.component_spec import ComponentSpec, Port, Param, AutomationSpec, ...

SPEC = ComponentSpec(
    name="my_pid",
    layer="logic",
    folder="Управление",
    ports=[Port("setpoint", dir="in"), Port("meas", dir="in"),
           Port("u", dir="out")],
    params=[Param("Kp", 0.8, "—"), Param("Ki", 0.05, "—"),
            Param("Kd", 0.1, "—"), Param("dt_min", 0.0, "с")],
    # ... и т.д. из вкладок Порты и Параметры
)

# <mars:user-imports>
import math                     # ← пользовательский импорт
# </mars:user-imports>

# <mars:user-helpers>
def _clamp(x, lo, hi):
    return max(lo, min(hi, x))  # ← пользовательская функция
# </mars:user-helpers>

@component("my_pid", inp="meas", out="u", spec=SPEC)
def my_pid(io):
    # <mars:user-compute>
    sp = io.read("setpoint", 0.0)
    meas = io.read("meas", 0.0)
    e = sp - meas
    Kp = io.param("Kp", 0.8)
    Ki = io.param("Ki", 0.05)
    Kd = io.param("Kd", 0.1)
    integral = io.state.get("integral", 0.0)
    de = (e - io.state.get("prev_error", 0.0))
    integral += e * io.dt
    u = Kp * e + Ki * integral + Kd * de / max(io.dt, 1e-9)
    u = _clamp(u, -100.0, 100.0)
    io.state["integral"] = integral
    io.state["prev_error"] = e
    io.write("u", u)
    # </mars:user-compute>

9.1. Когда нужно дописывать код вручную

  1. Подключение сторонних пакетов: numpy.linalg, scipy.signal, pandas.read_csv, модели scikit-learn — все они разрешены «из коробки», импорт делается в <mars:user-imports>.
  2. Сложные многоступенчатые алгоритмы: если алгоритм содержит вложенные for, списковые включения, генераторы табличных коэффициентов — их удобнее оформить в виде вспомогательной функции в <mars:user-helpers>.
  3. Локальные физико-математические формулы: например, реализация обратной матрицы, формулы перевода единиц, уравнения состояния.
  4. Использование .pyd/.so модулей — при необходимости подключения собственных скомпилированных библиотек их .pyd-файл размещается в одной папке с компонентом и импортируется обычным способом.

9.2. Таблица API io

Это полный контракт интерфейса, читаемый внутри функции шага.

МетодВозвратСемантика
io.read(port, default)floatСчитывает значение входного вывода. Сигнал с провода имеет приоритет над одноимённым параметром.
io.param(key, default)floatСчитывает числовой параметр. Нечисловые значения безопасно заменяются на default.
io.text(key, default)strСчитывает строковый параметр, например mode="sine".
io.flag(key, default)boolСчитывает логический флаг (true/false, 1/0, yes/no).
io.array(key, default)list[float]Считывает массив из JSON или CSV.
io.pair(key, default)tuple[float, float]Считывает двухэлементный кортеж (min, max).
io.table(key, default)list[tuple[float, float]]Считывает табличную кусочно-линейную характеристику (список пар точек).
io.write(port, value)NoneЗаписывает вычисленное значение в выходной порт (скаляр, вектор или словарь).
io.integrate(name, rate, x0)floatВыполняет шаг явного интегрирования Эйлера: $x_{n+1} = x_n + \text{rate} \cdot \Delta t$.
io.statedictСловарь персистентной памяти, сохраняемый между тиками и пересчётами.
io.tsint/floatТекущий индекс тика временной сетки (0, 1, 2, …).
io.dtfloatВеличина шага дискретизации по времени.

9.3. Импорт сторонних модулей

Генератор автоматически разрешает импорты четырёх встроенных пакетов: scipy, numpy, sympy, scikit-learn, pandas. Все они доступны без дополнительной настройки. Иные пакеты требуют размещения скомпилированного .pyd-файла в папке компонента:

Исходный код / фрагмент конфигурации
bmk/<group>/my_pid/
    ├── my_pid.py        # код компонента
    ├── my_pid.pyd       # скомпилированный собственный модуль
    └── tests/

После редактирования нажмите «Проверить код» — генератор перепарсит AST и убедится, что все импорты разрешены.

9.4. Ограничения

  • Тело функции должно быть чистым: без глобальных мутаций и без print. Шина может вызывать компонент параллельно.
  • Состояние хранится только в io.state. Глобальные переменные Python-модуля дают утечку между экземплярами.
  • Длинные табличные коэффициенты лучше выносить в отдельный файл .npz / .csv и читать через io.array или io.table — иначе def разрастается до десятков килобайт.
  • Запрещается явное использование requests, subprocess, socket — вычислительное ядро изолировано от внешних сервисов.

10. Шаг 6. Вкладка «Внешний вид»

Редактируем внешний вид
Редактируем внешний вид

Здесь задаётся векторное обозначение компонента, отображаемое в логической палитре и на холсте логического слоя. Функциональность идентична объектному слою (см. соответствующий раздел guide_object.md), но с двумя особенностями:

  1. Символ на теле блока. Для логических блоков принято использовать функциональные обозначения: треугольник для усилителей, прямоугольник с буквой S для сумматоров, прямоугольник с ∫ для интеграторов, прямоугольник с ∫ и pid для ПИД-регуляторов.
  2. Стрелки направления. В отличие от объектного слоя, на логических блоках явно рисуются стрелки in / out, чтобы инженер не ошибся при подключении. Генератор рисует их автоматически, но внешний вид можно скорректировать вручную.

Рекомендации

  • Длина подписей портов — не более 4–6 символов (in, out, u, y, pid_in).
  • Символ тела — опознаваемый, в верхнем регистре: PID, FF, EMA, SRC.
  • Точки подключения должны точно совпадать со сторонами, указанными на вкладке «Порты».

11. Шаг 7. Вкладка «Документация»

Заполняем информацию
Заполняем информацию

Что здесь задаётся: паспорт компонента. Функциональность идентична объектному слою (guide_object.md).

Минимальный набор полей

  • Поле "Краткое описание". Что можно писать:
АспектОписание
НазначениеКраткое (1–2 предложения) описание того, что моделирует компонент.
Область примененияГде уместно его использовать, с указанием типовых схем.
  • Поле "Внешние зависимости". Здесь необходимо указывать требуемые библиотеки для работы компонента.
  • Поле "Связанные компоненты". Указываются компоненты, связанные с описываемым, или рабоающие с ним в связке.
  • Поле "Замечания". Что можно писать:
АспектОписание
Алгоритм работыКраткое описание принципа работы
Ограничения моделиДиапазоны применимости, критичные условия (Re, ΔP, T).
Версия и авторКто и когда создал компонент. Используется для отслеживания изменений.
  • Поле "Ссылки на источники". Указывется ссылка на литературный источник, детально описывающий компонент.

Что указать обязательно

  • Суть алгоритма — даже если она очевидна из кода. Формула нужна для рецензирования без запуска Python.
  • Требования к шагу dt — устойчивость явного Эйлера зависит от выбора dt. Если компонент содержит интегратор с большим коэффициентом усиления, укажите минимально допустимый шаг.
  • Безопасное значение safe_value — особенно для исполнительных механизмов (вентилятор, клапан). При остановке сессии шина переводит выход в safe_value (см. п. 14 «Частые сценарии»).

12. Шаг 8. Контроль готовности и место сохранения

Перед регистрацией выполните финальную проверку.

Чек-лист готовности

  1. Машинное имя — латиница, без пробелов, уникальное.
  2. Слой — переключатель стоит в положении «Логический».
  3. Список портов соответствует логике алгоритма: каждый io.read("…") имеет одно объявление Port со стороной и направлением.
  4. Список параметров покрывает все io.param(...) / io.text(...) / io.flag(...) / io.array(...) / io.pair(...) / io.table(...).
  5. Состояния объявлены, если алгоритм обращается к io.state.
  6. Алгоритм записан и проходит проверку.
  7. Код проходит проверку синтаксиса и импортов.
  8. Глиф не выходит за габариты портов.
  9. Паспорт заполнен, ограничения указаны.

Выбор папки сохранения

Логические компоненты публикуются в пользовательскую папку bmk/:

Исходный код / фрагмент конфигурации
poc/bmk/<group>/<component_name>/
    ├── <component_name>.py     # код компонента
    ├── <component_name>.cpp    # опционально, для C++ версии
    ├── <component_name>.tex    # документация (паспорт)
    ├── glyph.svg               # внешний вид (опционально)
    └── tests/
        └── test_<component_name>.py

13. Шаг 9. Регистрация и проверка появления в БМК

Находим компонент в БМК
Находим компонент в БМК

По нажатию кнопки «Зарегистрировать» генератор выполняет конвейер регистрации (идентичен объектному слою):

  1. Сохранение исходников в папку bmk/<group>/<component>/. Формируются:
  2. <name>.py — код с ComponentSpec (где layer="logic") и декоратором @component;
  3. <name>.tex — паспорт;
  4. glyph.svg — глиф;
  5. test_<name>.py — заготовка юнит-теста.
  6. Вызов регистратора — компилирует код, импортирует модуль и регистрирует блок в реестрах REGISTRY, _RESIDUALS, _PHYSICAL_SPECS. Это возможно без перезапуска сервера (исполняемого приложения ORPHYS).
  7. Генерация записи catalog.json с метаданными для палитры.
  8. Сборка обновлённого клиентского каталога.
  9. Пересборка production-бандла (по необходимости).
  1. Запуск автотестов.
  2. Визуальная проверка: новый блок появляется в палитре логического слоя в указанной папке.

Как убедиться, что регистрация прошла успешно

  • В дереве палитры логического слоя отображается новая запись.
  • В инспекторе свойств видны параметры с правильными единицами.
  • При запуске минимальной пробной схемы Константа → мой_блок → Индикатор расчёт не падает, индикатор показывает значение.

14. Частые сценарии алгоритмов

Здесь приведены примеры логических компонентов.

14.1. Преобразователь вход → выход (датчик температуры)

Линейная функция $T = R \cdot \text{gain} + \text{offset}$:

Код (python)
@component("datchik_temperatury", inp="R_Ohm", out="T_C")
def datchik(io):
    x   = io.read("R_Ohm", 100.0)
    g   = io.param("gain", 2.5974)
    off = io.param("offset", -259.74)
    io.write("T_C", x * g + off)

14.2. Источник гармонического сигнала во времени

Без входа; выход строится на базе счётчика тиков io.ts:

Код (python)
import math
from mars_core.component_sdk import component

@component("istochnik_znacheniya", inp="p_in", out="value")
def src(io):
    base = io.param("base", 25.0)
    amp  = io.param("amplitude", 20.0)
    freq = io.param("freq", 0.1)
    val  = base + amp * math.sin(2.0 * math.pi * freq * io.ts)
    io.write("value", val)

14.3. Мультирежимный источник с переключением во время выполнения

Строковый режим io.text("mode", "") с числовым резервом mode_id:

Код (python)
import math
from mars_core.component_sdk import component

@component("multimod_source", inp="p_in", out="value")
def multimod_source(io):
    base  = io.param("base", 25.0)
    amp   = io.param("amplitude", 20.0)
    freq  = io.param("freq", 0.1)
    slope = io.param("slope", 1.0)
    t0    = io.param("t0", 0.0)

    modes = {"const": 0, "ramp": 1, "sine": 2, "square": 3, "step": 4}
    m = modes.get(io.text("mode", ""), int(io.param("mode_id", 2.0)))

    if   m == 0: v = base
    elif m == 1: v = base + slope * io.ts
    elif m == 3: v = base + amp * (1.0 if math.sin(2*math.pi*freq*io.ts) >= 0 else -1.0)
    elif m == 4: v = base + (amp if io.ts >= t0 else 0.0)
    else:        v = base + amp * math.sin(2 * math.pi * freq * io.ts)

    io.write("value", v)

14.4. Линейный ограничитель (Limiter)

Надёжная параметризация с безопасными дефолтами:

Код (python)
from mars_core.component_sdk import component

@component("limiter", inp="x", out="y")
def limiter(io):
    x  = io.read("x", 0.0)
    k  = io.param("k", 1.0)
    lo = io.param("min", 0.0)
    hi = io.param("max", 100.0)
    y  = max(lo, min(hi, x * k))
    io.write("y", y)

14.5. Составные типы данных: массивы, пары, таблицы

Полиномиальный фильтр с кусочно-линейной характеристикой:

Код (python)
from mars_core.component_sdk import component

@component("poly_filter", inp="x", out="y")
def poly_filter(io):
    x = io.read("x", 0.0)
    coeffs = io.array("coeffs", [0.0])
    lo, hi = io.pair("range", (-100.0, 100.0))
    table  = io.table("curve", [])

    y = 0.0
    p = 1.0
    for c in coeffs:
        y += c * p
        p *= x
    y = max(lo, min(hi, y))
    io.write("y", y)

14.6. EMA-фильтр (экспоненциальное скользящее среднее)

Демонстрация работы с io.state для алгебраических фильтров:

Код (python)
from mars_core.component_sdk import component

@component("ema", inp="x", out="y")
def ema(io):
    x = io.read("x", 0.0)
    alpha = io.param("alpha", 0.2)
    # При первом тике состояние инициализируется значением входа.
    y_prev = io.state.get("y", x)
    y_curr = y_prev + alpha * (x - y_prev)
    io.state["y"] = y_curr
    io.write("y", y_curr)

14.7. Интегратор с утечкой (термоаккумулятор)

Интегрирование через io.integrate с явным накоплением:

Код (python)
from mars_core.component_sdk import component

@component("thermal_storage", inp="Q", out="T")
def thermal_storage(io):
    q        = io.read("Q", 0.0)
    capacity = io.param("C", 1000.0)
    ambient  = io.param("Tamb", 20.0)
    loss     = io.param("k", 5.0)

    t_prev   = io.state.get("T", io.param("T0", ambient))
    dtdt     = (q - loss * (t_prev - ambient)) / capacity
    t_next   = io.integrate("T", dtdt, x0=t_prev)
    io.write("T", t_next)

14.8. Управляемый вентилятор со спецификацией автоматизации

Шаблон с AutomationSpec (команды, телеметрия, безопасное значение):

Код (python)
from mars_models_sdk import (
    component, ComponentSpec, Port, Param,
    AutomationSpec, AutomationCommand, AutomationTelemetry,
)

SPEC = ComponentSpec(
    name="fan_object",
    kind="Управляемый вентилятор",
    sym="FAN",
    layer="logic",
    folder="Пользовательские/Приводы",
    ports=[
        Port("cmd", dir="in",  side="left"),
        Port("rpm", dir="out", side="right"),
    ],
    params=[
        Param("rpm_nominal", 1500.0, "об/мин", type="number"),
        Param("enabled", True, type="bool"),
    ],
    automation=AutomationSpec(
        commands=(
            AutomationCommand(
                id="set_cmd", label="Команда скорости",
                target_attr="cmd", value_type="number",
                minimum=0.0, maximum=1.0, step=0.01,
                safe_value=0.0,
            ),
        ),
        telemetry=(
            AutomationTelemetry(
                id="rpm", label="Текущие обороты",
                source="rpm", unit="об/мин",
            ),
        ),
    ),
)

@component("fan_object", inp="cmd", out="rpm", spec=SPEC)
def fan_object(io):
    cmd = max(0.0, min(1.0, io.read("cmd", 0.0)))
    if not io.param("enabled", 1.0):
        cmd = 0.0
    rpm = cmd * io.param("rpm_nominal", 1500.0)
    io.write("rpm", rpm)

14.9. ПИД-регулятор с bumpless transfer

Типовая конструкция для замкнутых контуров управления:

Код (python)
@component("pid", inp="meas", out="u")
def pid(io):
    sp  = io.read("setpoint", 0.0)
    err = sp - io.read("meas", 0.0)
    Kp  = io.param("Kp", 0.8)
    Ki  = io.param("Ki", 0.05)
    Kd  = io.param("Kd", 0.0)
    dt  = max(io.dt, 1e-9)

    integral = io.state.get("i", 0.0) + err * dt
    de       = (err - io.state.get("prev_err", err)) / dt
    u        = Kp * err + Ki * integral + Kd * de

    io.state["i"]        = integral
    io.state["prev_err"] = err
    io.write("u", u)

14.10. Адаптер для связи с объектным слоем (l_device)

Один из самых важных компонентов: читает метрики и формирует команды для объектного аппарата:

Код (python)
@component("l_device", inp="cmd", out="value")
def l_device(io):
    target = io.text("device", "")
    bind   = io.text("bind", "")
    kind   = io.text("role", "out")
    bus    = io.state.get("__bus", None)

    if kind == "in":
        # Записать команду в объектный аппарат:
        cmd_value = io.read("cmd", 0.0)
        io.write("pushed_cmd", cmd_value)
        # Шина применит патч к объектному аппарату через __series.
    else:
        # Прочитать метрику аппарата через служебный узел __series.
        metric = io.state.get("__metric_" + bind, 0.0)
        io.write("value", metric)

15. Типичные ошибки и их диагностика

Этот раздел существенно отличается от таблицы ошибок объектного слоя: ошибки логики другие.

СимптомВероятная причинаРешение
Выход блока строго равен 0Имя порта в io.write("port", val) не совпадает с out= спецификации.Привести имя в io.write к объявленному в ComponentSpec.
Сигнал вырождается в константуНеправильная топология: выход подключен к выходу или вход к входу.Провод должен идти от выхода источника (часто справа) к входу потребителя (часто слева).
Сбой при считывании строкового режимаСтроковый параметр читается числовым методом io.param.Использовать io.text("mode", "") для строк и io.flag("on", False) для булевых.
Память затирается при перерисовке холстаИспользование глобальных переменных модуля вместо io.state.Хранить персистентные переменные только в io.state.
KeyError: 'acc' при первом тикеЧтение через io.state["acc"] без default.Заменить на io.state.get("acc", 0.0).
Интегратор «убегает» при перезапуске схемыСостояние интегратора не сбрасывается перед первым тиком.Инициализировать состояние значением параметра T0 (см. thermal_storage).
Ступенька в выходе ПИД при изменении KpРеализация ПИД обнуляет integral при каждом обновлении коэффициентов.Не трогать io.state["i"] при изменении коэффициентов (bumpless transfer).
Импорт собственного .pyd не находится.pyd не лежит в одной папке с <name>.py.Положить скомпилированный файл в ту же папку и проверить, что версия Python совпадает с целевой (3.10 / 3.11).
SyntaxError при «Проверить код»Опечатка в <mars:user-*> тегах или в отступах внутри них.Не смешивать табуляцию и пробелы; каждый тег ровно один раз.
Провод от выхода висит в воздухе, но ошибки нетВалидатор пропустил незаземлённый логический выход (это нормально — он просто не используется).Удалить лишний провод или подключить к нагрузке.
Один и тот же параметр перезаписывается конфликтующими источникамиКонкурентная запись из ПИД + ручной ввод + сценарий ИСУЛ без приоритетов.В AutomationCommand задать priorityClass; в сценариях ИСУЛ — policy.
Расхождение результатов Python и C++Различия в формулах или дефолтных значениях params.Поддерживать идентичность алгоритмов в .py и .cpp.

16. Приёмочные испытания после регистрации

После того как компонент зарегистрирован и до передачи его коллегам рекомендуется выполнить серию проверок.

16.1. Регистрация в реестре шины

Код (python)
import mars_core.component_host
from mars_core.dispatcher import REGISTRY

assert "my_pid" in REGISTRY

Если REGISTRY не экспортируется напрямую — посмотрите dir() на импортированном component_host.

16.2. Численное базовое тестирование шага

Подайте искусственные входы, рассчитайте ожидаемое значение и сравните:

Код (python)
io = make_io(setpoint=50.0, meas=55.0, Kp=0.8, Ki=0.05, Kd=0.0,
             dt=0.01, prev_err=0.0, i=0.0)
u = pid(io)
assert abs(u - (-0.8 * 5.0 + 0.05 * (-0.05))) < 1e-9

Здесь make_io — тестовая обёртка, формирующая совместимый контекст. В реальной кодовой базе такая утилита обычно лежит в tests/_harness/.

16.3. Интеграционный тест на схеме

Соберите минимальную пробную схему:

Исходный код / фрагмент конфигурации
Константа (setpoint=50) → PID → Индикатор "u"
                ↑
Датчик (meas=…) ┘

Запустите расчёт и убедитесь:

  • при meas = setpoint выход u стабилизируется около 0 (или значения, которое даёт интегральная составляющая);
  • при ступенчатом изменении meas выход реагирует с характерной ПИД-кривой (бросок → затухание → стабилизация);
  • состояние ПИД не сбрасывается при изменении связей в инспекторе (проверка bumpless transfer).

16.4. Регрессионное тестирование в pytest

Поместите тесты в tests/test_<имя_компонента>.py. Минимальный набор:

  1. Регистрация: assert "<name>" in REGISTRY.
  2. Аналитический баланс: при известных значениях выход равен расчётному.
  3. Память: интегратор и фильтр сохраняют io.state между тиками.
  4. Типовая ошибка топологии: попытка соединить два выхода должна приводить к ошибке валидатора (если хочется автоматизировать этот контроль).

17. Управление жизненным циклом

Обновление дефолтов параметров

Изменение значений по умолчанию безопасно: влияет только на вновь размещаемые блоки. Уже размещённые экземпляры хранят свои значения в attrs каждого блока.

Переименование машинного имени порта

Изменение алгоритма

Переписывание тела функции допустимо после публикации — внутренняя логика изолирована от топологии. Однако:

  • если вы изменяете список читаемых параметров — синхронно обновите вкладку «Параметры» в ComponentSpec;
  • если вы добавляете новое состояние — опишите его на вкладке «Параметры» как дифференцируемый параметр;
  • сохраните обратную совместимость: инспектор уже размещённых блоков не должен показывать незнакомые поля (для этого используются дефолты).

Удаление компонента

  1. Удалите файлы из bmk/<group>/<component>/.
  2. Запустите среду моделирования.
  3. Убедитесь, что активные проекты не содержат экземпляров компонента (иначе — ошибка валидатора при загрузке).

Конкурентная запись параметров в реальном времени

Один и тот же параметр исполнительного механизма (например, cmd) не должен одновременно перезаписываться регулятором, сценарием ИСУЛ и ручным вводом из инспектора. Для разрешения конфликта используются приоритетные классы управления (priorityClass в AutomationCommand) и политики сценария в ИСУЛ.

18. Краткая шпаргалка

18.1. Минимальный обязательный набор полей в генераторе

Исходный код / фрагмент конфигурации
Порты       — минимум один вход или один выход
Параметры   — минимум один числовой параметр (или пусто)
Алгоритм    — строки с read/param/state/integrate/write
Код         — пусто (генератор сам подставит декоратор и SPEC)
Внешний вид — пусто (стандартный прямоугольник)
Документация     — хотя бы краткое описание назначения

18.2. Эквивалент «вкладка ↔ код»

Вкладка генератораКод
Имя, папкаComponentSpec(name=..., kind=..., folder=..., layer="logic")
Портыports=[Port(...), ...]
Параметрыparams=[Param(...), ...]
Состоянияпаспорт io.state (в коде — словарь, ключи создаются по имени)
Алгоритм (пресеты)io.read(...), io.write(...), io.param(...), io.state.get(...), io.integrate(...), io.text(...), io.flag(...), io.ts
Код (<mars:user-imports>)блок import/from
Код (<mars:user-helpers>)вспомогательные def
Код (<mars:user-compute>)тело step(io)
АвтоматизацияAutomationSpec(commands=..., telemetry=...)

18.3. Сводка «когда какой слой выбирать»

ЗадачаСлойДекоратор
ПИД-регулятор, фильтр, логика, преобразованияL@component
Резистор, труба, клапан, источникC@physical
Адаптер между слоямиL (l_device)@component
Виджет индикатораV(визуальный слой)
Узел графа алгоритма контроллераСГМС(свой стек)
Лабораторная / сценарийИСУЛ(свой стек)
Раздел 10

Создание компонента объектного уровня

Практическое руководство по разработке физических компонентов объектного слоя (C-слоя) в среде моделирования ORPHYS 2.0 с помощью визуального «Генератора компонентов».

Расширенное практическое руководство по разработке физических компонентов объектного слоя (C-слоя) в среде моделирования ORPHYS 2.0 с помощью визуального «Генератора компонента».

1. Принцип работы объектного компонента

В отличие от логического блока, который «вычисляет выход из входа», физический компонент объектного слоя формирует уравнение физического закона в форме невязки:

Исходный код / фрагмент конфигурации
F(t, X, dX/dt) = 0

Здесь:

  • X = [V(узлы), I(ветви)] — общий вектор состояния, формируемый численным ядром, где V - потенциальная переменная (ток, расход, сила), I - потенциальная (напряжение, давление, скорость);
  • dX/dt — производные по времени для динамических систем;
  • t — текущее физическое время интегрирования.

Ядро решателя, а не компонент, вычисляет токи и потенциалы. Компонент лишь сообщает ядру, какое физическое соотношение связывает потенциалы его полюсов и поток в его ветви. Решатель синхронно находит узловые потенциалы и ветвевые потоки всей схемы методом Ньютона с LU-факторизацией разреженной матрицы Якоби (SuperLU). Поэтому вклад компонента — это одно уравнение невязки ветви для двухполюсника (плюс дополнительные уравнения для внутренних состояний и многопортовых аппаратов).

Эта особенность диктует три практических следствия:

  1. Каждый физический закон записывается в инженерной форме «должно быть равно нулю», а не в форме присваивания.
  2. Уравнения непрерывности потока (KCL) в узлах уже заложены в модель — их записывать не нужно, иначе система окажется переопределённой.
  3. Динамика объявляется явно: при написании кода компонента для dP/dt или dU/dt указывается diff="nodes", для dG/dt или dI/dt — diff="branch". Без этого флага решатель не выделит производную. При разработке модели в генераторе это деалется автоматически.

2. Архитектурный контракт и общая схема регистрации

Полный путь от определения модели до видимости компонента в палитре библиотеки модели компонентов (БМК) состоит из двух уровней регистрации.

УровеньНазначениеАртефакт
Палитра/UIСодержит метаданные, видимые в левой панели, инспекторе свойств и подсказках.data/catalog.json + артефакт сборки generated_catalog.js.
Вычислительное ядроСодержит исполняемый код невязки, регистрируется декоратором @physical.Скомпилированный файл в bmk/....

Цепочка регистрации выглядит так:

Исходный код / фрагмент конфигурации
Генератор модели компонента
   ↓ Спецификация компонента + код вычисления невязки
Модуль регистрации
   ↓ авторегистрация в момент импорта
Реестры ядра (_RESIDUALS, _PHYSICAL_SPECS)
   ↓
Перезапуск клиента среды
   ↓
Сборка и размещение в палитре БМК

В генераторе весь этот конвейер свёрнут в одну пользовательскую операцию — кнопку «Зарегистрировать». Далее в документе описано, что именно происходит на каждой вкладке генератора.

3. Домены физики: что и почему

Объектный слой ORPHYS — акаузальный: один и тот же решатель обслуживает несколько физических доменов. Для каждого домена закреплена пара потенциальная / потоковая переменные (across/through).

Домен (domain)Across (потенциальная)Through (потоковая)Типичные аппараты
electrical (по умолчанию)Напряжение V, ВТок I, АРезистор, конденсатор, индуктивность, источник ЭДС, диод
hydraulicДавление P, ПаМассовый расход G, кг/сТруба, клапан, ёмкость, АВО, инерционность столба жидкости
thermalТемпература T, К или °CТепловой поток Q, ВтТеплообменник, аккумулятор тепла, радиатор
oilgasДавление P, ПаМассовый расход G, кг/сСепаратор, ректификационная колонна, теплообмен с фазовым переходом
mechanicalСкорость v, м/сСила F, НОдномерная поступательная система
mechanical_2dКоординаты (x, y, θ)Силы и момент (F_x, F_y, M)Плоская кинематика, манипуляторы
kinematic_3dПространственные (X, Y, Z)Внутренние реакцииПространственные шарнирные механизмы

Кроме того, домены oilgas и thermal дополнительно требуют тепловую модель (см. п. 8.4) — это профили flow_through или accumulation, которые автоматически генерируют уравнения переноса тепла/состава. Домен electrical и hydraulic обычно обходятся одной невязкой ветви без расширений.

4. Подготовка: что нужно знать до запуска генератора

Прежде чем нажимать «Запустить генератор», целесообразно зафиксировать на бумаге следующие позиции. Это сокращает число возвратов к редактированию вкладок и снижает риск переименования идентификаторов (см. п. 15).

  1. Машинное имя (bus id): латиница, без пробелов, уникальное в пределах библиотеки. Используется решателем для сопоставления блока с функцией невязки. Не должно меняться после публикации.
  2. Читаемое имя и обозначение на теле блока (sym) — то, что увидит пользователь в палитре и на корпусе компонента.
  3. Список портов с указанием:
  4. имени (машинное);
  5. направления (in/out);
  6. стороны (left, right, top, bottom);
  7. физического домена.
  8. Список параметров: имя, тип, значение по умолчанию, единица измерения, границы (для int/number).
  9. Физический закон в инженерной форме — уравнение связи across и through для выбранного домена. Закон записывается как «разность должна быть равна нулю».
  10. Признаки динамики: содержит ли модель производные по времени, и если да — к какой переменной они относятся (потенциалу узлов или потоку ветви).
  11. Признак нелинейности: квадратичные потери, экспоненты, произведения неизвестных — всё это потребует nonlinear=True.
  12. Признак многопортовости: только если на портах должны быть независимые through-переменные (тройник, многообмоточный трансформатор, общий коллектор).
  13. Признак накоплнения среды: для нефтегазовых аппаратов потребуется выбор пресета flow_through / accumulation и входной порт для StreamProfile.
  14. Признак опции V-слоя: какие из параметров нужно открыть как пользовательские настройки визуального слоя.

Если хотя бы один из пунктов остался неясным, см. раздел 14 «Частые сценарии» — там приведены эталонные шаблоны для типовых аппаратов.

5. Шаг 1. Запуск «Генератора компонента»

Запускаем
Запускаем

Запуск производится из меню «Сервис» - «Генератор компонента» основного интерфейса ORPHYS (см. архитектурное руководство, «Инженерный инструментарий» → «визуальный конструктор компонентов»).

После запуска откроется окно с набором вкладок:

  • Порты;
  • Параметры;
  • Невязки;
  • Код;
  • Внешний вид;
  • Документация.

Заполнять вкладки рекомендуется в порядке Порты → Параметры → Невязки → Код → Внешний вид → Документация — так поля более поздних вкладок получают корректные ссылки на ранние сущности.

6. Шаг 2. Вкладка «Порты»

Заполняем порты
Заполняем порты

Что здесь задаётся: интерфейс компонента, который будет виден на холсте для подключения проводов и валидации при сборке схемы. С точки зрения архитектуры это поля ports= дескриптора ComponentSpec.

Минимальные поля одной записи порта

ПолеНазначениеПример
nameМашинное имя — уникальный идентификатор в пределах компонента. Используется в формулах невязок.inlet, outlet, cmd, T_prod_out
dirНаправление сигнала относительно компонента: in (вход) или out (выход). Влияет только на визуализацию — на физическом уровне акаузальная модель соединяет все полюса по общему узлу.in
sideСторона блока, на которой рисуется вывод: left, right, top, bottom. Правильное расположение критично для читаемости схем.left
descriptionЧеловеческое описание, отображается в инспекторе и подсказке.«Входной патрубок, сторона продукта»

Практические замечания

  • Количество портов определяется физикой процесса. Для двухполюсной ветви (труба, клапан, резистор) порты два: вход и выход. Для ёмкости между двумя узлами давления — также два, для спепаратора - три. Тройник — три и так далее.
  • Сторону подключения к сети имеет смысл выбирать так, чтобы поток среды на схеме шёл слева направо (inlet → left, outlet → right), это согласуется с автоматическим алгоритмом расчёта размера блока.
  • Переименование name после публикации компонента приведёт к потере привязок в ранее сохранённых проектах, сценариях ИСУЛ и алгоритмах СГМС — это миграционно-чувствительная операция.
  • Если компонент реализует наполнение материи по потоковой переменной (сепаратор с удержанием массы, ректификационная колонна, теплообменник с аккумуляцией), отметьте галочки:
  • «Независимые расходы портов» — для многопортового аппарата, у которого потоки на портах не равны (накопитель имеет отдельный вход и отдельный выход).
  • «Накопитель среды» — для аппаратов с гидродинамическим удержанием массы; разрешает генерацию дифференциального уравнения накопления. Эта опция автоматически расширяет размерность DAE на одно состояние.

Расширенный сценарий: «внешние переменные»

Для аппаратов, которые будут управляться из логического слоя или читаться им (например, для АВО нужны fan_speed и T_product_out для получения от регулятора целевого значения скорости вращения и передачи текущего значения температуры продукта на выходе). Таки переменные задаются как параметры с отметкой о публикации для V-слоя. Передача данных между L- и C- уровнями осуществляется на схеме с помощью компонента "Связь с аппаратом". То есть для передачи значения параметра / переменной на V- или L- уровень не требуется заводить специальный порт, достаточно завести параметр, публикуемый для передачи данных.

7. Шаг 3. Вкладка «Параметры»

Заполняем параметры
Заполняем параметры

Что здесь задаётся: коэффициенты, настройки и граничные условия компонента — поля params= дескриптора ComponentSpec и атрибуты блока attrs= в сериализации схемы.

Состав полей одной записи параметра

ПолеНазначение
nameМашинное имя атрибута, по которому параметр читается внутри невязки через c["attrs"].get("name", default).
value (default)Значение по умолчанию. Должно быть валидным для типа.
unitЕдиница измерения в свободной строковой форме: «Па/(кг/с)», «м», «об/мин». Влияет на отображение в инспекторе, на расчёт не влияет.
typeТип данных (см. ниже).
descriptionТекстово описание параметра.
publishФлаг «публиковать параметр для V-слоя». Если включён, значение выводится в визуальные виджеты (табло, графики) без дополнительных адаптеров.

Поддерживаемые типы параметров

typeНазначениеПример
numberВещественный скаляр (по умолчанию).Сопротивление R = 1000.0.
intЦелое число.Число секций теплообменника N = 12.
stringСтроковый режим работы, метка или путь.Режим АВО "ventilation".
boolЛогический флаг.enabled = True.
comboВыбор из фиксированного списка.Тип рабочей жидкости (вода, нефть, газ).
arrayМассив коэффициентов.Таблично заданная характеристика насоса.
pairКортеж (min, max) для отображения диапазона.Допустимые пределы давления (0, 10 МПа).
tableТаблица пар (x, y) для кусочно-линейной интерполяции.Реализация внешней Q-H характеристики насоса.

Дифференцируемые параметры (внутренние состояния)

Для моделей с памятью (ёмкость с инерцией давления, термоаккумулятор, гистерезисный клапан) на этой же вкладке добавляются состояния (states):

  • name — имя состояния, доступное в формулах невязок;
  • default — начальное условие x(t=0);
  • unit, description — для инспектора;
  • publish — публикация состояния как метрики на V-слой.

Тепловая модель

Для компонентов теплогидравлического и нефтегазового доменов в нижней части вкладки присутствует секция «Тепловая модель» с пресетами геометрии: кожухотрубный, пластинчатый, спиральный, произвольный.

Параметры:

  • длина трубок
  • диаметр трубок
  • количество трубок
  • толщина стенки
  • теплопроводность стенки
  • сопротивление загрязнений
  • схема внешнего контура: противоток, прямоток, перекрёстный ток.

8. Шаг 4. Вкладка «Невязки» (главный шаг)

Заполняем уравнения
Заполняем уравнения

Это центральный шаг разработки компонента. Здесь формулируется математический закон, на основании которого ядро будет включать компонент в совместно решаемую DAE-систему. Ошибки в этой вкладке дают трудно диагностируемые расходимости численного решателя.

8.1. Базовая форма записи уравнения

В генераторе формула вводится как обычное математическое выражение в правой части уравнения F = 0, то есть в инженерной форме «разность, которая должна обратиться в нуль». Например, для гидравлического сопротивления:

Исходный код / фрагмент конфигурации
P_in - P_out - R * G = 0

В генераторе используется математический движок MathLive / SymPy, что позволяет:

  • вводить формулы визуально (WYSIWYG), в том числе с автозаменой Vdot → $\dot{V}$;
  • использовать автоматическое символьное дифференцирование для построения якобиана (см. п. 8.6);
  • проверять размерности в символьном виде (при наличии описаний единиц).

8.2. Доступные идентификаторы в формулах

В формулах допустимо использовать:

  • Имена портов в виде P_in, P_out, V1, V2 — те читают potential соответствующего полюса;
  • Имена параметров, объявленных во вкладке «Параметры»: R, tau, mu, и т.п.;
  • Глобальный поток ветви G (для гидравлики/нефтегаза) или I (для электрики);
  • Производные: dP_dt, dG_dt — в зависимости от установленного флага diff;
  • Внутренние состояния (если объявлены): Pf, alarm — текущие значения; dPf_dt, dalarm_dt — их производные.

Идентификаторы портов, параметров и состояний подставляются генератором автоматически из соответствующих вкладок. Допускается также свободный ввод (объявленных, но не новых) имён.

8.3. Конститутивные уравнения для типовых доменов

В таблице ниже приведены канонические формы невязки для типовых компонентов.

Физический компонентУравнение невязки ветви ($F=0$)Тип уравнения
Резистор$U_{ab} - R \cdot I = 0$Линейное алгебраическое
Катушка индуктивности$U_{ab} - L \cdot dI/dt = 0$Дифференциальное по ветви
Конденсатор$I - C \cdot dU_{ab}/dt = 0$Дифференциальное по узлам
Источник напряжения$U_{ab} - \lambda \cdot E(t) = 0$Алгебраическое с гомотопией
Источник тока$I - \lambda \cdot J(t) = 0$Алгебраическое с гомотопией
Диод (Шокли)$I - I_s\!\left(\exp\!\left(\dfrac{U_{ab}}{nV_t}\right) - 1\right) = 0$Нелинейное трансцендентное
Центробежный насос$(P_{\text{out}} - P_{\text{in}}) - \lambda \cdot \Delta P(\dot m, t, \rho) = 0$Нелинейное алгебраическое
АВО$(P_{\text{in}} - P_{\text{out}}) - R_h \cdot \dot m = 0$Гидравлическое
Регулирующий клапан$(P_{\text{in}} - P_{\text{out}}) - \dfrac{R_{h,\text{min}}}{\text{opening}^2}\dot m = 0$Нелинейное параметрическое

8.4. Включение динамики: флаг «дифф. переменная»

В нижней части пресетов переменных вкладки «Невязки» находится пресеты по потоковым переменным (например, dG или dP для нефтегазового домена).

Если модель содержит собственные внутренние состояния, не сводящиеся к потенциалам полюсов или сквозному потоку (см. вкладку «Параметры», секция «Дифференцируемые параметры»), их уравнения дописываются отдельной строкой в таблицу невязок.

8.5. Включение нелинейности: флаг «Нелинейная модель»

Любой компонент, чей закон сохранения содержит квадратичные потери, модули, экспоненты или произведения неизвестных (диод, клапан с квадратичным сопротивлением, насос с кубической характеристикой), должен иметь установленный флаг Нелинейная модель. Это эквивалентно nonlinear=True в декораторе и сообщает ядру о необходимости полного пересчёта матрицы Якоби и включения адаптивного демпфирования шага Ньютона.

8.6. Аналитический якобиан (автоматическая генерация)

Если установлен флаг «Аналитический якобиан», генератор с помощью SymPy выполняет символьное дифференцирование формулы по каждой неизвестной (dF/dV_in, dF/dG, dF/dt и т.п.) и встраивает полученные выражения прямо в код невязки.

Когда это помогает: жёсткие нелинейные модели и большие схемы, где численное дифференцирование по раскраске столбцов становится узким местом. Символьный якобиан сокращает количество обращений к уравнениям в 10–30 раз.

Когда это не помогает (или вредит): модели с большим числом условных переходов (if/else, разные режимы работы в зависимости от значений переменных); в этом случае аналитический якобиан придётся дописывать вручную на вкладке «Код».

8.7. Проверка и сборка

После заполнения формулы рекомендуется нажать кнопку «Проверить и собрать». Генератор в этом режиме:

  1. Парсит выражение через SymPy и строит абстрактное синтаксическое дерево.
  2. Проверяет, что все идентификаторы разрешены (объявлены в портах или параметрах).
  3. Подставляет символьные значения по умолчанию и проверяет отсутствие деления на ноль в формуле.
  4. При включённом аналитическом якобиане — выполняет символьное дифференцирование.
  5. Генерирует код Python-функции невязки и помещает его в черновик компонента.

Если хотя бы один шаг возвращает диагностику, формула не сохранится до устранения проблем.

9. Шаг 5. Вкладка «Код» — пользовательская логика на Python

Дописываем код
Дописываем код

На этой вкладке формируется тело функции шага на языке Python — исполняемая часть компонента, которая будет зарегистрирована через @physical(SPEC) в реестре _RESIDUALS ядра.

Здесь доступны 4 специальных XML-тега, в которые вставляется пользовательский Python-код:

ТегНазначениеКогда использовать
<mars:user-imports>Блок импортов сторонних пакетов.Если используются numpy, scipy, sympy, pandas, scikit-learn, либо собственные модули.
<mars:user-helpers>Блок вспомогательных функций и констант.Длинные табличные коэффициенты, формулы физики, утилиты.
<mars:user-compute>Вычислительный блок (тело функции шага).Сложные расчёты, локальные if, вложенные циклы, генераторы.
<mars:user-equation>Блок невязокКод для расчета невязок из вкладки "Невязки" (подставляется автоматически)

9.1. Что именно генерирует вкладка «Невязки»

По нажатию «Зарегистрировать» генератор формирует файл следующей приблизительной структуры:

Код (python)
from mars_core.acausal import physical
from mars_core.component_spec import ComponentSpec, Port, Param

SPEC = ComponentSpec(
    name="hydraulic_resistor",
    kind="Гидравлическое сопротивление",
    layer="object",
    folder="Гидравлика",
    domain="hydraulic",
    ports=[
        Port("inlet",  dir="in",  side="left",  domain="hydraulic"),
        Port("outlet", dir="out", side="right", domain="hydraulic"),
    ],
    params=[Param("R", 1000.0, unit="Па/(кг/с)", type="number")],
)

@physical(SPEC)
def hydraulic_resistor(c, V, I, dV, dI, t, lam):
    a, b = c["nodes"]
    G = I[c["id"]]
    R = float(c["attrs"].get("R", 1000.0))
    return (V[a] - V[b]) - R * G

Пользовательский код на вкладке «Код» — это тело функции между строкой def hydraulic_resistor(c, V, I, dV, dI, t, lam): и return .... Если специальной логики не требуется, генератор сам подставит сгенерированное return из вкладки «Невязки», и вкладку «Код» можно пропустить.

9.2. Когда нужно дописывать код вручную

  • Сложные многоступенчатые алгоритмы (например, итерационный расчёт тепловой характеристики АВО как функции от оборотов и температуры);
  • Модели с условными переходами: if P > P_critical: ...; такие модели плохо выражаются одной формулой невязки;
  • Вспомогательные публикации метрик: можно дополнить словарь publish_metrics (см. ниже);
  • Импорт специализированных численных функций (например, термодинамика библиотеки теплофизических свойств).

9.3. Расширенные сценарии кода

Публикация метрик на V-слой

Для возможности публикации значений переменных на V-слой необходимо на вкладке "Параметры" отметить галочкой чекбокс "Публикуемый". Это генерирует следующий код:

Код (python)
def publish_metrics(c, V, I, t, S):
    a, b = c["nodes"]
    return {
        "flow_kgs": float(I[c["id"]]),
        "dp_pa":    float(V[a] - V[b]),
        "T_prod_out_c": float(S.get("T_out", 0.0)) - 273.15,
    }

@physical(SPEC, publish=publish_metrics)
def hydraulic_resistor(c, V, I, dV, dI, t, lam):
    a, b = c["nodes"]
    G = I[c["id"]]
    R = float(c["attrs"].get("R", 1000.0))
    return (V[a] - V[b]) - R * G
Использование объектного API через Elem

Для моделей с большим числом портов удобно читать потенциалы и потоки через класс Elem:

Код (python)
from mars_core.comp_runtime import Elem
_PORTS = ("inlet", "outlet")
_DIRS  = ("in", "out")
_MULTI = False

def _body(elem):
    inlet, outlet = elem.port("inlet"), elem.port("outlet")
    R = elem.param("R", 1000.0)
    return (inlet.potential - outlet.potential) - R * inlet.flow

@physical(SPEC)
def hydraulic_resistor(c, V, I, dV, dI, t, lam):
    return _body(Elem(c, V, I, dV, dI, t, lam, _PORTS, _DIRS, _MULTI))
Нефтегазовый аппарат с StreamProfile
Код (python)
from mars_core.stream_profile import StreamProfile

SPEC_OG = ComponentSpec(
    name="oilgas_linear_resistance",
    kind="Технологическое сопротивление смеси",
    layer="object",
    folder="Нефтегазовые аппараты",
    domain="oilgas",
    supports_stream=True,
    stream_role="resistance",
    ports=[Port("inlet", dir="in"), Port("outlet", dir="out")],
    params=[Param("R", 1000.0, unit="Па/(кг/с)")],
)

@physical(
    SPEC_OG,
    stream=StreamProfile.flow_through("inlet", "outlet", allow_reverse=True),
    publish=lambda c, V, I, t, S: {
        "flow_kgs": float(I[c["id"]]),
        "dp_pa": float(V[c["nodes"][0]] - V[c["nodes"][1]]),
    },
)
def oilgas_linear_resistance(c, V, I, dV, dI, t, lam):
    a, b = c["nodes"]
    return (V[a] - V[b]) - float(c["attrs"].get("R", 1000.0)) * I[c["id"]]

9.4. Ограничения

  • Тело функции должно быть чистым: без глобальных мутаций и без побочных эффектов. Ядро вычисляет невязки параллельно по цветам столбцов якобиана.
  • Запрещается print/stdout в горячем цикле (снижает производительность).
  • Любые пользовательские import должны находиться в шапке файла.

10. Шаг 6. Вкладка «Внешний вид» — глиф блока

Редактируем внешний вид
Редактируем внешний вид

Здесь задаётся векторное обозначение компонента, которое будет отображаться в палитре и на холсте. Редактор глифов позволяет:

  • загрузить готовый SVG-файл (например, из ГОСТ-обозначений);
  • нарисовать блок вручную из готовых примитивов (прямоугольник, треугольник, линии, текст, точки подключения);
  • установить символ на теле блока (например, R, AVO, PUMP1);
  • привязать подписи портов к их сторонам.

Рекомендации по оформлению

  • Длина подписей портов — не более 8 символов (например, in, out, cmd, fan). Иначе блок придётся расширять, что нарушит читаемость схемы.
  • Символ на теле блока — уникальный и опознаваемый. Лучше использовать ANSI-стандартизованные обозначения для электрических компонентов (R, L, C) и единообразные сокращения для технологических (AVO, PUMP, SEP).
  • Точки подключения должны точно совпадать со сторонами, указанными на вкладке «Порты». Алгоритм canvas.jsx распределяет порты по вертикали с шагом 22 px, начиная с HEAD = 22 px смещения от шапки.

11. Шаг 7. Вкладка «Документация» — паспорт компонента

Заполняем информацию
Заполняем информацию

Что здесь задаётся: карточка-паспорт компонента, видимая в палитре, в инспекторе свойств и в режиме просмотра документации. Эти данные не влияют на расчёт, но критичны для повторного использования компонента в проектах, ИСУЛ-сценариях и при коллективной разработке.

Минимальный набор полей

  • Поле "Краткое описание". Что можно писать:
АспектОписание
НазначениеКраткое (1–2 предложения) описание того, что моделирует компонент.
Область примененияГде уместно его использовать, с указанием типовых схем.
  • Поле "Внешние зависимости". Здесь необходимо указывать требуемые библиотеки для работы компонента.
  • Поле "Связанные компоненты". Указываются компоненты, связанные с описываемым, или рабоающие с ним в связке.
  • Поле "Замечания". Что можно писать:
АспектОписание
Физические допущенияНапример, «ламинарный режим течения», «несжимаемая среда», «пренебрегаем теплотой трения».
Ограничения моделиДиапазоны применимости, критичные условия (Re, ΔP, T).
Версия и авторКто и когда создал компонент. Используется для отслеживания изменений.
  • Поле "Ссылки на источники". Указывется ссылка на литературный источник, детально описывающий компонент.

Советы

  • Заполняйте все ограничения модели — это поможет будущим пользователям избежать ошибочных применений.
  • В графе «Источники» указывайте не только ГОСТ, но и конкретные формулы (см. раздел про шпаргалку нелинейных моделей в архитектурном руководстве — там приведены классические соотношения, на которые можно ссылаться).
  • Длинные описания допустимо выносить в отдельный PDF/Markdown и прикладывать как адрес в интернете как ссылку на источник.

12. Шаг 8. Контроль готовности и место сохранения

Перед регистрацией выполните финальную проверку:

  1. Машинное имя компонента — латиница, без пробелов, уникальное в пределах вашей библиотеки.
  2. Список портов соответствует физической топологии.
  3. Список параметров содержит все необходимые для формулы идентификаторы.
  4. Невязка записана в форме F = 0 (а не присваивания).
  5. Флаги установлены корректно: динамика, нелинейность, материальный поток.
  6. Глиф не выходит за рамки портов.
  7. Паспорт заполнен, ограничения модели указаны.

Выбор папки сохранения

Компоненты публикуются в вашу пользовательскую папку, обычно:

Исходный код / фрагмент конфигурации
bmk/<domain>/<component_name>/
    ├── <component_name>.py     # код невязки и ComponentSpec
    ├── <component_name>.tex    # документация (паспорт)
    ├── glyph.svg               # внешний вид (опционально)
    └── tests/
        └── test_<component_name>.py  # юнит-тесты (опционально)

13. Шаг 9. Регистрация и проверка появления в БМК

Находим компонент в БМК
Находим компонент в БМК

По нажатию кнопки «Зарегистрировать» генератор выполняет последовательность операций, описанных в архитектурном руководстве (раздел «Генератор компонентов», шаги 7–11):

  1. Сохранение исходников в папку, выбранную на предыдущем шаге. Генерируются:
  2. <name>.py — код компонента с ComponentSpec и декоратором @physical;
  3. <name>.tex — документация паспорта;
  4. glyph.svg — глиф (если был задан);
  5. test_<name>.py — заготовка юнит-теста.
  6. Вызов регистратора — компилирует код, импортирует модуль и регистрирует блок в реестрах REGISTRY, _RESIDUALS, _PHYSICAL_SPECS. Это возможно без перезапуска сервера (исполняемого приложения ORPHYS).
  7. Генерация записи catalog.json с метаданными для палитры.
  8. Сборка обновлённого клиентского каталога:
  9. Пересборка production-бандла (по необходимости):
  1. Запуск автотестов:
  2. Визуальная проверка в браузере (ручная): компонент должен появиться в папке палитры, указанной при создании, и перетаскиваться на холст соответствующего слоя.

Как убедиться, что регистрация прошла успешно

  • В дереве палитры отображается новая папка/иконка с указанным обозначением.
  • В инспекторе свойств все параметры видны с правильными единицами.
  • При запуске минимальной пробной схемы с этим компонентом (источник → компонент → сток) расчёт не падает с ошибкой размерности или сингулярности.

14. Частые сценарии настройки невязок

Здесь приведены примеры кода компонентов объектного уровня.

14.1. Простое гидравлическое сопротивление

Линейная потеря давления. Один скаляр невязки ветви.

Код (python)
SPEC = ComponentSpec(
    name="hydraulic_resistor",
    kind="Гидравлическое сопротивление",
    sym="RES",
    body="RES",
    render="block",
    layer="object",
    folder="Гидравлика",
    domain="hydraulic",
    ports=[
        Port("inlet",  dir="in",  side="left",  domain="hydraulic"),
        Port("outlet", dir="out", side="right", domain="hydraulic"),
    ],
    params=[Param("R", 1000.0, unit="Па/(кг/с)")],
)

@physical(SPEC)
def hydraulic_resistor(c, V, I, dV, dI, t, lam):
    a, b = c["nodes"]
    G = I[c["id"]]
    R = float(c["attrs"].get("R", 1000.0))
    return (V[a] - V[b]) - R * G

Поле «Невязки» генератора: P_in - P_out - R * G. Флаги: динамика «нет», нелинейность «нет».

14.2. Гидравлический трубопровод по Хагену — Пуазёйлю

Ламинарный режим:

$$ \Delta P = \frac{128 \mu L G}{\pi \rho d^4} $$

Код (python)
SPEC = ComponentSpec(
    name="laminar_pipe",
    kind="Трубопровод ламинарный",
    layer="object", folder="Гидравлика", domain="hydraulic",
    ports=[
        Port("inlet",  dir="in",  side="left"),
        Port("outlet", dir="out", side="right"),
    ],
    params=[
        Param("L", 100.0, unit="м"),
        Param("d", 0.015, unit="м"),
        Param("rho", 998.0, unit="кг/м³"),
        Param("mu", 1.0e-3, unit="Па·с"),
    ],
)

@physical(SPEC)
def laminar_pipe(c, V, I, dV, dI, t, lam):
    a, b = c["nodes"]
    attrs = c["attrs"]
    G   = float(I[c["id"]])
    L   = float(attrs.get("L", 100.0))
    d   = float(attrs.get("d", 0.015))
    rho = float(attrs.get("rho", 998.0))
    mu  = float(attrs.get("mu", 1.0e-3))
    dp = 128.0 * mu * L * G / (pi * rho * (d ** 4))
    return (V[a] - V[b]) - dp

Поле «Невязки»: P_in - P_out - 128 * mu * L * G / (pi * rho * d**4). Флаги: динамика «нет», нелинейность «нет».

14.3. Гидравлическая ёмкость (diff="nodes")

Ёмкость с накоплением среды; поток ветви выражается через производную давления.

Код (python)
SPEC_CAPACITY = ComponentSpec(
    name="hydraulic_capacity",
    kind="Гидравлическая емкость",
    layer="object", folder="Гидравлика", domain="hydraulic",
    ports=[
        Port("a", dir="in",  side="left"),
        Port("b", dir="out", side="right"),
    ],
    params=[Param("C_h", 1.0e-5, unit="кг·с²/м⁵")],
)

@physical(SPEC_CAPACITY, diff="nodes")
def hydraulic_capacity(c, V, I, dV, dI, t, lam):
    a, b = c["nodes"]
    G = I[c["id"]]
    C_h = float(c["attrs"].get("C_h", 1.0e-5))
    return G - C_h * (dV[a] - dV[b])

Поле «Невязки»: G - C_h * (dP_in_dt - dP_out_dt). Флаг: дифф. переменная «по узлам».

14.4. Гидравлическая инерционность (diff="branch")

Инерционность столба жидкости в трубе.

Код (python)
SPEC_INERTANCE = ComponentSpec(
    name="hydraulic_inertance",
    kind="Гидравлическая инерционность",
    layer="object", folder="Гидравлика", domain="hydraulic",
    ports=[
        Port("a", dir="in",  side="left"),
        Port("b", dir="out", side="right"),
    ],
    params=[Param("L_h", 100.0, unit="кг/м⁴")],
)

@physical(SPEC_INERTANCE, diff="branch")
def hydraulic_inertance(c, V, I, dV, dI, t, lam):
    a, b = c["nodes"]
    L_h = float(c["attrs"].get("L_h", 100.0))
    return (V[a] - V[b]) - L_h * dI[c["id"]]

Поле «Невязки»: P_in - P_out - L_h * dG_dt. Флаг: дифф. переменная «по ветви».

14.5. Аппарат с внутренним состоянием фильтрации

Ёмкость с диагностикой давления; добавляется дифференциальное состояние Pf и алгебраическое alarm.

Код (python)
SPEC = ComponentSpec(
    name="smart_vessel",
    kind="Ёмкость с диагностикой",
    layer="object", folder="Технологические аппараты",
    ports=[Port("a", dir="in"), Port("b", dir="out")],
    params=[
        Param("V",     2.0,    unit="м³"),
        Param("T",     300.0,  unit="К"),
        Param("Rs",    287.0,  unit="Дж/(кг·К)"),
        Param("tau",   20.0,   unit="с"),
        Param("P_set", 40000.0, unit="Па"),
    ],
)

@physical(SPEC, diff="nodes", states=[("Pf", "diff"), ("alarm", "alg")])
def smart_vessel(elem):
    a, b   = elem.port("a"), elem.port("b")
    V      = elem.param("V", 2.0)
    T      = elem.param("T", 300.0)
    Rs     = elem.param("Rs", 287.0)
    tau    = elem.param("tau", 20.0)
    P_set  = elem.param("P_set", 0.0)
    Pf     = elem.state("Pf", 0.0)
    d_Pf   = elem.d_state("Pf")
    alarm  = elem.state("alarm", 0.0)
    Ch = V / (Rs * T)
    return [
        a.flow - Ch * (a.d_potential - b.d_potential),
        d_Pf   - (a.potential - Pf) / tau,
        alarm  - (Pf - P_set),
    ]

Поле «Невязки» генератора заполняется тремя строками. Состояния объявляются на вкладке «Параметры», секция «Дифференцируемые параметры».

14.6. Нефтегазовое сопротивление (StreamProfile)

Код (python)
from mars_core.stream_profile import StreamProfile

SPEC_OG = ComponentSpec(
    name="oilgas_linear_resistance",
    kind="Технологическое сопротивление смеси",
    layer="object", folder="Нефтегазовые аппараты", domain="oilgas",
    supports_stream=True,
    stream_role="resistance",
    ports=[Port("inlet", dir="in"), Port("outlet", dir="out")],
    params=[Param("R", 1000.0, unit="Па/(кг/с)")],
)

@physical(
    SPEC_OG,
    stream=StreamProfile.flow_through("inlet", "outlet", allow_reverse=True),
    publish=lambda c, V, I, t, S: {
        "flow_kgs": float(I[c["id"]]),
        "dp_pa":    float(V[c["nodes"][0]] - V[c["nodes"][1]]),
    },
)
def oilgas_linear_resistance(c, V, I, dV, dI, t, lam):
    a, b = c["nodes"]
    return (V[a] - V[b]) - float(c["attrs"].get("R", 1000.0)) * I[c["id"]]

На вкладке «Параметры» в секции «Тепловая модель» выбирается пресет flow_through.

14.7. Многопортовый аппарат (тройник)

Идеальный гидравлический тройник с тремя независимыми потоками:

Код (python)
_PORTS = ("p1", "p2", "p3")
_DIRS  = ("in", "out", "out")
_MULTI = True

def _body(elem):
    p1, p2, p3 = elem.port("p1"), elem.port("p2"), elem.port("p3")
    return [
        p1.potential - p2.potential,
        p1.potential - p3.potential,
        p1.flow + p2.flow + p3.flow,
    ]

@physical(SPEC_TEE, ports=3)
def hydraulic_tee(c, V, I, dV, dI, t, lam):
    return _body(Elem(c, V, I, dV, dI, t, lam, _PORTS, _DIRS, _MULTI))

Поле «Невязки» генератора содержит три строки; количество уравнений = числу портов. Галочка «Независимые расходы портов» установлена.

15. Типичные ошибки и их диагностика

СимптомВероятная причинаРешение
TypeError: takes 1 positional argument but 7 were givenТело функции записано как @physical(SPEC) def body(elem): ... напрямую.Реализовать функцию _body(elem) и вызвать её через согласующий адаптер с сигнатурой (c, V, I, dV, dI, t, lam).
Компонент гидравлики ведёт себя как электрическийНе указан domain="hydraulic" в ComponentSpec. По умолчанию действует domain="electrical".Явно задать domain="hydraulic" и тот же домен в портах Port(..., domain="hydraulic").
Для двух портов возвращается вектор из 2 уравнений и расчёт падаетОшибочное отождествление количества графических контактов с параметром ports=2.Возвращать одиночный скаляр; ports=M указывать только при M независимых потоках.
Попытка записать G_in - G_out = 0 в невязку ветвиУравнение непрерывности уже заложено в MNA.Записывать только собственное конститутивное уравнение связи потенциала и потока.
Использование расхода в м³/с без учёта плотностиВ домене hydraulic через G передаётся массовый расход в кг/с.Вводить формулу преобразования $Q_v = G / \rho$.
Нелинейное сопротивление не сходитсяЗабыт флаг nonlinear=True (или «Нелинейная модель» в генераторе).Всегда устанавливать «Нелинейная модель» при наличии нелинейностей.
Мутация внутренних словарей внутри невязки вызывает сбоиПобочные эффекты нарушают параллелизм по раскраске столбцов.Функция невязки обязана быть чистой: только чтение входов и возврат скаляра/списка.
Сообщение «Нет опоры: добавьте Землю / Источник / Приёмник»В схеме отсутствует элемент с фиксированным потенциалом $V_0=0$.В электрической цепи добавить блок «Земля»; в гидравлике — блоки «Источник» (FEED) или «Приёмник» (SINK).
Решатель дробит шаг до минимума и расчёт не идётАналитическая характеристика крутая, решатель не успевает сходиться.Уменьшить шаг интегрирования h или включить автоматический метод (solve_adaptive); включить гомотопию λ.
Компонент не появляется в палитреКод не зарегистрирован ни в _PHYSICAL_SPECS, ни в REGISTRY.Запустить Python с MARS_DEBUG_LOAD=1 для подробного журнала сканирования библиотеки; убедиться, что файл лежит в models_and_tests/models_library/ или импортирован явно.
Конфликт имён с уже существующим компонентомМашинное имя не уникально.Переименовать компонент (новое имя должно быть уникальным во всём реестре).
После переименования порта старые проекты перестали открыватьсяМашинные имена интерфейса — неизменяемый контракт API.Не переименовывать name у Port и Param после публикации; при необходимости миграции — добавлять новые порты, оставляя старые для обратной совместимости.

16. Приёмочные испытания после регистрации

После того как компонент зарегистрирован, до передачи его другим пользователям имеет смысл выполнить серию проверок. Это требование совпадает с «Методикой проверки и изолированного тестирования» из архитектурного руководства.

16.1. Проверка автоматической регистрации в реестрах ядра

Код (python)
import mars_core.component_host
from mars_core.acausal import _PHYSICAL_SPECS, _RESIDUALS

assert "hydraulic_resistor" in _PHYSICAL_SPECS
assert "hydraulic_resistor" in _RESIDUALS

Если одно из утверждений падает, декоратор @physical не сработал. Самая частая причина — синтаксическая ошибка в сгенерированном .py файле или отсутствие импорта модуля.

16.2. Численное базовое тестирование невязки

Подставьте искусственные словари V и I, при которых уравнение аналитически выполняется, и проверьте близость невязки к нулю:

Код (python)
c = {"id": "p1", "nodes": [1, 2],
     "attrs": {"L": 100.0, "d": 0.015, "rho": 998.0, "mu": 1e-3}}
V = {1: 150000.0, 2: 100000.0}  # ΔP = 50 000 Па

# Аналитическое решение для ламинарного течения:
G_exact = 50000.0 * pi * 998.0 * (0.015 ** 4) / (128 * 1e-3 * 100.0)

res = laminar_pipe(c, V, {"p1": G_exact}, {}, {}, 0.0, 1.0)
assert abs(res) < 1e-9, f"Невязка должна быть ≈ 0, получено {res}"

16.3. Интеграционный тест на схеме

Соберите минимальную проверочную схему (по образцу из архитектурного руководства):

Исходный код / фрагмент конфигурации
Источник FEED → [Ваш компонент] → Приёмник SINK

Запустите расчёт в статике (DC) и убедитесь:

  • расчёт завершается без ошибок;
  • баланс расходов сходится с точностью не хуже относительной погрешности Ньютона (tol ≈ 1e-9 по умолчанию);
  • на графике метрик отображается физически адекватная траектория (без скачков, монотонная там, где ожидается).

Затем запустите расчёт в переходном режиме (Transient) и убедитесь в отсутствии осцилляций, вызванных численной неустойчивостью.

16.4. Регрессионное тестирование в pytest

Поместите тесты в tests/test_<имя_компонента>.py для запуска в CI/CD. Тесты должны покрывать:

  • успешную регистрацию (_PHYSICAL_SPECS);
  • аналитический баланс при известных значениях;
  • реакцию на переключение режимов (diff, nonlinear).

17. Управление жизненным циклом: обновление и удаление

Обновление значений параметров по умолчанию

Изменения значений по умолчанию безопасны: они влияют только на вновь размещаемые блоки. Уже размещённые блоки хранят свои значения в attrs каждого экземпляра.

Переименование машинного имени порта или параметра

Если переименование неизбежно:

  1. Добавьте новое имя как алиас, оставив старое в ComponentSpec (через «совместимое имя» — поддерживается в новых версиях SDK);
  2. Обновите все проекты через миграционный скрипт;
  3. Только после этого удаляйте старое имя.

Изменение количества портов

Добавление нового порта — относительно безопасная операция, если:

  • новый порт опционален и совместим со старой топологией;
  • существующие подключения продолжают работать без правки формулы.

Удаление существующего порта ломает все схемы, в которых он использовался. Требуется миграция.

Удаление компонента из библиотеки

  1. Удалите файлы из bmk/<component>/.
  2. Запустите среду моделирования.
  3. Убедитесь, что все активные проекты больше не содержат экземпляров компонента (иначе — ошибка валидатора при загрузке).
  4. При наличии опубликованных проектов выполните миграцию на заменяющий компонент.

18. Краткая шпаргалка

18.1. Минимальный обязательный набор полей в генераторе

Исходный код / фрагмент конфигурации
Порты       — минимум 1 порт (in / out)
Параметры   — минимум один числовой параметр (или без них)
Невязки     — формула в форме F = 0
Код         — пусто (генератор сам подставит return)
Внешний вид — пусто (стандартный прямоугольник)
Документация     — хотя бы краткое описание назначения

18.2. Эквивалент «вкладка ↔ код»

Вкладка генератораКод
Имя, вид, папкаComponentSpec(name=..., kind=..., folder=...)
Портыports=[Port(...), Port(...)]
Параметрыparams=[Param(...), Param(...)]
Тепловая модельsupports_stream=True, stream_role=..., stream=StreamProfile.flow_through(...)
Невязка (формула)тело функции @physical
Дифф. переменная (узлы / ветвь)diff="nodes" / diff="branch"
Нелинейная модельnonlinear=True
Аналитический якобианавтогенерация символьных производных
Многопортовостьports=N + возврат списка из N уравнений
Состояния`states=[("имя", "diff"

18.3. Соответствие «шаг генератора ↔ раздел архитектуры»

  • Шаги 2–3 (порты и параметры) → раздел «Описание дескриптора ComponentSpec».
  • Шаг 4 (невязки) → разделы «Прямой @physical», «Elem», register_component.
  • Шаг 4 (флаги) → «Динамические компоненты», «Нелинейные физические компоненты», «Многопортовые компоненты», «Материальные потоки».
  • Шаг 5 (код) → «Высокопроизводительный C++ SDK» (при необходимости расширения на C++).
  • Шаги 8–9 (контроль и регистрация) → «Двухуровневая природа регистрации».
  • Шаг 16 (приёмочные испытания) → «Методика проверки и изолированного тестирования» и «Сводная матрица типичных ошибок».
Раздел 11

Справочник компонентов библиотеки

Полный каталог паспортов компонентов среды моделирования ORPHYS с описанием назначения, портов, параметров, математической модели и алгоритма работы. Поиск работает по Ctrl+K (глобальный) и по полю «Поиск компонента» (локально).

Поиск компонента
Слой модели
Тип
Категория
Найдено компонентов: 0
Вращ. пара OX
kpbx
Объектный
физическийkinematic\_3dkinematic3dkinematic3d/

Вращательная кинематическая пара: поворот вокруг оси $OX$ на угол $\alpha$ (переменный). Действует на плоскость $(X, Z)$: $Y_2 = Y_1$, $X_2 = X_1$, $Z_2, X_2$ — преобразуются матрицей поворота вокруг $X$ на угол $\alpha$. Используется в кинематических цепях как «сочленение» (revolute joint), базовый элемент манипуляторов и механических рук.

SPEC.folderkinematic3d/
Папкаkinematic3d/
Типфизический
Уровеньobject
Доменkinematic\_3d
body$\circlearrowright\mathrm{X

Связи компонента (порты)

p_outoutkinematic_3dВыходное сочленение
Не применимо.

Параметры и состояние

Состояния и интегрирование

Собственных состояний нет.

Математическая модель

Невязки (Таблица 1, буквально: действует на плоскость $(X, Z)$; $Y_2 = Y_1$): \[ \begin{aligned} F_0 &= X_{\text{out}} - X_{\text{in}}, \\ F_1 &= Y_{\text{out}} - Y_{\text{in}}, \\ F_2 &= Z_{\text{out}} - (Z_{\text{in}} \cos\alpha + X_{\text{in}} \sin\alpha). \end{aligned} \] (Полная матрица поворота в 3D даёт дополнительные нелинейные члены; placeholder chain-режима возвращает [0.0, 0.0] — реальное преобразование выполняется в kinematic_chain.)

Алгоритм работы

См. zvx; отличие — фабрика make_rotational_spec("kpbx", ..., "x").
Вращ. пара OY
kpby
Объектный
физическийkinematic\_3dkinematic3dkinematic3d/

Вращательная кинематическая пара: поворот вокруг оси $OY$ на угол $\alpha$ (переменный). Действует на плоскость $(Y, Z)$: $X_2 = X_1$, $Y_2 = Y_1$, $Y_2, Z_2$ — преобразуются матрицей поворота вокруг $Y$ на угол $\alpha$. Аналог kpbx с заменой оси.

SPEC.folderkinematic3d/
Папкаkinematic3d/
Типфизический
Уровеньobject
Доменkinematic\_3d
body$\circlearrowright\mathrm{Y

Связи компонента (порты)

p_outoutkinematic_3dВыходное сочленение
Не применимо.

Параметры и состояние

Состояния и интегрирование

Собственных состояний нет.

Математическая модель

Невязки: \[ \begin{aligned} F_0 &= X_{\text{out}} - X_{\text{in}}, \\ F_1 &= Y_{\text{out}} - Y_{\text{in}}, \\ F_2 &= Z_{\text{out}} - (Z_{\text{in}} \cos\alpha - X_{\text{in}} \sin\alpha). \end{aligned} \] Placeholder chain-режима возвращает [0.0, 0.0].

Алгоритм работы

См. kpbx; отличие — фабрика make_rotational_spec("kpby", ..., "y").
Вращ. пара OZ
kpbz
Объектный
физическийkinematic\_3dkinematic3dkinematic3d/

Вращательная кинематическая пара: поворот вокруг оси $OZ$ на угол $\alpha$ (переменный). Действует на плоскость $(X, Y)$: $Z_2 = Z_1$, $X_2, Y_2$ — преобразуются матрицей поворота вокруг $Z$ на угол $\alpha$. Аналог kpbx с заменой оси.

SPEC.folderkinematic3d/
Папкаkinematic3d/
Типфизический
Уровеньobject
Доменkinematic\_3d
body$\circlearrowright\mathrm{Z

Связи компонента (порты)

p_outoutkinematic_3dВыходное сочленение
Не применимо.

Параметры и состояние

Состояния и интегрирование

Собственных состояний нет.

Математическая модель

Невязки: \[ \begin{aligned} F_0 &= X_{\text{out}} - (X_{\text{in}} \cos\alpha - Y_{\text{in}} \sin\alpha), \\ F_1 &= Y_{\text{out}} - (X_{\text{in}} \sin\alpha + Y_{\text{in}} \cos\alpha), \\ F_2 &= Z_{\text{out}} - Z_{\text{in}}. \end{aligned} \] Placeholder chain-режима возвращает [0.0, 0.0].

Алгоритм работы

См. kpbx; отличие — фабрика make_rotational_spec("kpbz", ..., "z").
Звено OX
zvx
Объектный
физическийkinematic\_3dkinematic3dkinematic3d/

Звено постоянной длины $L$, направленное вдоль оси $OX$ — ограничение: $X_1 = X_2 + L$, $Y_1 = Y_2$, $Z_1 = Z_2$. Часть кинематической 3D-библиотеки (домен kinematic_3d, топология chain). Используется для сборки трёхмерных кинематических цепей (рука робота, манипулятор, платформа Стюарта) из типовых элементов. Реальное вычисление позиций — в mars_core.kinematic_chain.evaluate_ kinematic_chain, чистая прямая кинематика; этот компонент регистрирует только каталожный дескриптор (ComponentSpec) и placeholder-невязки (топология chain исключает их из обычного Newton-цикла).

SPEC.folderkinematic3d/
Папкаkinematic3d/
Типфизический
Уровеньobject
Доменkinematic\_3d
body$\mathrm{L\,(X)

Связи компонента (порты)

p_outoutkinematic_3dВыходное сочленение
Не применимо.

Параметры и состояние

Состояния и интегрирование

Собственных состояний нет.

Математическая модель

Невязки (в MultibodySolver): \[ \begin{aligned} F_0 &= X_{\text{out}} - (X_{\text{in}} + L), \\ F_1 &= Y_{\text{out}} - Y_{\text{in}}, \\ F_2 &= Z_{\text{out}} - Z_{\text{in}}. \end{aligned} \] В режиме placeholder (chain-топология) возвращается [0.0, 0.0, 0.0] — реальная кинематика считается вне этого блока.

Алгоритм работы

  1. Спецификация собирается фабрикой make_link_spec("zvx", ..., "x").
  2. Регистрация placeholder через register_chain_component(SPEC) — всегда возвращает [0.0, 0.0].
Звено OY
zvy
Объектный
физическийkinematic\_3dkinematic3dkinematic3d/

Звено постоянной длины $L$, направленное вдоль оси $OY$ — ограничение: $Y_1 = Y_2 + L$, $X_1 = X_2$, $Z_1 = Z_2$. Полный аналог zvx с заменой оси. Используется в тех же сценариях (рука робота, манипулятор), но с направлением звена по Y.

SPEC.folderkinematic3d/
Папкаkinematic3d/
Типфизический
Уровеньobject
Доменkinematic\_3d
body$\mathrm{L\,(Y)

Связи компонента (порты)

p_outoutkinematic_3dВыходное сочленение
Не применимо.

Параметры и состояние

Состояния и интегрирование

Собственных состояний нет.

Математическая модель

Невязки: \[ \begin{aligned} F_0 &= X_{\text{out}} - X_{\text{in}}, \\ F_1 &= Y_{\text{out}} - (Y_{\text{in}} + L), \\ F_2 &= Z_{\text{out}} - Z_{\text{in}}. \end{aligned} \] Placeholder chain-режима возвращает [0.0, 0.0].

Алгоритм работы

См. zvx; отличие — в фабрике make_link_spec("zvy", ..., "y") и в соответствующих невязках.
Звено OZ
zvz
Объектный
физическийkinematic\_3dkinematic3dkinematic3d/

Звено постоянной длины $L$, направленное вдоль оси $OZ$ — ограничение: $Z_1 = Z_2 + L$, $X_1 = X_2$, $Y_1 = Y_2$. Полный аналог zvx с заменой оси на OZ.

SPEC.folderkinematic3d/
Папкаkinematic3d/
Типфизический
Уровеньobject
Доменkinematic\_3d
body$\mathrm{L\,(Z)

Связи компонента (порты)

p_outoutkinematic_3dВыходное сочленение
Не применимо.

Параметры и состояние

Состояния и интегрирование

Собственных состояний нет.

Математическая модель

Невязки: \[ \begin{aligned} F_0 &= X_{\text{out}} - X_{\text{in}}, \\ F_1 &= Y_{\text{out}} - Y_{\text{in}}, \\ F_2 &= Z_{\text{out}} - (Z_{\text{in}} + L). \end{aligned} \] Placeholder chain-режима возвращает [0.0, 0.0].

Алгоритм работы

См. zvx; отличие — в фабрике make_link_spec("zvz", ..., "z").
Пост. пара OX
kpcx
Объектный
физическийkinematic\_3dkinematic3dkinematic3d/

Поступательная кинематическая пара: сдвиг вдоль оси $OX$ с переменной величиной $L$. Ограничения: $Y_1 = Y_2$, $Z_1 = Z_2$; $X_1 = X_2 + L$ ($L$ — переменная, может меняться во времени). Используется в кинематических цепях как «слайдер» (линейный привод без вращения), обычно в паре со звеньями zv* и вращательными парами kpb*.

SPEC.folderkinematic3d/
Папкаkinematic3d/
Типфизический
Уровеньobject
Доменkinematic\_3d
body$\leftrightarrow\mathrm{X

Связи компонента (порты)

p_outoutkinematic_3dВыходное сочленение
Не применимо.

Параметры и состояние

Состояния и интегрирование

Собственных состояний нет.

Математическая модель

Невязки: \[ \begin{aligned} F_0 &= X_{\text{out}} - (X_{\text{in}} + L), \\ F_1 &= Y_{\text{out}} - Y_{\text{in}}, \\ F_2 &= Z_{\text{out}} - Z_{\text{in}}. \end{aligned} \] Placeholder chain-режима возвращает [0.0, 0.0].

Алгоритм работы

См. zvx; отличие — фабрика make_translational_spec("kpcx", ..., "x").
Пост. пара OY
kpcy
Объектный
физическийkinematic\_3dkinematic3dkinematic3d/

Поступательная кинематическая пара: сдвиг вдоль оси $OY$ с переменной величиной $L$. Аналог kpcx с заменой оси.

SPEC.folderkinematic3d/
Папкаkinematic3d/
Типфизический
Уровеньobject
Доменkinematic\_3d
body$\leftrightarrow\mathrm{Y

Связи компонента (порты)

p_outoutkinematic_3dВыходное сочленение
Не применимо.

Параметры и состояние

Состояния и интегрирование

Собственных состояний нет.

Математическая модель

Невязки: \[ \begin{aligned} F_0 &= X_{\text{out}} - X_{\text{in}}, \\ F_1 &= Y_{\text{out}} - (Y_{\text{in}} + L), \\ F_2 &= Z_{\text{out}} - Z_{\text{in}}. \end{aligned} \] Placeholder chain-режима возвращает [0.0, 0.0].

Алгоритм работы

См. kpcx; отличие — в фабрике make_translational_spec("kpcy", ..., "y").
Пост. пара OZ
kpcz
Объектный
физическийkinematic\_3dkinematic3dkinematic3d/

Поступательная кинематическая пара: сдвиг вдоль оси $OZ$ с переменной величиной $L$. Аналог kpcx с заменой оси на OZ.

SPEC.folderkinematic3d/
Папкаkinematic3d/
Типфизический
Уровеньobject
Доменkinematic\_3d
body$\leftrightarrow\mathrm{Z

Связи компонента (порты)

p_outoutkinematic_3dВыходное сочленение
Не применимо.

Параметры и состояние

Состояния и интегрирование

Собственных состояний нет.

Математическая модель

Невязки: \[ \begin{aligned} F_0 &= X_{\text{out}} - X_{\text{in}}, \\ F_1 &= Y_{\text{out}} - Y_{\text{in}}, \\ F_2 &= Z_{\text{out}} - (Z_{\text{in}} + L). \end{aligned} \] Placeholder chain-режима возвращает [0.0, 0.0].

Алгоритм работы

См. kpcx; отличие — фабрика make_translational_spec("kpcz", ..., "z").
Демпфер
damper
Объектный
физическийmechanicalМеханика 2Dmechanical/

Физическая (acausal) модель линейного вязкого демпфера: $F = c v$, где $F$ — сила сопротивления, $v$ — скорость. Используется в системах с потерями (масса-пружина-демпфер — классическая колебательная система с затуханием) и как элемент трения между двумя телами.

SPEC.folderМеханика 2D
Папкаmechanical/
Типфизический
Уровеньobject
Доменmechanical
bodyc

Связи компонента (порты)

Не применимо.

Параметры и состояние

Состояния и интегрирование

Собственных states=[...] нет.

Математическая модель

Невязка ветви: \[ F = I[\texttt{id}] - c \cdot V[p] \] (при $F = 0$: $F = c v$ — линейное вязкое трение).

Алгоритм работы

  1. p, _ = c["nodes"]; c_damp из attrs.
  2. F = I[c["id"]], v = V[p].
  3. Вернуть $F - c_{\text{damp}} \cdot v$.
Масса
mass
Объектный
физическийmechanicalМеханика 2Dmechanical/

Физическая (acausal) модель инерционной массы — сосредоточенного тела, к которому приложена внешняя сила. Реализует 2-й закон Ньютона в форме $m \cdot dv/dt = F$ для узла $p$ (скорость) и тока компонента (сила). Используется как базовый элемент сосредоточенных механических моделей (масса на пружине, масса-демпфер), а также в системах с одной степенью свободы (модель маятника без жёсткости).

SPEC.folderМеханика 2D
Папкаmechanical/
Типфизический
Уровеньobject
Доменmechanical
bodym

Связи компонента (порты)

Не применимо.

Параметры и состояние

Состояния и интегрирование

Собственных states=[...] нет; узловые дифф. переменные ($V[p]$) дифференцируются ядром ($dV[p]$ — ускорение).

Математическая модель

Невязка ветви: \[ F = m \cdot \dot V[p] - I[\texttt{id}] \] (при $F = 0$: $m \cdot dv/dt = F$ — 2-й закон Ньютона).

Алгоритм работы

  1. p, _ = c["nodes"]; m из attrs.
  2. F = I[c["id"]] (сила, действующая на массу).
  3. Вернуть невязку $m \cdot dV[p] - F$.
Ползун
slider
Объектный
физическийmechanical\_2dМеханика 2Dmultibody/

Физическая связь «ползун»: фиксирует одну координату тела ($\mathrm{axis} \in \{x, y, \theta\}$) на заданном значении $\mathrm{value}$. Невязка — линейная: $\mathrm{coord} - \mathrm{value} = 0$. Используется для «заземления» тел (фиксация одной координаты), для направляющих (ползун движется только по $x$ или только по $y$), для ограничения ориентации ($\theta = \mathrm{const}$). Демонстрация паттерна «двойная» функция (см. hinge, rod).

SPEC.folderМеханика 2D
Папкаmultibody/
Типфизический
Уровеньobject
Доменmechanical\_2d
bodyaxis,\;value

Связи компонента (порты)

Не применимо.

Параметры и состояние

Входные параметры

value$0.0$м/радnumberЦелевое значение координаты
body«»—textИмя тела

Состояния и интегрирование

Собственных состояний нет.

Математическая модель

Невязка: \[ F = \mathrm{coord}_{\text{axis}}(\mathrm{body}) - \mathrm{value} \] где $\mathrm{coord}_{\text{axis}}$ — соответствующая координата из S["<axis>_<body>"].

Алгоритм работы

  1. Считать axis, value, body из c["attrs"].
  2. key = f"\{axis\}_\{body\}"; coord = S[key].
  3. return [coord - value].
  1. return [0.0] (MultibodySolver делает реальное вычисление).
Пружина
spring
Объектный
физическийmechanicalМеханика 2Dmechanical/

Физическая (acausal) модель линейной пружины (закон Гука) с внутренним состоянием — координатой $x$. Реализует две связанные невязки: $F = k x$ (сила упругости) и $\dot x = v$ (связь координаты и скорости). Используется в сосредоточенных механических системах (масса-пружина-демпфер, пружинный маятник) и как элемент «упругого звена» между двумя телами.

SPEC.folderМеханика 2D
Папкаmechanical/
Типфизический
Уровеньobject
Доменmechanical
bodyk

Связи компонента (порты)

Не применимо.

Параметры и состояние

Состояния и интегрирование

Внутренние состояния:
  • x (diff) — координата пружины (удлинение/сжатие), м.
Также дифференцируются узловые переменные ($V[p]$).

Математическая модель

Возвращаемый вектор невязок: \[ \begin{aligned} F_0 &= I[\texttt{id}] - k \cdot x, \\ F_1 &= \dot x - V[p]. \end{aligned} \] (при $F_0 = F_1 = 0$: $F = k x$, $\dot x = v$).

Алгоритм работы

  1. p, _ = c["nodes"]; k из attrs.
  2. F = I[c["id"]], v = V[p].
  3. Вернуть список $[F - k \cdot S[\texttt{"x"}],\; dS[\texttt{"x"}] - v]$.
Стержень
rod
Объектный
физическийmechanical\_2dМеханика 2Dmultibody/

Физическая связь «жёсткий стержень» между двумя 2D-телами: расстояние между ними фиксировано и равно $L$. Невязка — нелинейная: $\sqrt{(x_B - x_A)^2 + (y_B - y_A)^2} - L = 0$. Используется в рычажных механизмах, маятниках с жёсткой связью, в «сочленённых» механизмах. Демонстрация паттерна «двойная» функция (rod_residual(S, c) + rod_residual_physical), как у hinge.

SPEC.folderМеханика 2D
Папкаmultibody/
Типфизический
Уровеньobject
Доменmechanical\_2d
bodyL

Связи компонента (порты)

Не применимо.

Параметры и состояние

Входные параметры

body_a«»—textИмя тела A
body_b«»—textИмя тела B

Состояния и интегрирование

Собственных состояний нет.

Математическая модель

Невязка (длина, нелинейная): \[ F = \sqrt{(x_B - x_A)^2 + (y_B - y_A)^2} - L \] В MultibodySolver это даёт алгебраическую переменную $R$ — силу реакции стержня (вдоль оси $A B$).

Алгоритм работы

  1. Считать $L$, body_a, body_b из c["attrs"].
  2. Считать позиции из S["x_<body>"], S["y_<body>"].
  3. return [sqrt(dx² + dy²) - L].
  1. L из c["attrs"]; return [0.0] (MultibodySolver делает реальное вычисление).
Тело 2D
body_2d
Объектный
физическийmechanical\_2dМеханика 2Dmultibody/

Физическое 2D-тело (точечная масса с моментом инерции) в плоскости: 3 степени свободы ($x, y, \theta$), 3 скорости ($v_x, v_y, \omega$), 3 ускорения. Реализует уравнения Даламбера в форме: $m \dot v_x = F_x$, $m \dot v_y = F_y$, $I \dot \omega = M$. Используется как базовое тело в 2D-механизмах (манипуляторы, рычажные системы, многозвенники) с MultibodySolver.

SPEC.folderМеханика 2D
Папкаmultibody/
Типфизический
Уровеньobject
Доменmechanical\_2d
bodym,\,I

Связи компонента (порты)

Не применимо.

Параметры и состояние

Входные параметры

I$0.1$кг$\cdot$м$^2$numberМомент инерции

Состояния и интегрирование

Внутренние состояния (io.state):
  • x, y, theta — координаты (м, м, рад);
  • vx, vy, omega — скорости (м/с, м/с, рад/с).

Математическая модель

Невязки: \[ \begin{aligned} F_x &= \dot v_x - \frac{F_x^{\text{ext}}}{m}, \\ F_y &= \dot v_y - \frac{F_y^{\text{ext}}}{m}, \\ F_\theta &= \dot \omega - \frac{M^{\text{ext}}}{I}. \end{aligned} \] Здесь $F_x^{\text{ext}}, F_y^{\text{ext}}, M^{\text{ext}}$ — внешние силы/момент, передаваемые через множители Лагранжа (lam) или из глобального решателя.

Алгоритм работы

  1. m, I_mom из c.
  2. Fx, Fy, M из lam (или 0, если нет).
  3. dvx = dS.get("vx", 0.0), dvy, domega — производные скоростей.
  4. Вернуть список $[d\dot v_x - F_x/m,\; \dot v_y - F_y/m,\; \dot \omega - M/I]$.
Шарнир
hinge
Объектный
физическийmechanical\_2dМеханика 2Dmultibody/

Физическая связь между двумя 2D-телами в одной точке: позиции (со смещениями offset_a и offset_b) и ориентации тел совпадают. Три линейных невязки: \[ \begin{aligned} x_B + \mathrm{off}_{B,x} - (x_A + \mathrm{off}_{A,x}) &= 0, \\ y_B + \mathrm{off}_{B,y} - (y_A + \mathrm{off}_{A,y}) &= 0, \\ \theta_B - \theta_A &= 0. \end{aligned} \] Используется как универсальное соединение двух тел (эквивалент ball-joint в 2D, без вращательной свободы). Демонстрация паттерна «двойная» функция: чистый hinge_residual(S, c) для прямого тестирования и hinge_residual_physical как placeholder для интеграции в MultibodySolver.

SPEC.folderМеханика 2D
Папкаmultibody/
Типфизический
Уровеньobject
Доменmechanical\_2d

Связи компонента (порты)

Не применимо.

Параметры и состояние

Входные параметры

body_b«»—textИмя тела B
offset_a$(0, 0)$мtupleСмещение точки шарнира на теле A
offset_b$(0, 0)$мtupleСмещение точки шарнира на теле B

Состояния и интегрирование

Собственных состояний нет — это связь-constraint, не динамический элемент.

Математическая модель

Невязки: \[ \begin{aligned} F_0 &= (x_B + \mathrm{off}_{B,x}) - (x_A + \mathrm{off}_{A,x}), \\ F_1 &= (y_B + \mathrm{off}_{B,y}) - (y_A + \mathrm{off}_{A,y}), \\ F_2 &= \theta_B - \theta_A. \end{aligned} \]

Алгоритм работы

  1. Считать body_a, body_b, offset_a, offset_b из c["attrs"].
  2. Считать позиции тел из S["x_<body>"], S["y_<body>"], S["theta_<body>"].
  3. Вернуть список из 3 невязок.
  1. Вернуть [0.0, 0.0, 0.0] (MultibodySolver обрабатывает реальное вычисление).
Тестовый насос
pump_test
Объектный
физический, сгенерированныйoilgasМои компоненты

Тестовый компонент на примере модели насоса со стабильным дифференциальным давлением

SPEC.folderМои компоненты
Типфизический, сгенерированный
Уровеньobject
Доменoilgas
symТест

Связи компонента (порты)

outletoutoilgas
Не применимо.

Параметры и состояние

Состояния и интегрирование

Внутренние состояния не зарегистрированы (states=[]).
Drum
drum
Объектный
физическийoilgasНефтегазовые аппаратыoilgas/drum/

Трёхпортовый нефтегазовый барабан-ёмкость с полной термодинамикой (жидкость+газ, многокомпонентная смесь), построенный на mars_core.thermoprop (CoolProp или другой backend). Поддерживает геометрию (плоскодонный цилиндр), штуцеры (inlet, drain, vent) с учётом уровня жидкости, инициализацию по $P_{\text{init}}, T_{\text{init}}$, доле заполнения и составу; публикует результат флэша в attrs["_pub"] для downstream-клапанов и UI. Внутри компонент выделен в класс Drum с фазами (DrumPhases) и кэшем флэша (LRU по (P, T, trial)). Используется как типовая ёмкость для нефтегазовых цепей (сепараторы, барабаны-аккумуляторы, сборники).

SPEC.folderНефтегазовые аппараты
Папкаoilgas/drum/
Типфизический
Уровеньobject
Доменoilgas
symD
bodyD

Связи компонента (порты)

drainoutbottomШтуцер дренажа (низ ёмкости)
ventouttopШтуцер сдувки (верх ёмкости)
Across — давление (Па), through — массовый поток (кг/с). KCL на каждом штуцере связывает ток компонента с расходом клапана, стоящего на этом штуцере. Не применимо.

Параметры и состояние

Входные параметры

H$1.0$мnumberВысота корпуса
nozzle_radius$0.1$мnumberРадиус штуцеров
drain_elevation$0.0$мnumberОтметка дренажного штуцера
vent_elevation$1.0$мnumberОтметка штуцера сдувки
fill$0.10$доляnumberНачальный налив (доля объёма)
P_init$101325$ПаnumberНачальное давление
T_init$293.15$КnumberНачальная температура
components«Water:1.0»—compositionСостав жидкой фазы
gas_components«Nitrogen:1.0»—compositionСостав газовой фазы
backend«auto»—stringthermoprop backend
eos«HEOS»—stringМодель уравнения состояния
heater_power_W$0.0$ВтnumberМощность нагревателя
heater_off_t$0.0$сnumberМомент отключения нагревателя

Состояния и интегрирование

Внутренние состояния:
  • N_liq (diff) — отклонение количества жидкости от init (моль).
  • N_gas (diff) — отклонение количества газа от init (моль).
  • U (diff) — отклонение внутренней энергии от init (Дж).
  • T (alg) — алгебраическая температура (К, определяется из флэша).
Узловые дифф. переменные (diff="nodes"): давление в штуцере inlet. Метод интегрирования — неявный Эйлер / Ньютон, выполняемый ядром.

Математическая модель

Возвращаемый вектор невязок (в порядке индексов): \[ \begin{aligned} F_0 &= \mathrm{pressure\_closure}(P_{\text{abs}}, S), \\ F_1 &= \frac{V[\texttt{drain}] - (P_{\text{abs}} + \rho g h_{\text{drain}})} {\max(|P_{\text{abs}}| V_{\text{tank}}, 1)}, \\ F_2 &= \frac{V[\texttt{vent}] - (P_{\text{abs}} + \rho g h_{\text{vent}})} {\max(|P_{\text{abs}}| V_{\text{tank}}, 1)}, \\ F_3 &= \dot N_{\text{liq}} - \mathrm{liq\_net}, \\ F_4 &= \dot N_{\text{gas}} - \mathrm{gas\_net}, \\ F_5 &= \dot U - (\mathrm{energy\_net} + Q_{\text{heater}}), \\ F_6 &= \frac{S[\texttt{"U"}] - U_{\text{calc}}} {\max(|U_{\text{calc}}|, 1)}. \end{aligned} \] $h_{\text{drain}} = \max(\mathrm{level} - \mathrm{drain\_elevation}, 0)$, аналогично для vent; $\mathrm{liq\_net}/\mathrm{gas\_net}$ складываются из вкладов штуцеров с учётом фазы и состава; $Q_{\text{heater}} = \mathrm{heater\_power\_W}$, пока $t < \mathrm{heater\_off\_t}$. Внимание: для Ньютона $F_0$ нормализуется $\frac{V_{\text{gas}} - m_{\text{gas}}/\rho_{\text{gas}}} {V_{\text{tank}}}$, что эквивалентно газовому закону с поправкой на уровень; при превышении объёма жидкости $V_{\text{liq}}^{\text{ideal}} > V_{\text{tank}}$ используется линейный отклик давления $P_{\text{expected}} = 101325 + K_{\text{bulk}} (V_{\text{liq}}^{\text{ideal}} - V_{\text{tank}})/V_{\text{tank}}$ с $K_{\text{bulk}} = 5 \cdot 10^7$.

Алгоритм работы

  1. n_in, n_drain, n_vent = c["nodes"][:3], attrs = c["attrs"].
  2. ctx = attrs["_ctx"] (lazy init при первом вызове: создаёт FlatBottomCylinder, NozzleFitting-и для drain/vent, MediumWrapper, инициализирует $N^{\text{init}}, U^{\text{init}}$).
  3. P_abs = V[n_in].
  4. T = S["T"] (из алгебраического состояния, warm-start: $T_{\text{init}}$).
  5. state = self.state_from_solver(P_abs, S, trial=True): флэш по $(P_{\text{abs}}, T)$ через ctx._make_medium, phase_state (с LRU-кэшем); state содержит уровень, $V_{\text{liq}}^{\text{ideal}}$, фазы штуцеров, гидростатический напор.
  6. _publication_from_state(state) → attrs["_pub"] для downstream и UI.
  7. attrs["__ctx"].component_state[c["id"]] = dict(pub) (если контекст привязан).
  8. residuals собирает 7 невязок по формулам выше.
Аппарат воздушного охлаждения
air_cooler
Объектный
физическийoilgasНефтегазовые аппаратыoilgas/air_cooler/

Аппарат воздушного охлаждения (АВО) — двухполюсник acausal-цепи: гидравлика ($\Delta P = R_h \cdot \dot m$) + термобаланс с воздухом (NTU/LMTD, рециркуляция воздуха через жалюзи). Термосвойства продукта ($\rho$, $h$, $c_p$, $\mu$, $\lambda$) берутся ИЗ ПОРТА product_in.medium (НЕ из атрибутов — без silent fallbacks). Воздушная сторона — граничные скаляры ($T_{\text{air}}$, $G_{\text{air}}$, $P_{\text{air}}$, влажность), как и c_motor у насоса: не acausal-порт, а параметры. Использует новый стрим-стек (MaterialStream, MixPort). Внутри компонент выделен в класс AirCooler с геометрией (geo: трубный пучок + оребрение), гидравликой и внутренним fsolve-балансом для двух уравнений (теплосъём, расход воздуха). Примечание: для промышленной модели RericTubeBundle (C++ GPA) используется отдельный тип avo_reric; данный компонент не использует её.

SPEC.folderНефтегазовые аппараты
Папкаoilgas/air_cooler/
Типфизический
Уровеньobject
Доменoilgas
symАВО
bodyАВО

Связи компонента (порты)

product_outoutrightВыход охлаждённого продукта
Across — давление (Па), through — массовый поток (кг/с). $I > 0$ — поток в естественном направлении. Не применимо. Воздушная сторона — граничные скаляры (T_air_in, G_air_in_h, P_air, air_humidity, natural_convection), а не порты.

Параметры и состояние

Входные параметры

T_air_in$299.15$КnumberТемпература воздуха на входе
G_air_in_h$144224$кг/чnumberНоминальный расход воздуха
P_air$101325$ПаnumberАтмосферное давление
air_humidity$0.8$—numberВлажность (доля, 0..1)
natural_convection$0.03$—numberКоэф. естественной конвекции
n_bays$2.0$—numberКоличество аппаратов
n_bundles$2.0$—numberПучков на аппарат
n_fans$2.0$—numberВентиляторов на аппарат
n_tubes$344$—numberТруб в одном пучке
L$9.0$мnumberДлина трубы
d_in$0.02118$мnumberВнутренний диаметр трубы
d_out$0.0254$мnumberНаружный диаметр трубы
n_strokes$4$—intЧисло ходов труб
n_rows$8$—numberКоличество рядов
fin_d$0.05715$мnumberДиаметр ребра
fin_thickness$0.0004$мnumberТолщина ребра
fins_per_m$393$1/мnumberЧисло рёбер на метр
fouling_factor$0.0007$м²·К/ВтnumberСопротивление загрязнений
temp_head_correction$1.0$—numberКоррекция температурного напора
thermal_efficiency$1.0$—numberКоэф. тепловой эффективности
wall_conductivity$500$Вт/(м·К)numberТеплопроводность стенки
air_nu_c$0.15$—numberКоэф. геометрии пучка C
air_re_exp$0.5$—numberПоказатель Рейнольдса m
air_area$45.89$м²numberПлощадь сечения воздуха
narrow_coef$0.453$—numberКоэф. загромождения
product_viscosity_fallback$1.2\!\cdot\!10^{-4}$Па·сnumberВязкость (резерв)
product_conductivity_fallback$0.02$Вт/(м·К)numberТеплопроводность (резерв)
fan_power$1.0$—numberКоэф. мощности вентиляторов
fan_nominal_rpm$1500$об/минnumberНоминал оборотов
fan_time_to_nominal$5.0$сnumberВремя выхода на номинал
fan_initial_rpm$1500$об/минnumberНачальные обороты
fan_cmdNone—numberУправляющий вход (мост L $\to$ C)

Состояния и интегрирование

Внутренние состояния:
  • h (alg) — алгебраическое состояние энтальпии продукта на выходе (h_out_target = h_in - Q/$\dot{m}$).

Математическая модель

Невязки (2 значения): \[ \begin{aligned} F_0 &= (V[\texttt{product\_out}] - V[\texttt{product\_in}]) + R_h \cdot \dot m, \\ F_1 &= S[\texttt{"h"}] - h_{\text{out\_target}}, \qquad h_{\text{out\_target}} = h_{\text{in}} - \frac{Q}{\dot m}. \end{aligned} \] Здесь $Q$ — теплосъём, который определяется внутренним fsolve по двум уравнениям термобаланса:
  1. $Q_{\text{prod}} = \dot m \cdot c_p \cdot (T_{\text{prod\_in}} - T_{\text{prod\_out}})$ — энергия, отданная продуктом;
  2. $Q_{\text{air}} = G_{\text{air\_mix}} \cdot c_{p,\text{air}} \cdot (T_{\text{air\_out}} - T_{\text{air\_mix}})$ — энергия, полученная воздухом;
  3. $Q_{\text{prod}} = Q_{\text{air}} = U A \cdot \mathrm{LMTD}(T_{\text{prod\_in}}, T_{\text{prod\_out}}, T_{\text{air\_mix}}, T_{\text{air\_out}}) \cdot F_t$ — равенство тепловых потоков через стенку.
$U$ — коэффициент теплопередачи; $A$ — общая площадь теплопередачи; $F_t$ — поправка на температурный напор; LMTD — средне-логарифмический температурный напор. Корреляции: внутритрубная Nu $= 0.021 \cdot \mathrm{Re}^{0.8} \cdot \mathrm{Pr}^{0.43}$ (legacy MTB), наружная — Briggs–Young для пучка с оребрением. $G_{\text{air\_mix}} = G_{\text{forced}} + G_{\text{natural}} + G_{\text{recirc}}$ (рециркуляция через жалюзи; Phase 4 упрощение: 90° открыты, $G_{\text{recirc}} = 0$). Свойства воздуха ($c_{p,\text{air}}, \mu_{\text{air}}, \lambda_{\text{air}}$) — из таблицы AIR_PROPS с поправкой на влажность. Свойства продукта ($c_p$, $\mu$, $\lambda$) — из product_in.medium через snapshot; если их там нет — фолбэк product_viscosity_fallback / product_conductivity_fallback. Скорость вентилятора (разгон): \[ k_{\text{fan}}(t) = k_{\text{cmd}} + (k_{\text{initial}} - k_{\text{cmd}}) \cdot \exp(-6.75\, t / \mathrm{fan\_time\_to\_nominal}), \] где $k_{\text{cmd}} = \mathrm{fan\_cmd}$, если задан; иначе — $\mathrm{fan\_power}$. $k_{\text{initial}} = \mathrm{fan\_initial\_rpm} / \mathrm{fan\_nominal\_rpm}$.

Алгоритм работы

  1. a, b = c["nodes"]; attrs = c["attrs"]; m_dot = I[c["id"]].
  2. Получить/создать AirCooler(attrs) через _get_avo(attrs) (кэш в attrs["__avo"]).
  3. product_in = self._port(c, "product_in").
  4. Из residual_ctx.snapshot(...) получить $\rho, h_{\text{in}}, c_p, \mu, \lambda$ (или через product_in.medium с фолбэком product_viscosity_fallback / product_conductivity_fallback).
  5. Если $c_p$ или $\rho$ не положительны — ValueError("inlet.medium не привязан; используйте bind_boundary_streams").
  6. branch = (V[b] - V[a]) + Rh * m_dot.
  7. _thermal_solve(m_dot, T_prod_in, P_prod, h_in, c_p, rho, mu, k, t) — внутренний fsolve (2 неизвестных: $T_{\text{prod\_out}}$, $T_{\text{air\_out}}$).
  8. energy_res = S["h"] - (h_in - Q / m_dot).
  9. Опубликовать attrs["_pub"].
  10. Вернуть [branch, energy_res].
Источник (граничное условие)
boundary_feed
Объектный
физическийoilgasНефтегазовые аппаратыoilgas/boundary_feed/

Boundary Feed — граничный источник сети: фиксирует одно из двух граничных условий на outlet-узле. В режиме pressure задаёт давление $P_{\text{outlet}} = P_{\text{set}}$ (Па), и сеть сама подбирает расход через источник из баланса на узле. В режиме flow задаёт массовый расход $\dot m = G_{\text{set}}$ (кг/с; положительный — подача В сеть). Термодинамика (T, состав) фиксирована атрибутами и публикуется в attrs["__port_outlet"].stream через MaterialStream, чтобы downstream-компоненты (насос, АВО, drum) читали согласованное состояние. Является first-class DAE-компонентом, заменившим legacy feed/mixturesource из mars_core.acausal.

SPEC.folderНефтегазовые аппараты
Папкаoilgas/boundary_feed/
Типфизический
Уровеньobject
Доменoilgas
symFEED
bodyFEED

Связи компонента (порты)

Across — давление (Па), through — массовый поток (кг/с). По KCL: $I > 0$ означает подачу ИЗ земли В outlet-узел (т. е. В сеть). Не применимо.

Параметры и состояние

Входные параметры

P_Pa$10^5$ПаnumberУставка давления (для mode=pressure)
m_dot_kgs$1.0$кг/сnumberУставка расхода (для mode=flow)
T_K$293.15$КnumberТемпература потока
components«Water:1.0»—compositionСостав потока

Состояния и интегрирование

Собственных дифф. состояний нет — feed статeless. Все параметры читаются из attrs на каждой итерации (это устраняет баг устаревшего состояния при live-патче).

Математическая модель

Невязка (классический двухполюсник, 1 значение): \[ F = \begin{cases} V[\texttt{outlet}] - \lambda \cdot P_{\text{set}}, & \texttt{mode = pressure}, \\ I - \lambda \cdot G_{\text{set}}, & \texttt{mode = flow}. \end{cases} \] Множитель $\lambda$ — параметр гомотопии решателя ($\lambda = 1$ на решении, $0 \to 1$ в процессе итераций для улучшения сходимости при сильно нелинейных цепях). В коде residual виден как lam. Термодинамика outlet-порта — из attrs["__port _outlet"].stream (MaterialStream с medium, заполненным при биндинге стримов через bind_boundary_streams; см. _bind_boundary_feed_streams ниже).

Алгоритм работы

  1. out_node = c["nodes"][1]; I_branch = I[c["id"]]; attrs = c["attrs"].
  2. Создать/взять из attrs инстанс BoundaryFeed(attrs): режим, $P_{\text{set}}$, $G_{\text{set}}$, $T_K$.
  3. Считать V[out_node] и I_branch.
  4. Сформировать невязку по формуле выше.
  5. Опубликовать attrs["_pub"] для UI.
  6. Вернуть невязку.
  • BoundaryFeed(attrs) — класс-обёртка с полями mode, P_set, G_set, T_K и методом residuals(c, V, I, dV, dI, t, lam).
  • BoundaryFeed._port(c) — ленивая фабрика MixPort (тот же паттерн, что в centrifugal_pump).
  • _publish_boundary_feed(c, V, I, t, S) — функция публикации, передаваемая в @physical(... publish=...).
  • _bind_boundary_feed_streams(circuit) — серверный биндер (@register_circuit_binder): вызывает bind_boundary_streams(circuit) для привязки MaterialStream к портам boundary_feed до первого residual.
  • mars_core.acausal (physical, register_circuit_binder);
  • mars_core.component_spec (ComponentSpec, Port, Param, AutomationSpec, AutomationCommand, AutomationTelemetry);
  • mars_core.stream_profile (StreamProfile.source(port="outlet"));
  • mars_core.streams (MaterialStream, MixPort, bind_boundary_streams).
  • oilgas/boundary_sink — парный сток.
  • valves/mixing_valve, oilgas/centrifugal_pump, oilgas/air_cooler, oilgas/drum — типовые downstream-потребители.
  • Соглашение о знаках: $I > 0$ означает подачу В outlet-узел (поток из земли в сеть). Это естественно для feed.
  • В режиме flow feed задаёт расход жёстко; сеть подстраивает давление на outlet-узле из баланса.
  • В режиме pressure feed задаёт давление жёстко; сеть подстраивает расход через feed из баланса на outlet-узле.
  • AutomationSpec определяет команды (pressure_setpoint, flow_setpoint, temperature_setpoint) и телеметрию (pressure_actual_pa, flow_actual_kgs) для GUI/SCADA.
  • Поддержка legacy-ключей: P_Pa / P / value для $P_{\text{set}}$; m_dot_kgs / mdot / value для $G_{\text{set}}$ — для совместимости со старыми circuit.json.
Клапан (с составом)
mixing_valve
Объектный
физическийoilgasНефтегазовые аппаратыvalves/mixing_valve/

Stream-aware нефтегазовый клапан с обратимым потоком через состав. Реализует гидравлическую невязку $\Delta P = \rho \cdot (k_v \cdot \mathrm{opening})^2 \cdot \dot m / \sqrt{\rho}$ (с гладкой сшивкой при $|\Delta P| < \Delta P_{\mathrm{smooth}}$). Свойства среды (плотность) берутся из trial-snapshot через ctx.snapshot() и donor — НЕ из атрибутов компонента. Совместим со stream-стеком: StreamProfile .flow_through(...) и thermo_ports добавляют к невязкам уравнения переноса состава и энтальпии (h_out = h_in). В файл mixing_valve.py также экспортируется legacy-обёртка valve_mix для совместимости со старыми цепями (Rh_min-схемы): она активируется, если в attrs есть Rh_min или Rh, и добавляет 3 алгебраических состояния (x0, x1, h).

SPEC.folderНефтегазовые аппараты
Папкаvalves/mixing_valve/
Типфизический
Уровеньobject
Доменoilgas
symMV
bodyMV

Связи компонента (порты)

outletoutrightВыход (давление, Па)
Across-переменная — давление (Па), through-переменная — массовый поток (кг/с). Поток через клапан может быть обратимым (allow_reverse=True в StreamProfile.flow_through). Не применимо.

Параметры и состояние

Входные параметры

opening$1.0$0..1numberСтепень открытия
eps_opening$10^{-3}$0..1numberМин. открытие (защита от 0)
opening_slew$0.0$1/сnumberМакс. скорость открытия
dp_smooth_Pa$10.0$ПаnumberЗона гладкой сшивки при $|\Delta P| < \mathrm{dp\_smooth}$
Rh_min$10^3$Па$\cdot$с/кгnumberСопротивление при полном открытии
x_in$[0.7, 0.3]$—arrayСостав на входе (мольные доли)
h_in$0.0$Дж/кгnumberЭнтальпия на входе

Состояния и интегрирование

В новом стеке собственных состояний нет — гидравлика однозначно вычисляется по opening и плотности. Состав и энтальпия идут как алгебраические невязки пресета flow_through. В legacy-режиме (при наличии Rh_min) — 3 алгебраических состояния (x0, x1, h).

Математическая модель

Невязка гидравлики (классический двухполюсник, 1 значение): \[ F = I - \dot m, \qquad \dot m = k_v \cdot \mathrm{opening} \cdot \sqrt{\rho} \cdot \begin{cases} \dfrac{\Delta P}{\sqrt{\mathrm{dp\_smooth}}}, & |\Delta P| < \mathrm{dp\_smooth}, \\ \sqrt{|\Delta P|}\,\mathrm{sign}(\Delta P), & \text{иначе}, \end{cases} \] где $\Delta P = V[\texttt{a}] - V[\texttt{b}]$. При $|\Delta P| \to 0$ остаётся ненулевая производная $\partial \dot m / \partial \Delta P = k_v \cdot \mathrm{opening} \cdot \sqrt{\rho / \mathrm{dp\_smooth}}$ — это даёт несингулярный Якобиан в нуле (Newton сходится). Плотность $\rho$ берётся через _donor_density(attrs, c["id"], "inlet"):
  1. snapshot = ctx.snapshot(c["id"], "inlet");
  2. если snapshot доступен — $\rho$ = snapshot.rho_kgm3;
  3. иначе — rho_fallback;
  4. если оба не дали положительного значения — ValueError("SNAPSHOT_MISSING").
Пресет stream=flow_through + thermo_ports добавляет невязки переноса состава и энтальпии (живут в mars_core.streams, не дублируются здесь): \[ \begin{aligned} x_{\mathrm{out},i} - x_{\mathrm{in},i} &= 0, \quad i = 0, \ldots, n_{\mathrm{comp}} - 1, \\ h_{\mathrm{out}} - h_{\mathrm{in}} &= 0. \end{aligned} \]

Алгоритм работы

  1. a, b = c["nodes"]; attrs = c["attrs"].
  2. $\Delta P = V[a] - V[b]$.
  3. rho = _donor_density(attrs, c["id"], "inlet").
  4. k_v и opening из attrs (opening_cmd имеет приоритет над opening).
  5. conductance = k_v * opening * sqrt(rho).
  6. Если abs($\Delta P$) < dp_smooth: flow = conductance * $\Delta P$ / sqrt(dp_smooth). Иначе: flow = conductance * sqrt(abs($\Delta P$)) * sign($\Delta P$).
  7. Вернуть $F = I[\texttt{c["id"]}] - \dot m$.
Приёмник (граничное условие)
boundary_sink
Объектный
физическийoilgasНефтегазовые аппаратыoilgas/boundary_sink/

Boundary Sink — граничный сток сети: фиксирует одно из двух граничных условий на inlet-узле. В режиме pressure задаёт давление $P_{\text{inlet}} = P_{\text{set}}$ (Па), сеть подбирает расход через приёмник из баланса. В режиме flow задаёт отбор $\dot m = G_{\text{set}}$ (кг/с; положительный — отбор ИЗ сети). Термодинамику (T, состав) Sink НЕ диктует: он принимает то, что пришло через порт из upstream-цепи. Является парой к boundary_feed: feed — источник (создаёт поток), sink — сток (забирает поток). First-class DAE-компонент, заменивший legacy sink/outlet из mars_core.acausal.

SPEC.folderНефтегазовые аппараты
Папкаoilgas/boundary_sink/
Типфизический
Уровеньobject
Доменoilgas
symSINK
bodySINK

Связи компонента (порты)

Across — давление (Па), through — массовый поток (кг/с). По KCL: $I > 0$ означает отбор ИЗ inlet-узла В землю. Не применимо.

Параметры и состояние

Входные параметры

P_Pa$10^5$ПаnumberУставка давления (для mode=pressure)
m_dot_kgs$0.0$кг/сnumberУставка отбора (для mode=flow)

Состояния и интегрирование

Собственных состояний нет. Параметры читаются из attrs на каждой итерации (stateless — устраняет баг устаревшего состояния при live-патче).

Математическая модель

Невязка (классический двухполюсник, 1 значение): \[ F = \begin{cases} V[\texttt{inlet}] - \lambda \cdot P_{\text{set}}, & \texttt{mode = pressure}, \\ I - \lambda \cdot G_{\text{set}}, & \texttt{mode = flow}. \end{cases} \] Sink НЕ обновляет термодинамику стрима (в отличие от boundary_feed, который задаёт $(T, \text{состав})$): он принимает $(P, T, \text{состав})$ из upstream-компонента (насоса, барабана, АВО) — эти величины определяются решением общей DAE.

Алгоритм работы

  1. in_node = c["nodes"][0]; I_branch = I[c["id"]]; attrs = c["attrs"].
  2. Создать/взять из attrs инстанс BoundarySink(attrs).
  3. Считать V[in_node] и I_branch.
  4. Сформировать невязку по формуле выше.
  5. Опубликовать attrs["_pub"].
  6. Вернуть невязку.
  • BoundarySink(attrs) — класс-обёртка с полями mode, P_set, G_set и методом residuals(c, V, I, dV, dI, t, lam).
  • BoundarySink._port(c) — ленивая фабрика MixPort.
  • _publish_boundary_sink(c, V, I, t, S) — функция публикации для @physical(... publish=...).
  • mars_core.acausal (physical);
  • mars_core.component_spec (ComponentSpec, Port, Param, AutomationSpec, AutomationCommand, AutomationTelemetry);
  • mars_core.stream_profile (StreamProfile.sink(port="inlet"));
  • mars_core.streams (MixPort).
  • oilgas/boundary_feed — парный источник.
  • valves/mixing_valve, oilgas/centrifugal_pump, oilgas/air_cooler, oilgas/drum — типовые upstream-поставщики потока.
  • Соглашение о знаках: $I > 0$ означает отбор ИЗ inlet-узла (поток из сети в землю). Это естественно для sink.
  • Sink не задаёт термодинамику ($(T, \text{состав})$) — в отличие от boundary_feed. Это правильно: сток не определяет, что забирает, только сколько.
  • В режиме flow sink задаёт отбор жёстко; сеть подстраивает давление на inlet-узле из баланса.
  • В режиме pressure sink задаёт давление жёстко; сеть подстраивает отбор через sink из баланса на inlet-узле.
  • AutomationSpec определяет команды (pressure_setpoint, flow_setpoint) и телеметрию (pressure_actual_pa, flow_actual_kgs) для GUI/SCADA.
  • Поддержка legacy-ключей: P_Pa / P / value для $P_{\text{set}}$; m_dot_kgs / mdot / value для $G_{\text{set}}$ — для совместимости со старыми circuit.json.
Регулирующий клапан
actuated_valve
Объектный
физическийoilgasНефтегазовые аппаратыvalves/actuated_valve/

Универсальный динамически-управляемый потокорегулирующий клапан (oilgas) с приводом: задаётся opening_cmd (0..1), реальное открытие opening_actual (состояние, diff) подтягивается к команде с ограниченной скоростью ($\pm 1/\texttt{stroke\_time\_s}$). Закон расхода: $\dot m = k_v \cdot \mathrm{eff} \cdot \sqrt{\rho} \cdot f(\Delta P)$, где $\mathrm{eff} = \mathrm{leak\_fraction} + (1 - \mathrm{leak\_fraction}) \cdot \mathrm{opening\_actual}$. Поддерживает fault-режимы: fault_mode = none / fail_close / fail_open / fail_hold и общий available=False. Используется как типовая регулирующая арматура в цепях регулирования (давление, расход, температура).

SPEC.folderНефтегазовые аппараты
Папкаvalves/actuated_valve/
Типфизический
Уровеньobject
Доменoilgas
symAV
bodyAV

Связи компонента (порты)

outletoutrightВыход (давление, Па)
Across — давление, through — массовый поток (allow_reverse=True). Не применимо.

Параметры и состояние

Входные параметры

opening_cmd$1.0$0..1numberКоманда открытия
opening_initial$1.0$0..1numberНачальное opening_actual
stroke_time_s$2.0$сnumberПолный ход привода
fail_action«close»—comboАварийное поведение: close/open/hold
fault_mode«none»—comboFault: none/fail_close/fail_open/fail_hold
availableTrue—boolПривод в работе
dp_smooth_Pa$10.0$ПаnumberЗона гладкой сшивки при $|\Delta P| < \mathrm{dp\_smooth}$
leak_fraction$10^{-6}$0..1numberДоля утечки (закрыт не до конца)

Состояния и интегрирование

Внутреннее состояние:
  • opening_actual (diff) — текущее открытие (0..1, динамика привода).

Математическая модель

Возвращаемый вектор невязок (2 значения): \[ \begin{aligned} F_0 &= I - \dot m, \\ F_1 &= \dot{\mathrm{opening\_actual}} - \mathrm{rate}(t_{\text{target}}, \mathrm{opening\_actual}), \end{aligned} \] где: \[ \begin{aligned} t_{\text{target}} &= \mathrm{\_target\_opening}(attrs, \mathrm{opening\_actual}), \\ \mathrm{rate} &= \mathrm{clamp}(t_{\text{target}} - \mathrm{opening\_actual},\; -1 / \mathrm{stroke\_time\_s},\; +1 / \mathrm{stroke\_time\_s}). \end{aligned} \] Расход (см. также mixing_valve): \[ \begin{aligned} \rho &\leftarrow \mathrm{\_density}(c), \\ \mathrm{eff} &= \mathrm{leak\_fraction} + (1 - \mathrm{leak\_fraction}) \cdot \mathrm{opening\_actual}, \\ \mathrm{conductance} &= k_v \cdot \mathrm{eff} \cdot \sqrt{\rho}, \\ \Delta P &= V[\texttt{inlet}] - V[\texttt{outlet}], \\ \mathrm{shape} &= \begin{cases} \Delta P / \sqrt{\mathrm{dp\_smooth}}, & |\Delta P| < \mathrm{dp\_smooth}, \\ \sqrt{|\Delta P|}\,\mathrm{sign}(\Delta P), & \text{иначе}, \end{cases} \\ \dot m &= \mathrm{conductance} \cdot \mathrm{shape}. \end{aligned} \] $\mathrm{\_target\_opening}$:
  • fault_mode = fail_open: $t_{\text{target}} = 1.0$ (полное открытие).
  • fault_mode = fail_close: $t_{\text{target}} = 0.0$ (закрыт).
  • fault_mode = fail_hold: $t_{\text{target}} = \mathrm{opening\_actual}$ (удержание).
  • Иначе (fault_mode = none): \begin{itemize}
  • если available = False: $t_{\text{target}}$ по fail_action (close/open/hold).
  • иначе: $t_{\text{target}} = \texttt{opening\_cmd}$.
\end{itemize} Все _target_opening и _density — внутренние функции; для продакшена достаточно знать, что $t_{\text{target}} \in [0, 1]$.

Алгоритм работы

  1. $a, b \leftarrow \texttt{c["nodes"]}$, $attrs \leftarrow \texttt{c["attrs"]}$.
  2. $\mathrm{actual} \leftarrow \mathrm{clamp}(\texttt{io.state["opening\_actual"]})$.
  3. $t_{\text{target}} \leftarrow \mathrm{\_target\_opening}(attrs, \mathrm{actual})$.
  4. $\mathrm{stroke\_time} \leftarrow \mathrm{\_stroke\_time}(attrs)$ (валидация $> 0$).
  5. $\mathrm{rate} \leftarrow \mathrm{clamp}(t_{\text{target}} - \mathrm{actual},\; -1/\mathrm{stroke\_time},\; +1/\mathrm{stroke\_time})$.
  6. $\rho \leftarrow \mathrm{\_density}(c)$ (через snapshot; при отсутствии — fallback на rho_fallback; если оба невалидны — ValueError("inlet density missing")).
  7. $k_v \leftarrow \max(0, \texttt{attrs.get("k\_v", $10^{-4}$})$).
  8. $\mathrm{dp\_smooth} \leftarrow \max(\mathrm{attrs.dp\_smooth}, 10^{-12})$.
  9. $\mathrm{leak} \leftarrow \mathrm{clamp}(\texttt{attrs.leak\_fraction},\, 0,\, 1)$.
  10. $\Delta P \leftarrow V[a] - V[b]$.
  11. $\mathrm{eff} \leftarrow \mathrm{leak} + (1-\mathrm{leak}) \cdot \mathrm{actual}$.
  12. $\mathrm{conductance} \leftarrow k_v \cdot \mathrm{eff} \cdot \sqrt{\rho}$.
  13. $\mathrm{shape} \leftarrow$ формула выше (с $\mathrm{dp\_smooth}$-сшивкой).
  14. $\dot m \leftarrow \mathrm{conductance} \cdot \mathrm{shape}$.
  15. $\mathrm{hydraulic} \leftarrow I[\texttt{c["id"]}] - \dot m$.
  16. Вернуть $[\mathrm{hydraulic},\; \mathrm{dS[ opening\_actual]} - \mathrm{rate}]$.
  17. Опубликовать attrs["_pub"].
Управляемый центробежный насос
controlled_centrifugal_pump
Объектный
физическийoilgasНефтегазовые аппаратыoilgas/controlled_centrifugal_pump/

Расширение базового centrifugal_pump — добавляет команду run_cmd (bool), speed_setpoint (0..1.2), динамику разгона/торможения (first-order, разные постоянные start_tau_s и stop_tau_s), fault-инжекцию (fault_mode: none / trip / electrical_trip / dry_run / mechanical_seizure), latched-trip (trip_latched c защёлкой через trip_latch_tau_s, сбрасывается через reset_trip_cmd), конечное сопротивление в остановленном состоянии (off_resistance, $\sim 10^8$ Па/(кг/с) — не «идеальная» стенка при speed = 0, чтобы Якоби не вырождался). Свойства среды через ctx.snapshot("inlet") — без silent fallback: при отсутствии — ValueError("inlet medium is not bound").

SPEC.folderНефтегазовые аппараты
Папкаoilgas/controlled_centrifugal_pump/
Типфизический
Уровеньobject
Доменoilgas
symP
bodyP

Связи компонента (порты)

outletoutrightНагнетание
Не применимо.

Параметры и состояние

Входные параметры

head_data$[124, 118, 95, 110]$мНапор в точках
eff_data$[0.63, 0.77, 0.75, 0.80]$—КПД
power_data$[298, 359, 527, 440.2]$кВтМощность на валу
nominal_rpm$1489$об/минНоминальные обороты
c_motor$1.0$—Коэф. скорости
time_to_nominal$2.0$сВремя выхода на номинал
run_cmdFalse—Команда пуска (bool)
availableTrue—Доступность (bool)
fault_mode«none»—Fault: none/trip/electrical_trip/dry_run/mechanical_seizure
reset_trip_cmdFalse—Сброс защёлки (bool)
speed_setpoint$1.0$0..1.2Задание скорости (долей номинала)
speed_cmd$1.0$0..1.2Legacy-имя задания
start_tau_s$2.0$сПостоянная разгона
stop_tau_s$0.5$сПостоянная торможения
speed_tau_s$2.0$сLegacy универсальная
trip_latch_tau_s$0.1$сВремя защёлки trip
reset_trip_tau_s$0.1$сВремя сброса
speed_initial$0.0$0..1.2Начальная скорость (legacy)
initial_speed_ratio$0.0$0..1.2Начальная скорость (new)
running_threshold$0.05$0..1.2Порог «работает» для индикации
off_resistance$10^8$Па/(кг/с)Сопротивление в останове
min_flow_m3h$0.0$м³/чМин. допустимый расход (для телеметрии)
flow_policy«forward_only_infeasible»—Политика обратного потока
eps_flow$10^{-3}$кг/сЗащита Якобиана по потоку

Состояния и интегрирование

  • speed_ratio (diff) — дробные обороты (0..1.2, 1 = номинал).
  • trip_latched (diff) — защёлка аварии (0 или 1).
  • h (alg) — энтальпия на нагнетании (определяется через StreamProfile.energy).

Математическая модель

Возвращаемый вектор невязок (4 значения): \[ \begin{aligned} F_0 &= \frac{(V_b - V_a) - \Delta P + R_{\text{off}} \cdot \dot m} {\max(R_{\text{off}} + \Delta p_{\text{scale}},\, 1)}, \\ F_1 &= \dot{s} - \mathrm{\_speed\_rate}(t_{\text{target}},\, s,\, \mathrm{start\_tau},\, \mathrm{stop\_tau}), \\ F_2 &= \dot{L} - \mathrm{\_trip\_latch\_rate}(attrs,\, L), \\ F_3 &= \mathrm{weight} \cdot \frac{|\dot m| (S[h] - h_{\text{in}}) - W}{1.2 \cdot 10^9} \\ &\quad + (1 - \mathrm{weight}) \cdot \frac{S[h] - h_{\text{in}}}{4 \cdot 10^6}. \end{aligned} \] Здесь:
  • $a, b$ = c["nodes"].
  • $s = S[\texttt{"speed\_ratio"}]$ (дробные обороты).
  • $L = S[\texttt{"trip\_latched"}]$ (0 или 1).
  • $\mathrm{weight} = \dot m^2 / (\dot m^2 + \varepsilon_{\dot m}^2)$ (сглаживание по $|\dot m|$).
  • $\Delta p_{\text{scale}} = \max(|\texttt{head\_data}|) \cdot g \cdot \rho \cdot s^2$ (характерный напор).
  • $\Delta P$ — из _operating_values: квадратичный экстраполятор каталога с flow_smoothing и forward_q ($\ge 0$) для forward_only-политики; знаковый $\mathrm{copysign} \cdot head(\cdot)$ для bidirectional.
  • $W = \Delta P \cdot q/3600 / \mathrm{eff}$ — мощность на валу через изотермический гидравлический баланс (N_hydraulic = eff $\cdot$ N_shaft); не использует независимый power_poly, иначе нарушается согласование при изменении $\rho$ или типа среды.
$\mathrm{\_target\_speed}$: \[ \begin{aligned} t_{\text{target}} &= \begin{cases} 0.0, & \text{если } \neg \mathrm{run\_cmd}, \\ 0.0, & \text{если } \neg \mathrm{available} \lor \mathrm{tripped}, \\ \mathrm{clamp}(\texttt{speed\_setpoint} \lor \texttt{speed\_cmd}, 0, 1.2), & \text{иначе}. \end{cases} \end{aligned} \] $\mathrm{\_speed\_rate}$ (первого порядка, разные постоянные): \[ \begin{aligned} \tau &= \begin{cases} \mathrm{start\_tau\_s}, & \text{если } t_{\text{target}} \ge s, \\ \mathrm{stop\_tau\_s}, & \text{иначе}. \end{cases} \\ \mathrm{rate} &= \frac{t_{\text{target}} - s}{\tau}. \end{aligned} \] $\mathrm{\_trip\_latch\_rate}$ (защёлка): \[ \begin{aligned} \mathrm{rate} &= \begin{cases} (1 - L) / \tau_{\text{latch}}, & \text{если } \mathrm{fault\_active}, \\ -L / \tau_{\text{reset}}, & \text{если } \mathrm{reset\_trip\_cmd}, \\ 0, & \text{иначе}. \end{cases} \end{aligned} \] off_resistance: при $s = 0$ (останов) клапан имеет конечное сопротивление $\sim 10^8$ Па/(кг/с); плавный переход $R_{\text{off}} = 10^8 \cdot (1 - s_{\text{smooth}})$, $s_{\text{smooth}} = \mathrm{clamp}(s / r_{\text{threshold}})^2 \cdot (3 - 2 \,\mathrm{clamp}(s/r_{\text{threshold}}))$. Избегает «идеального давления» источника, вырождающего Якоби.

Алгоритм работы

  1. $a, b \leftarrow \texttt{c["nodes"]}$, $attrs \leftarrow \texttt{c["attrs"]}$.
  2. _operating_values(c, I, S) (snapshot $\rho, h_{\text{in}}$; $\dot m = I[\texttt{c["id"]}]$; $q = \dot m/\rho \cdot 3600$; $\texttt{pump = \_get\_pump(attrs)}$; $\mathrm{eff} = \max(\texttt{pump.efficiency}, 10^{-3}$); $\Delta P$, $W$, $s$). Если snapshot недоступен/нефин — ValueError("inlet medium is not bound").
  3. $\mathrm{tripped} = \mathrm{\_fault\_active} \lor S[\texttt{"trip\_latched"}] \ge 0.5$.
  4. $t_{\text{target}} = \mathrm{\_target\_speed}(attrs, \mathrm{tripped})$.
  5. $F_1 = \dot{s} - \mathrm{\_speed\_rate}(t_{\text{target}}, s, \mathrm{start\_tau\_s}, \mathrm{stop\_tau\_s})$.
  6. $F_2 = \dot{L} - \mathrm{\_trip\_latch\_rate}(attrs, L)$.
  7. $R_{\text{off}} = \max(0,\,\mathrm{attrs.off\_resistance})$.
  8. $s_{\text{off}} = \mathrm{\_off\_resistance\_fraction}(s, \mathrm{running\_threshold})$ (плавный C$^\infty$-переход).
  9. $\Delta p_{\text{scale}} = \max(|\texttt{head\_data}|) \cdot g \cdot \rho \cdot s^2$.
  10. $\mathrm{branch} = ((V_b - V_a) - \Delta P + R_{\text{off}} \cdot s_{\text{off}} \cdot \dot m) / \max(R_{\text{off}} \cdot s_{\text{off}} + \Delta p_{\text{scale}},\, 1)$.
  11. $\varepsilon_{\dot m} = \max(\mathrm{eps\_flow},\, 10^{-12})$; $\mathrm{weight} = \dot m^2/(\dot m^2 + \varepsilon_{\dot m}^2)$.
  12. $F_3 = \mathrm{weight} \cdot (|\dot m|(S[h] - h_{\text{in}}) - W) / (300 \cdot 4 \cdot 10^6) + (1 - \mathrm{weight}) \cdot (S[h] - h_{\text{in}}) / (4 \cdot 10^6)$.
  13. Опубликовать attrs["_pub"].
  14. Вернуть $[F_0, F_1, F_2, F_3]$.
Центробежный насос
centrifugal_pump
Объектный
физическийoilgasНефтегазовые аппаратыoilgas/centrifugal_pump/

Центробежный насос как гидравлическая ветвь acausal-цепи. Реализует напорную характеристику по каталожным точкам (аппроксимация полиномом 2-й степени), аффинное подобие по оборотам двигателя, разгон двигателя (аналитика по t), и энергобаланс через энтальпию на выкиде. Свойства среды ($\rho$, $h$, $c_p$, $\mu$, $\lambda$, состав) берутся из порта inlet.medium через trial-snapshot — НЕ из атрибутов компонента. Использует новый стрим-стек (MaterialStream, MixPort). Внутри компонент выделен в класс CentrifugalPump с полиномами head_poly, eff_poly, power_poly и об/мин двигателя; инстанс кэшируется в attrs["__pump"] по сигнатуре параметров.

SPEC.folderНефтегазовые аппараты
Папкаoilgas/centrifugal_pump/
Типфизический
Уровеньobject
Доменoilgas
symP
bodyP

Связи компонента (порты)

outletoutrightНагнетание (давление, Па)
Across — давление (Па), through — массовый поток (кг/с). $I > 0$ — поток в естественном направлении через насос. Не применимо.

Параметры и состояние

Входные параметры

head_data$[124, 118, 95, 110]$мarrayНапор в каталожных точках
eff_data$[0.63, 0.77, 0.75, 0.80]$—arrayКПД (допустимо в \%)
power_data$[298, 359, 527, 440.2]$кВтarrayМощность на валу
c_motor$1.0$—numberКоэффициент скорости (множитель)
time_to_nominal$2.0$сnumberВремя выхода на номинал
nominal_rpm$1489$об/минnumberНоминальные обороты
flow_policy«forward_only_infeasible»—comboПолитика обратного потока
eps_flow$10^{-3}$кг/сnumberЗащита от деления на ноль по потоку

Состояния и интегрирование

Внутренние состояния:
  • h (alg) — алгебраическое состояние энтальпии на выкиде (Дж/кг). Решатель подбирает S["h"] так, чтобы невязка S["h"] - (h_in + W/$\dot{m}$) = 0.

Математическая модель

Невязки (2 значения): \[ \begin{aligned} F_0 &= (V[\texttt{b}] - V[\texttt{a}]) - \Delta P(\dot m, t), \\ F_1 &= \frac{\dot m^2}{\dot m^2 + \varepsilon_{\dot m}^2} \cdot \frac{|\dot m| (S[\texttt{"h"}] - h_{\text{in}}) - W}{300 \cdot 4 \cdot 10^6} \\ &\quad + \left(1 - \frac{\dot m^2}{\dot m^2 + \varepsilon_{\dot m}^2}\right) \cdot \frac{S[\texttt{"h"}] - h_{\text{in}}}{4 \cdot 10^6}. \end{aligned} \] $\Delta P(\dot m, t)$: в режиме bidirectional — $\Delta P = \mathrm{sign}(\dot m) \cdot \mathrm{head}(|\dot m| / \rho \cdot 3600,\, k) \cdot g \cdot \rho$; иначе — $\Delta P = \mathrm{head}(\max(\dot m / \rho \cdot 3600,\, 0),\, k) \cdot g \cdot \rho$. Здесь $k = \mathrm{coef\_velocity}(t)$ — аналитический разгон двигателя: \[ k(t) = c_{\text{motor}} \cdot \begin{cases} 1, & \mathrm{time\_to\_nominal} \le 0, \\ 1 - \exp(-6.75\, t / \mathrm{time\_to\_nominal}), & \text{иначе}. \end{cases} \] $\mathrm{head}(q, k)$ и $\mathrm{power\_w}(q, k)$ — полиномы по $q$ (м³/ч), домноженные на $k^2$ и $k^3$ соответственно; защита от отрицательных значений (max(..., 0)). КПД: $\mathrm{eff}(q)$ — полином, обрезанный в $[0, 1]$.

Алгоритм работы

  1. a, b = c["nodes"]; attrs = c["attrs"]; m_dot_in = I[c["id"]] (скаляр).
  2. Получить/создать CentrifugalPump(attrs) через _get_pump(attrs) (кэш в attrs["__pump"]).
  3. inlet = self._port(c, "inlet"); outlet = self._port(c, "outlet").
  4. Через residual_ctx.snapshot(c["id"], "inlet") получить $\rho$, h_in (trial snapshot — иначе inlet.density, inlet.enthalpy_mass). Если оба не определены — ValueError("inlet medium is not bound").
  5. $k = \mathrm{coef\_velocity}(t)$.
  6. $q = m_{\text{dot}} / \rho \cdot 3600$ (с защитой $\rho$ >= 1e-9).
  7. dp_pump по flow_policy (см. формулу выше).
  8. branch = (V[b] - V[a]) - dp_pump.
  9. shaft_w = power_w(|q|, k); weight = $\dot m^2$ / ($\dot m^2$ + $\varepsilon_{\dot m}^2$).
  10. energy_res по нормированной формуле выше; spec_energy = shaft_w / |$\dot{m}$| (или 0 при $|\dot m| < \varepsilon_{\dot m}$); h_out_target = h_in + spec_energy.
  11. Опубликовать attrs["_pub"].
  12. Вернуть [branch, energy_res].
Обратный клапан
check_valve
Объектный
физическийoilgasНефтегазовые аппараты/Арматура и сопротивленияvalves/check_valve/

Пассивный обратный клапан: пропускает поток только в прямом направлении (inlet $\to$ outlet), когда $\Delta P = P_{\text{inlet}} - P_{\text{outlet}}$ превышает давление открытия (cracking_pressure_Pa); при обратном направлении — утечка с $\mathrm{k\_v}_{\text{reverse}}$ (обычно на 6 порядков меньше прямого). Гладкая (C$^\infty$) аппроксимация замыкания: $\sqrt{\Delta P} \cdot \exp(-\mathrm{dp\_smooth}/\Delta P)$ — ноль при $\Delta P \le 0$, квадратичный рост при $\Delta P > \mathrm{cracking}$ без сингулярности Якобиана в нуле. Поддержка старого синтаксиса: k_v / cracking_pressure_Pa (одна буква), k_v_forward / cracking_pressure_pa (предпочтительно). Используется как типовой обратный клапан между секциями трубопровода, на нагнетании насоса и т. п.

SPEC.folderНефтегазовые аппараты/Арматура и сопротивления
Папкаvalves/check_valve/
Типфизический
Уровеньobject
Доменoilgas
symCV
bodyCV

Связи компонента (порты)

outletoutrightВыход (давление, Па)
Across — давление, through — массовый поток (allow_reverse=True, но $\dot m < 0$ сильно подавлено). Не применимо.

Параметры и состояние

Входные параметры

k_v$10^{-4}$кг/(с$\sqrt{\text{Па}}$)numberПрямой (legacy)
cracking_pressure_pa$0.0$ПаnumberДавление открытия
cracking_pressure_Pa$0.0$ПаnumberДавление открытия (legacy)
reverse_leak_k_v$10^{-10}$кг/(с$\sqrt{\text{Па}}$)numberОбратная утечка
dp_smooth_Pa$10.0$ПаnumberЗона плавного закрытия

Состояния и интегрирование

Собственных состояний нет — статика.

Математическая модель

Невязка (1 значение): \[ F = I - \mathrm{flow\_rate}(c,\; \Delta P), \] где flow_rate: \[ \begin{aligned} \Delta P &= V[\texttt{inlet}] - V[\texttt{outlet}], \\ \rho &\leftarrow \max(\mathrm{\_density}(c), 10^{-12}), \\ \mathrm{root}_+(p, \sigma) &= \begin{cases} 0, & p \le 0, \\ \sqrt{p} \cdot \exp(-\min(\sigma / p,\, 700)), & \text{иначе}, \end{cases} \\ \mathrm{forward} &= k_{v,f} \cdot \sqrt{\rho} \cdot \mathrm{root}_+(\Delta P - p_{\text{crack}},\, \mathrm{dp\_smooth}), \\ \mathrm{reverse} &= k_{v,r} \cdot \sqrt{\rho} \cdot \mathrm{root}_+(-\Delta P,\, \mathrm{dp\_smooth}), \\ \mathrm{flow\_rate} &= \mathrm{forward} - \mathrm{reverse}. \end{aligned} \] _closed_root (C$^\infty$-замыкание) — ключевая идея:
  • При $\Delta P \le 0$: $\mathrm{root} = 0$ — идеальный «закрыт».
  • При $0 < \Delta P < p_{\text{crack}}$: $\sqrt{\Delta P} \cdot \exp(-\sigma / \Delta P) \to 0$ экспоненциально быстро.
  • При $\Delta P \gg p_{\text{crack}}$: $\exp(-\sigma/\Delta P) \to 1$, $\mathrm{root} \approx \sqrt{\Delta P}$ — как обычный клапан.
Гладкая (C$^\infty$) стыковка без разрыва $\to$ Якоби не имеет «ударной» особенности в $\Delta P = 0$ — Ньютон сходится. Параметр dp_smooth_Pa:
  • dp_smooth = max(пользовательское, $0.05 \cdot p_{\text{crack}}$); при $p_{\text{crack}} = 0$ остаётся floor $10$ Па.
  • Полоса «сшивки» масштабируется с $p_{\text{crack}}$ — при больших $p_{\text{crack}}$ (килоПаскаль) полоса автоматически тоже большая; «dead-headed pump start» (насос против закрытого нагнетания) не «прыгает» на мегаПаскаль за итерацию.

Алгоритм работы

  1. $a, b \leftarrow \texttt{c["nodes"]}$, $attrs \leftarrow \texttt{c["attrs"]}$.
  2. $\Delta P \leftarrow V[a] - V[b]$.
  3. Считать настройки через _settings(attrs): k_v = k_v_forward или k_v, $p_{\text{crack}}$, reverse_leak_k_v, dp_smooth.
  4. $\rho \leftarrow$ через _density(c) (snapshot или rho_fallback; защита от нуля max($\rho$, $10^{-12}$)).
  5. Вычислить forward, reverse по формуле.
  6. $\dot m \leftarrow \mathrm{forward} - \mathrm{reverse}$.
  7. Вернуть $I[\texttt{c["id"]}] - \dot m$.
  8. Опубликовать attrs["_pub"].
Технологическое сопротивление
process_resistance
Объектный
физическийoilgasНефтегазовые аппараты/Арматура и сопротивленияoilgas/process_resistance/

Универсальное пассивное гидравлическое сопротивление для oilgas-цепей: задаёт линейный $\Delta P$ с необязательным квадратичным членом ($R_{\text{linear}} \dot m$ и $K_{\text{quad}} \dot m^2$), всемирным коэф. загрязнения (fouling_factor), ручным множителем (resistance_multiplier) и флагом «забит/отключён» (blocked, увеличивает сопротивление на blocked_multiplier). Поддерживает обратимость через allow_reverse=True и знаковый flow_policy (bidirectional, check_valve, forward_only_infeasible). Используется как типовое технологическое сопротивление (трубопроводы, фильтры, диафрагмы, теплообменники, погружные насосы в стеснённых условиях и т. п.).

SPEC.folderНефтегазовые аппараты/Арматура и сопротивления
Папкаoilgas/process_resistance/
Типфизический
Уровеньobject
Доменoilgas
symR
bodyR

Связи компонента (порты)

outletoutrightВыход (давление, Па)
Across — давление, through — массовый поток. allow_reverse=True. Не применимо.

Параметры и состояние

Входные параметры

resistance_pa_per_kgs$10^3$Па/(кг/с)numberЛинейный коэф.
resistance_multiplier$1.0$—numberМножитель (ручной)
K_quad$0.0$Па/(кг/с)$^2$numberКвадратичный коэф.
fouling_factor$0.0$—numberМножитель загрязнения
blockedFalse—boolЗаслонка (резко \$\uparrow R\$)
blocked_multiplier$10^6$—numberМножитель при blocked
flow_policy«forward_only_infeasible»—comboПолитика обратного потока
eps_flow$10^{-3}$кг/сnumberЗащита от деления на ноль

Состояния и интегрирование

Собственных состояний нет — чистая алгебраическая модель (статика на каждом тике).

Математическая модель

Невязка (1 значение): \[ F = \frac{V[\texttt{inlet}] - V[\texttt{outlet}] - \Delta P_{\text{loss}}}{\mathrm{scale}}. \] Значение $\Delta P_{\text{loss}}$ (см. функцию pressure_loss(c, m_dot)): \[ \begin{aligned} m_{\text{clamp}} &= \frac{m_{\text{dot}}}{\sqrt{m_{\text{dot}}^2 + \varepsilon_{\dot m}^2}}, \\ \Delta P_{\text{loss}} &= m_{\text{clamp}} \cdot \bigl(R_{\text{linear}} + K_{\text{quad}} |m_{\text{dot}}|\bigr) \cdot \text{mult} \cdot (1 + f) \cdot b, \\ \text{mult} &= \text{resistance\_multiplier}, \\ f &= \text{fouling\_factor}, \\ b &= \text{blocked\_multiplier}, \quad \text{если \texttt{blocked=True}, \quad \text{иначе } 1. \end{aligned} \] Знак несёт $m_{\text{clamp}}$: пассивное сопротивление сохраняет знак входа. Нормализация невязки (для устойчивости): \[ \mathrm{scale} = \max\bigl(R_{\text{linear}} + 2 K_{\text{quad}} |\dot m| \bigr) \cdot \text{mult} \cdot (1 + f) \cdot b \] (при $|K_{\text{quad}}| \cdot |\dot m|$ << $R_{\text{linear}}$ невязка ~ Па / (Па/(кг/с)) = безразмерный; при больших $K_{\text{quad}}$ нормализация удерживает масштаб). Сложность $\Delta P$:
  • Линейный член доминирует при малых $\dot m$ — типично для длинных трубопроводов, шероховатых стенок.
  • Квадратичный член при больших $\dot m$ — типично для диафрагм, поворотов, клапанов (турбулентная потеря).
  • blocked=True увеличивает $R$ в blocked_multiplier раз (~$10^6$) — фактически закрытый канал, остаточный «поток» только из eps_flow.

Алгоритм работы

  1. Считать параметры через _settings(attrs).
  2. $\dot m \leftarrow I[\texttt{c["id"]}]$.
  3. Если $|\dot m|$ не конечно — взять $\pm$sys.float_info.max (или 0, если None).
  4. $m_{\text{clamp}}, \Delta P_{\text{loss}}$ — через pressure_loss.
  5. $\mathrm{scale} \leftarrow \max\bigl(R_{\text{linear}} + 2 K_{\text{quad}} |\dot m|\bigr) \cdot \text{mult} \cdot (1 + f) \cdot b$, $\mathrm{scale} \ge 1$.
  6. Вернуть $\bigl(V[\texttt{inlet}] - V[\texttt{outlet}] - \Delta P_{\text{loss}}\bigr) / \mathrm{scale}$.
  7. Опубликовать attrs["_pub"].
Трансформатор
transformer
Объектный
физическийelectricalПассивные элементыpower/transformer/

Идеализированный четырёхполюсник — двухобмоточный трансформатор без потерь. Первичная обмотка: in_star + in; вторичная: out_star + out. «star» — одноимённые зажимы (согласное включение). Реализует отношение напряжений $K = U_{\text{in}} / U_{\text{out}} \approx W_1 / W_2$ и сохранение ампер-витков. Намагничивающий ток не моделируется (идеализация). Используется как типовая модель трансформатора в электрических подсхемах ORPHYS (блоки питания, согласование уровней).

SPEC.folderПассивные элементы
Папкаpower/transformer/
Типфизический
Уровеньobject
Доменelectrical
symT
bodyT

Связи компонента (порты)

ininleftelectricalПервичная обмотка, обратный зажим
out_staroutrightelectricalВторичная обмотка, «звезда»
outoutrightelectricalВторичная обмотка, обратный зажим
Across — потенциал (В), through — ток (А). Не применимо.

Параметры и состояние

Состояния и интегрирование

Собственных состояний нет — идеальный трансформатор описывается линейной алгеброй.

Математическая модель

Возвращаемый вектор невязок (4 значения): \[ \begin{aligned} F_0 &= (V[\texttt{in\_star}] - V[\texttt{in}]) - K \cdot (V[\texttt{out\_star}] - V[\texttt{out}]), \\ F_1 &= I[\texttt{in\_star}] + I[\texttt{out\_star}] / K, \\ F_2 &= I[\texttt{in\_star}] + I[\texttt{in}], \\ F_3 &= I[\texttt{out\_star}] + I[\texttt{out}]. \end{aligned} \] Физический смысл:
  • $F_0$ — отношение напряжений: $U_{\text{in}} = K \cdot U_{\text{out}}$.
  • $F_1$ — сохранение ампер-витков: $I_1 W_1 + I_2 W_2 = 0 \Leftrightarrow I_1 + I_2 / K = 0$ (с учётом знаков «звезды»).
  • $F_2, F_3$ — непрерывность тока в каждой обмотке (KCL).
Намагничивающий ток не моделируется (идеализация); в реальном трансформаторе добавляется ветвь намагничивания ($R_m, L_m$) параллельно первичке.

Алгоритм работы

  1. a = elem.port("in_star"), b = elem.port("in"), c = elem.port("out_star"), d = elem.port("out"); attrs = elem._c["attrs"].
  2. K = float(attrs.get("K", attrs.get("Коэффициент", 2.0))); if abs(K) < 1e-12: K = 1e-12 (защита от деления на ноль).
  3. Сформировать список невязок по формулам выше.
  4. @physical-обёртка через Elem(c, V, I, dV, dI, t, lam, _PORTS, _DIRS, True) (multi-port, 4 узла, 4 направления: ('in', 'in', 'out', 'out')).
Диодный мост
diode_bridge
Объектный
физическийelectricalПолупроводникиpower/diode_bridge/

Четырёхполюсник — мост Гретца на четырёх диодах Шокли. Вход — переменное напряжение (ac1, ac2), выход — выпрямленное (dc_p, dc_n). Внутри: $D_1$: ac1 → dc_p; $D_2$: ac2 → dc_p; $D_3$: dc_n → ac1; $D_4$: dc_n → ac2. ВАХ диода — стандартная модель Шокли: $i = I_s (\exp(U / (n V_t)) - 1)$ с ограничительной экспонентой limexp (pnjlim) для устойчивости Ньютона и параллельной проводимостью $G_{\min}$ (как в SPICE). Параметр Ud (прямое падение) задаёт рабочую точку: $I_s$ подбирается так, чтобы при токе 1 мА падение было $\approx$ Ud (кремний $\sim 0.7$ В, Шоттки $\sim 0.3$ В). Используется как типовая модель выпрямителя (вместо упрощённой rectifier, где диод — идеальная «защёлка»).

SPEC.folderПолупроводники
Папкаpower/diode_bridge/
Типфизический
Уровеньobject
Доменelectrical
symVDB
bodyVDB

Связи компонента (порты)

ac2inrightelectricalПеременный вход 2
dc_pouttopelectricalПостоянный выход «+»
dc_noutbottomelectricalПостоянный выход «-»
Across — потенциал (В), through — ток (А). Не применимо.

Параметры и состояние

Входные параметры

Также поддерживаются legacy-ключи: Прямое падение (для Ud). Скрытые параметры: Is, Vt (по умолчанию $0.02585$ В), n (по умолчанию $1.0$), Gmin (по умолчанию $10^{-9}$ См).

Состояния и интегрирование

Собственных состояний нет. Неявная алгебраическая система (токи — мгновенные функции напряжений через ВАХ диодов).

Математическая модель

ВАХ каждого диода (с защитой от переполнения через limexp и параллельной проводимостью $G_{\min}$): \[ i_d(u) = I_s \cdot (\mathrm{limexp}(u / (n V_t)) - 1) + G_{\min} \cdot u \] Распределение диодов в мосте: \[ \begin{aligned} i_{D_1} &= i_d(V_{\text{ac1}} - V_{\text{dc\_p}}), \\ i_{D_2} &= i_d(V_{\text{ac2}} - V_{\text{dc\_p}}), \\ i_{D_3} &= i_d(V_{\text{dc\_n}} - V_{\text{ac1}}), \\ i_{D_4} &= i_d(V_{\text{dc\_n}} - V_{\text{ac2}}). \end{aligned} \] Возвращаемый вектор невязок (4 значения): \[ \begin{aligned} F_0 &= I[\texttt{ac1}] - (i_{D_1} - i_{D_3}), \\ F_1 &= I[\texttt{ac2}] - (i_{D_2} - i_{D_4}), \\ F_2 &= I[\texttt{dc\_p}] - (-(i_{D_1} + i_{D_2})), \\ F_3 &= I[\texttt{dc\_n}] - (i_{D_3} + i_{D_4}). \end{aligned} \] Калибровка $I_s$ по Ud (при токе $I_{\text{ref}} = 1$ мА падение $\approx$ Ud): \[ I_s = \frac{I_{\text{ref}}}{\exp(\texttt{Ud} / V_t) - 1} \] Защита: Ud ограничен снизу $0.05$ В (иначе $I_s$ взрывается при Ud $\to 0$).

Алгоритм работы

  1. ac1 = elem.port("ac1"), ac2 = elem.port("ac2"), dcp = elem.port("dc_p"), dcn = elem.port("dc_n"); attrs = elem._c["attrs"].
  2. Ud = max(float(attrs.get("Ud", attrs.get("Прямое падение", 0.7))), 0.05).
  3. Is = float(attrs.get("Is", 0.0)) or _Is_from_Ud(Ud).
  4. Vt = float(attrs.get("Vt", 0.02585)); n = float(attrs.get("n", 1.0)); gmin = float(attrs.get("Gmin", attrs.get("gmin", 1e-9))).
  5. Вычислить токи $i_{D_1} \ldots i_{D_4}$ через _diode_i.
  6. Сформировать 4 невязки.
  7. @physical-обёртка через Elem(... _PORTS, _DIRS, True) (4 порта, multi).
DC/DC buck-преобразователь
dc_dc_buck
Логический
логическийistochnikiistochniki/dc_dc_buck/

Усреднённая модель понижающего (buck) DC/DC-преобразователя с ШИМ-управлением: выходное напряжение $U_{\text{out}} = D \cdot U_{\text{in}} \cdot \eta$ (стационар), с LC-фильтром ($L$ — индуктивность дросселя, $C$ — ёмкость выходного конденсатора) и нагрузкой $R_{\text{load}}$. Усреднение за период ШИМ снимает высокочастотную пульсацию ключа и даёт гладкую динамику, пригодную для исследования переходных процессов во вторичных цепях питания. Внутреннее состояние — токи/напряжения LC-фильтра; шаг — неявный Эйлер (для устойчивости при больших dt).

SPEC.folderistochniki/dc_dc_buck/
Папкаistochniki/dc_dc_buck/
Типлогический
Уровеньlogic

Связи компонента (порты)

U_outoutrightВыходное напряжение, В
Не применимо.

Параметры и состояние

Входные параметры

L$100\!\cdot\!10^{-6}$ГнnumberИндуктивность дросселя
C$100\!\cdot\!10^{-6}$ФnumberЁмкость выходного конденсатора
R_load$10.0$ОмnumberСопротивление нагрузки (или R)
eta$0.9$—numberКПД

Состояния и интегрирование

Внутренние состояния:
  • io.state["i_L"] — ток дросселя;
  • io.state["u_out"] — напряжение выхода (сглаженное, до применения $\eta$).

Математическая модель

Не применимо для логических/мета компонентов.

Алгоритм работы

Усреднённые уравнения LC-фильтра: \[ \begin{aligned} L \frac{dI_L}{dt} &= D U_{\text{in}} - U_{\text{out}}, \\ C \frac{dU_{\text{out}}}{dt} &= I_L - \frac{U_{\text{out}}}{R_{\text{load}}}. \end{aligned} \] Неявная дискретизация (для $I_L$-секции — ступенчатая аппроксимация $R_{\text{load}}$): \[ \begin{aligned} I_L^{\text{new}} &= \frac{I_L + dt \cdot D U_{\text{in}} / L}{1 + dt \cdot r_{\text{eff}} / L}, \\ U_{\text{out}}^{\text{new}} &= \frac{U_{\text{out}} + dt \cdot I_L^{\text{new}} / C}{1 + dt / (R_{\text{load}} C)}. \end{aligned} \] Выход: io.write("U_out", U_out_new * eta).
  1. Считать u_in, D, L, C, R_load, eta, dt через io.param.
  2. i_L = io.state.get("i_L", 0.0), u_out = io.state.get("u_out", 0.0).
  3. Ступенчатая аппроксимация $r_{\text{eff}} = \max(u_{\text{out}} / (i_L + \varepsilon), 1\!\cdot\!10^{-9})$ при $|i_L| > \varepsilon$, иначе $r_{\text{eff}} = R_{\text{load}}$.
  4. Применить формулы неявного Эйлера.
  5. Сохранить i_L, u_out; выдать U_out * eta.
Выпрямитель мостовой
rectifier
Логический
логическийistochnikiistochniki/vypryamitel/

Упрощённая функциональная модель мостового выпрямителя (мост Гретца) для лабораторной работы № 1 по ИВЭП. Вход — мгновенное напряжение переменного тока, выход — двухполупериодное выпрямленное напряжение с учётом падения на двух последовательных диодах ($2 \cdot U_d$). Используется в составе ИВЭП: сеть $\to$ трансформатор $\to$ rectifier $\to$ LC-фильтр $\to$ стабилизатор $\to$ нагрузка. Без состояния.

SPEC.folderistochniki/vypryamitel/
Папкаistochniki/vypryamitel/
Типлогический
Уровеньlogic

Связи компонента (порты)

U_outoutrightВыходное напряжение (DC), В
Не применимо.

Параметры и состояние

Состояния и интегрирование

Состояние отсутствует.

Математическая модель

Не применимо для логических/мета компонентов.

Алгоритм работы

\[ U_{\text{out}} = \max\bigl(0,\; |U_{\text{in}}| - 2 U_d\bigr) \]
  1. u = io.read("U_in"), Ud = io.param("Ud", 0.7).
  2. io.write("U_out", max(0.0, abs(u) - 2.0 * Ud)).
Датчик температуры
temperature_sensor
Логический
логическийdatchikidatchiki/datchik_temperatury/

Линейное преобразование сопротивления терморезистора (Ом) в температуру (°C): $T = \mathrm{gain} \cdot R + \mathrm{offset}$. Коэффициенты gain и offset задаются пользователем и по умолчанию соответствуют характеристике типового платинового ТС (PT100 в районе 0 °C). Простой безынерционный преобразователь. Файл помечен как «Сгенерировано ORPHYS Component Generator»; правки делаются через редактор компонентов, не вручную.

SPEC.folderdatchiki/datchik_temperatury/
Папкаdatchiki/datchik_temperatury/
Типлогический
Уровеньlogic

Связи компонента (порты)

T_CoutrightТемпература, °C
Не применимо.

Параметры и состояние

Входные параметры

offset$-259.7402597$°CnumberСмещение характеристики

Состояния и интегрирование

Состояние отсутствует.

Математическая модель

Не применимо для логических/мета компонентов.

Алгоритм работы

\[ T = \mathrm{gain} \cdot R + \mathrm{offset} \]
  1. x = io.read("R_Ohm").
  2. gain = io.param("gain", 2.5974025974), offset = io.param("offset", -259.7402597).
  3. io.write("T_C", x * gain + offset).
Драйвер L293D
l293d_driver
Логический
логическийdvigatelidvigateli/drayver_l293d/

Логическая модель H-моста L293D. Три входа (IN1, IN2, ENA), где IN1 — сигнальный вход, IN2 и ENA — параметры (у L293D три входа, но в библиотечной шине для простоты один сигнальный порт). Выход OUT1 — основной, плюс публикация внутренних OUT2 и I_out в io.state для тестов и других компонентов. Используется как типовая силовая часть для DC-двигателей и шаговых двигателей.

SPEC.folderdvigateli/drayver_l293d/
Папкаdvigateli/drayver_l293d/
Типлогический
Уровеньlogic

Связи компонента (порты)

OUT1outrightВыход 1 (на нагрузку)
Не применимо.

Параметры и состояние

Входные параметры

ENA$1.0$—numberРазрешение (или en)
Vs$12.0$ВnumberНапряжение питания
Uce_sat$1.8$ВnumberНапряжение насыщения ключа
R_load$10.0$ОмnumberСопротивление нагрузки (или R)

Состояния и интегрирование

Состояние используется только как «опубликованное хранилище» выходов, не как интегратор.

Математическая модель

Не применимо для логических/мета компонентов.

Алгоритм работы

Таблица истинности:
100$0$$0$свободный ход
110$V_s - 2U_{ce}$$0$вперёд
101$0$$V_s - 2U_{ce}$назад
111$0$$0$торможение (brake)
\[ I_{\text{out}} = \frac{\mathrm{OUT1} - \mathrm{OUT2}}{R_{\text{load}}} \]
  1. Считать in1, in2, ena, Vs, Uce, R через io.read/io.param.
  2. v_drop = max(0, Vs - 2*Uce).
  3. По таблице истинности выбрать пару (out1, out2).
  4. i_out = (out1 - out2) / R.
  5. io.write("OUT1", out1); io.state["OUT2"] = out2; io.state["I_out"] = i_out.
Источник значения
value_source
Логический
логическийistochnikiistochniki/istochnik_znacheniya/

Универсальный источник тестового сигнала. Поддерживает пять форм: const, ramp (линейный рост), sine (синус), square (меандр), step (ступенька). Форма выбирается параметром mode (строка) или mode_id (число). Используется как стандартное воздействие при тестировании САУ. Без состояния; модельное время берётся из io.ts. Файл помечен <<Сгенерировано ORPHYS Component Generator>>; правки делаются через редактор компонентов, не вручную.

SPEC.folderistochniki/istochnik_znacheniya/
Папкаistochniki/istochnik_znacheniya/
Типлогический
Уровеньlogic

Связи компонента (порты)

valueoutrightТекущее значение сигнала
Не применимо.

Параметры и состояние

Входные параметры

amplitude$20.0$ед.numberАмплитуда $A$
freq$0.1$ГцnumberЧастота (для sine/square)
slope$1.0$ед./сnumberНаклон (для ramp)
t0$0.0$сnumberМомент включения (для step)
mode«»—textИмя формы: const / ramp / sine / square / step
mode_id$2$—intКод формы (если mode пуст): 0/1/2/3/4

Состояния и интегрирование

Состояние отсутствует.

Математическая модель

Не применимо для логических/мета компонентов.

Алгоритм работы

\[ \mathrm{value} = \mathrm{signal}(\mathrm{mode},\, t) \] где signal одна из: \[ \begin{aligned} \mathrm{const:} & \;\; b \\ \mathrm{ramp:} & \;\; \mathrm{slope} \cdot t + b \\ \mathrm{sine:} & \;\; b + A \sin(2\pi f t) \\ \mathrm{square:} & \;\; b + A \cdot \mathrm{sign}\bigl(\sin(2\pi f t)\bigr) \\ \mathrm{step:} & \;\; b + A \cdot [t \ge t_0] \end{aligned} \]
  1. Считать все параметры через io.param.
  2. mode_id = MODES.get(io.text("mode", ""), int(io.param("mode_id", 2))).
  3. В зависимости от mode_id вычислить value.
  4. io.write("value", value).
Партнёрский резистор (тест)
partner_test_resistor
Логический
логическийpartnerpartner/partner_test_resistor/

Партнёрская модель резистора с ограничением тока. Используется как пример подключения «внешнего» компонента и как демонстрация clamp-логики через параметры (без io.state — лимит на ток задаётся входным параметром I_max). В реальной модели – линейный резистор с «идеальным» токовым ограничителем, что позволяет безопасно коммутировать напряжение на нагрузке. Без состояния.

SPEC.folderpartner/partner_test_resistor/
Папкаpartner/partner_test_resistor/
Типлогический
Уровеньlogic

Связи компонента (порты)

IoutrightТок, А
Не применимо.

Параметры и состояние

Входные параметры

I_max$10.0$АnumberМаксимально допустимый ток

Состояния и интегрирование

Состояние отсутствует.

Математическая модель

Не применимо для логических/мета компонентов.

Алгоритм работы

\[ I = \mathrm{clamp}\!\left(\frac{U}{R},\; -I_{\max},\; +I_{\max}\right) \] с защитой от деления на ноль.
  1. U = io.read("U"), R = io.param("R", 1.0), I_max = io.param("I_max", 10.0).
  2. Если $R = 0$: io.write("I", 0.0); выйти.
  3. I = clamp(U / R, -I_max, +I_max); io.write("I", I).
ПИД-регулятор
pid
Логический
логическийregulyatoryregulyatory/pid/

Дискретный ПИД-регулятор с анти-windup (clamp интеграла при насыщении выхода). На вход — измеренная величина (например, температура продукта с датчика), на выходе — управляющий сигнал $u \in [u_{\min}, u_{\max}]$, который мост L $\to$ C (терминал term) подаёт на управляющий вход физблока (обороты вентиляторов, открытие клапана). Применяется как стандартный регулятор в замкнутых контурах (например, контур стабилизации температуры АВО). Состояние: $I$ (накопленный интеграл), $e_{\text{prev}}$ (предыдущая ошибка). Позиционная форма.

SPEC.folderregulyatory/pid/
Папкаregulyatory/pid/
Типлогический
Уровеньlogic

Связи компонента (порты)

uoutrightУправляющий сигнал $u \in [u_{\min}, u_{\max}]$
Не применимо.

Параметры и состояние

Входные параметры

Kp$0.5$—numberП-коэффициент
Ki$0.05$—numberИ-коэффициент
Kd$0.0$—numberД-коэффициент
u_min$0.0$—numberМинимум выхода
u_max$1.0$—numberМаксимум выхода
dt$1.0$сnumberШаг интегрирования
reverseTrue—flagreverse=True: ошибка $e = T_{\text{meas}} - T_{\text{set}}$ (охлаждение)

Состояния и интегрирование

Внутренние состояния (io.state):
  • I — накопленный интеграл ошибки;
  • e_prev — предыдущая ошибка (для дифференциальной части).

Математическая модель

Не применимо для логических/мета компонентов.

Алгоритм работы

Позиционная форма (с анти-windup): \[ \begin{aligned} e &= T_{\text{meas}} - T_{\text{set}} \quad \text{(если reverse)} \\ I &= I_{\text{prev}} + e \cdot dt \\ D &= (e - e_{\text{prev}}) / dt \\ u_{\text{raw}} &= K_p e + K_i I + K_d D \\ u_{\text{clamped}} &= \mathrm{clamp}(u_{\text{raw}},\, u_{\min},\, u_{\max}) \\ I &= \begin{cases} I, & u_{\text{clamped}} = u_{\text{raw}} \\ I_{\text{prev}}, & \text{иначе} \quad \text{(анти-windup)} \end{cases} \\ e_{\text{prev}} &= e \\ u &= u_{\text{clamped}} \end{aligned} \]
  1. Считать sp, Kp, Ki, Kd, u_min, u_max, dt, reverse через io.param / io.flag.
  2. meas = io.read("T_meas"); e = (meas - sp) if reverse else (sp - meas).
  3. I = io.state.get("I", 0.0), e_prev = io.state.get("e_prev", e).
  4. I = I + e*dt, D = (e - e_prev)/dt.
  5. u = Kp*e + Ki*I + Kd*D, u = clamp(u, u_min, u_max).
  6. Анти-windup: I = I if u не насыщен else I_prev.
  7. Сохранить io.state["I"] = I, io.state["e_prev"] = e; io.write("u", u).
Терминал
terminal
Логический
логическийterminalyterminaly/term/

Кросс-слойный терминал для «прыжка» сигнала между слоями (L-уровень) без рисования длинного провода через всю схему. Поведение — аффинный проводник: $y = x \cdot \mathrm{scale} + \mathrm{offset}$ (для приёмника с пересчётом 0..1 $\to$ физическая величина). Пара терминалов с общим tag образует одну «логическую связь»; реальная маршрутизация по тегу — забота диспетчера/редактора (модель Simulink Goto/From). Визуально рисуется на каждом слое нативно (провод, входящий в goto / исходящий из from).

SPEC.folderterminaly/term/
Папкаterminaly/term/
Типлогический
Уровеньlogic

Связи компонента (порты)

youtrightВыходное значение
Не применимо.
  • Тег (одинаковый у пары goto $\leftrightarrow$ from): задаётся в редакторе как атрибут терминала, не параметр компонента.
  • Режим (__PX0__): goto (источник) или from (приёмник).

Параметры и состояние

Входные параметры

offset$0.0$—numberСмещение

Состояния и интегрирование

Состояние отсутствует.

Математическая модель

Не применимо для логических/мета компонентов.

Алгоритм работы

\[ y = \mathrm{scale} \cdot x + \mathrm{offset} \]
  1. scale = io.param("scale", 1.0), offset = io.param("offset", 0.0).
  2. io.write("y", io.read("x") * scale + offset).
Маршрутизация по тегу и проверка наличия пары выполняются вне компонента (в диспетчере).
Термосопротивление
thermoresistor
Логический
логическийdatchikidatchiki/termosoprotivlenie/

Линейное преобразование температуры (°C) в сопротивление (Ом): $R = \mathrm{gain} \cdot T + \mathrm{offset}$. По умолчанию коэффициенты подобраны для платинового ТС вблизи 0 °C. Простой безынерционный преобразователь; для учёта инерции используйте sensor_lag. Файл помечен «Сгенерировано ORPHYS Component Generator»; правки делаются через редактор компонентов, не вручную.

SPEC.folderdatchiki/termosoprotivlenie/
Папкаdatchiki/termosoprotivlenie/
Типлогический
Уровеньlogic

Связи компонента (порты)

R_OhmoutrightСопротивление, Ом
Не применимо.

Параметры и состояние

Входные параметры

offset$100.0$ОмnumberСмещение характеристики

Состояния и интегрирование

Состояние отсутствует.

Математическая модель

Не применимо для логических/мета компонентов.

Алгоритм работы

\[ R = \mathrm{gain} \cdot T + \mathrm{offset} \]
  1. x = io.read("T_in").
  2. gain = io.param("gain", 0.385), offset = io.param("offset", 100.0).
  3. io.write("R_Ohm", x * gain + offset).
Шаговый двигатель (униполярный)
unipolar_stepper
Логический
логическийdvigatelidvigateli/shagovyy_unipolar/

Кинематическая модель шагового двигателя (NEMA 17, 200 шагов на оборот, 1.8°/шаг) с поддержкой режимов full и half step. На каждый входной импульс step вал поворачивается на $\theta_{\text{step}} = 2\pi / (\mathrm{steps\_per\_rev} \cdot k)$, где $k = 1$ (full) или $k = 2$ (half). Направление задаётся параметром dir. Состояние: $\theta$ (накапливается через io.integrate).

SPEC.folderdvigateli/shagovyy_unipolar/
Папкаdvigateli/shagovyy_unipolar/
Типлогический
Уровеньlogic

Связи компонента (порты)

thetaoutrightТекущий угол вала, рад
Не применимо.

Параметры и состояние

Входные параметры

steps_per_rev$200.0$—numberЧисло шагов на оборот (full)
step_mode«full»—textРежим: full / half
theta0$0.0$радnumberНачальный угол

Состояния и интегрирование

Внутреннее состояние — io.state["theta"]: накопленный угол (обновляется через io.integrate).

Математическая модель

Не применимо для логических/мета компонентов.

Алгоритм работы

\[ \begin{aligned} \theta_{\text{step}} &= \frac{2\pi}{\mathrm{steps\_per\_rev} \cdot k}, \quad k = 1\ \text{или}\ 2 \\ \Delta\theta &= \mathrm{step} \cdot \theta_{\text{step}} \cdot (1\ \text{при}\ \mathrm{dir}\ge 0.5,\ -1\ \text{иначе}) \\ \theta_{\text{new}} &= \theta + \Delta\theta \\ \omega &= \Delta\theta / dt \end{aligned} \]
  1. step = io.read("step"), dir, spr, mode, theta0.
  2. divisor = 2 if mode == "half" else 1; step_angle = 2π / (spr * divisor).
  3. delta = step * step_angle * (1 if dir >= 0.5 else -1).
  4. new_theta = io.integrate("theta", delta / io.dt, x0=theta0).
  5. io.state["omega"] = delta / io.dt; io.write("theta", new_theta).
Энкодер инкрементальный
encoder
Логический
логическийdvigatelidvigateli/encoder/

Преобразование абсолютного угла вала $\theta$ (рад) в накопленный счётчик импульсов и текущую частоту вращения (об/мин). Используется как датчик обратной связи в контурах управления двигателями. На каждом тике вычисляется приращение угла, по нему — число импульсов (с округлением); счётчик накапливается. Состояние: angle_prev, count.

SPEC.folderdvigateli/encoder/
Папкаdvigateli/encoder/
Типлогический
Уровеньlogic

Связи компонента (порты)

countoutrightНакопленный счётчик импульсов
Не применимо.

Параметры и состояние

Состояния и интегрирование

См. выше.

Математическая модель

Не применимо для логических/мета компонентов.

Алгоритм работы

\[ \begin{aligned} \Delta\theta &= \theta - \theta_{\text{prev}}, \\ \Delta \mathrm{count} &= \mathrm{round}\!\left(\frac{\Delta\theta \cdot \mathrm{ppr}}{2\pi}\right), \\ \mathrm{count} &\mathrel{+}= \Delta \mathrm{count}, \\ \mathrm{rpm} &= \frac{\Delta\theta / dt}{2\pi} \cdot 60. \end{aligned} \]
  1. angle = io.read("angle"), ppr = io.param("ppr", 1000), dt = io.dt или 1e-3.
  2. Восстановить angle_prev, count из io.state.
  3. Посчитать $\Delta\theta$, $\Delta \mathrm{count}$, rpm.
  4. Сохранить angle_prev = angle, count += Delta, rpm.
  5. io.write("count", count), io.write("rpm", rpm).
Two-unit duty/standby controller
two_unit_duty_standby
Логический
логическийControllerscontrollers/two_unit_standby/

Детерминированный автомат A/B duty/standby для двух взаимозаменяемых агрегатов (например, два насоса, два компрессора, две линии резервирования). Состояния: STOPPED $\to$ STARTING_A/B $\to$ RUNNING_A/B $\to$ (TRANSFER/overlap) $\to$ STARTING_B/A. Поддерживает предпочтение (duty_select), таймаут пуска (start_timeout_s), задержку передачи (transfer_delay_s) и допустимое перекрытие (overlap_s). Входы: статусы available, trip, running для A и B; выходы: команды пуска a_run_cmd, b_run_cmd, флаги transfer_active, no_unit_available. Состояние автомата и таймер — в io.state (state, timer_s). Используется как типовой A/B-контроллер резервирования в непрерывных процессах.

SPEC.folderControllers
Папкаcontrollers/two_unit_standby/
Типлогический
Уровеньlogic
Доменlogic
symA/B
bodyDUTY/STBY

Связи компонента (порты)

b_availableinleftДоступность агрегата B
a_tripinleftАвария агрегата A
b_tripinleftАвария агрегата B
a_runninginleftФакт пуска A (от привода)
b_runninginleftФакт пуска B (от привода)
a_run_cmdoutrightКоманда пуска A
b_run_cmdoutrightКоманда пуска B
transfer_activeoutrightИдёт передача (1)
no_unit_availableoutrightНи один не доступен (1)
Не применимо.

Параметры и состояние

Входные параметры

duty_select«A»—comboПредпочтение: A / B
start_timeout_s$10.0$сnumberТаймаут пуска
transfer_delay_s$0.0$сnumberЗадержка передачи (0 = мгновенно)
overlap_s$0.0$сnumberДопустимое перекрытие (взаимный пуск)

Состояния и интегрирование

Внутреннее состояние: state, timer_s. Метод интегрирования не используется (логика).

Математическая модель

Не применимо для логических/мета компонентов.

Алгоритм работы

  1. Если enable = False: state = STOPPED; a_run_cmd = b_run_cmd = 0; transfer_active = no_unit_available = 0; выйти.
  2. Определить preferred = duty_select (значение A/B; по умолчанию A).
  3. dt = io.dt; timeout = start_timeout_s; transfer_delay = transfer_delay_s; overlap = overlap_s.
  4. Конечный автомат с переходами (см. файл). В каждом переходе обновляются a_run_cmd, b_run_cmd, transfer_active, no_unit_available.
  5. Таймер timer_s тикает на dt только в STARTING_... и TRANSFER_... / OVERLAP_....
  6. При недостижимом/повреждённом state — детерминированный recovery в STOPPED.
Оптимизатор
optimizer
Логический
метаАнализ и оптимизацияanaliz/optimizer/

Мета-блок для визуального представления оптимизатора параметров на схеме. Хранит конфигурацию оптимизации (имена целевых параметров, начальные значения, пределы, шаги, критерии, метод, режим) в своих параметрах. НЕ исполняется тик-за-тиком: шаг-заглушка просто отдаёт на выход текущее значение вектора параметров params (dict по именам из target_params) — для осциллограммы. Реальная оптимизация запускается отдельно через OptimizerService.Optimize по сокету (фаза C).

SPEC.folderАнализ и оптимизация
Папкаanaliz/optimizer/
Типмета
Уровеньlogic
symOPT
bodyOPT

Связи компонента (порты)

measinleftОтклик $y(t)$ (для визуализации)
paramsoutrightВектор параметров (после оптимизации)
Не применимо.

Параметры и состояние

Входные параметры

target_params«Kp,Ki,Kd»—textИмена оптимизируемых параметров
p_init«0.6,0.08,0.0»—textНачальные значения
p_min«0,0,0»—textНижние пределы
p_max«5,2,1»—textВерхние пределы
p_step«0.1,0.02,0.01»—textШаги дискретизации
criteria«IAE,overshoot,settling,steady»—textСписок критериев
c_weights«1.0,0.5,0.3,0.5»—textВеса критериев
c_limits«,0.1,30,0.5»—textПределы критериев
combine«quadratic»—comboСпособ свёртки: additive/quadratic/minimax/multiplicative
method«nelder_mead»—comboМетод: nelder_mead/powell/coord_descent/steepest/conjugate_grad
mode«full»—comboРежим: full / online
max_runs$80$—intМакс. число итераций
tol$10^{-4}$—numberДопуск сходимости
opt_h$0.0$сnumberНачальное время оптимизации
optstep$1.0$сnumberШаг времени оптимизации
signal_label«»—textМетка наблюдаемого сигнала (например, «AVO1»)
signal_field«T_prod_out»—textИмя поля сигнала

Состояния и интегрирование

Состояние отсутствует.

Математическая модель

Не применимо для логических/мета компонентов.

Алгоритм работы

  1. Разобрать target_params по запятой (имена параметров).
  2. Считать p_init как массив чисел.
  3. Собрать vec = \{name: p_init[i]\}.
  4. io.write("params", vec) — значения текущей рабочей точки, без модификации.
Реальная оптимизация (поиск оптимальных значений) запускается отдельно через сервис OptimizerService.Optimize / online_optimizer; найденные значения фронт записывает в p_init, и они становятся видны на выходе params.
Датчик с инерцией
sensor_lag
Логический
логическийИзмерителиdatchiki/sensor_lag/

Модель реального датчика: «истинная» измеряемая величина $T_{\text{in}}$ сначала проходит через инерционный фильтр первого порядка (постоянная $\tau$), затем — линейная калибровка в выходную физическую величину (например, в Ом). Используется для моделирования термопар и термосопротивлений в сборках: отдельно — термодинамика объекта, отдельно — инерция датчика. Демонстрирует паттерн «смешанного» компонента: один дифур + одна алгебра в одном шаге.

SPEC.folderИзмерители
Папкаdatchiki/sensor_lag/
Типлогический
Уровеньlogic
body$\tau$

Связи компонента (порты)

RoutrightОткалиброванное показание, Ом
Не применимо.

Параметры и состояние

Входные параметры

gain$0.385$Ом/°CnumberКоэффициент калибровки (алгебра)
offset$100.0$ОмnumberСмещение калибровки (алгебра)
T0$20.0$°CnumberНачальная температура датчика
dt$1.0$сnumberШаг интегрирования

Состояния и интегрирование

Внутреннее состояние — io.state["Tf"]: фильтрованная (инерционная) температура датчика.

Математическая модель

Не применимо для логических/мета компонентов.

Алгоритм работы

\[ \begin{aligned} \text{ДИФУР:} \quad & \frac{dT_f}{dt} = \frac{T_{\text{in}} - T_f}{\tau}, \\ \text{АЛГЕБРА:} \quad & R = \mathrm{gain} \cdot T_f + \mathrm{offset}. \end{aligned} \]
  1. T_in = io.read("T_in").
  2. Считать $\tau, \mathrm{gain}, \mathrm{offset}, T_0, dt$ через io.param.
  3. Tf = io.state.get("Tf", T0).
  4. dTf = (T_in - Tf) / tau.
  5. Tf = io.integrate("Tf", dTf, x0=T0).
  6. R = gain * Tf + offset; io.write("R", R).
Апериодическое звено
sau_first_order
Логический
логическийСАУ / Динамические звеньяsau/first_order/

Апериодическое (инерционное) звено первого порядка с передаточной функцией $W(s) = K / (T s + 1)$ — базовое динамическое звено ТАУ, описывающее бесчисленное множество реальных объектов: термопару, RC-фильтр, ёмкость с притоком и стоком, мотор с инерцией. Переходная характеристика — экспонента с постоянной времени $T$, установившееся значение $K \cdot x_{\text{const}}$. При $T = 0$ вырождается в чистый усилитель ($y = K \cdot x$).

SPEC.folderСАУ / Динамические звенья
Папкаsau/first_order/
Типлогический
Уровеньlogic
sym1/(Ts+1)
bodyPT1

Связи компонента (порты)

outoutrightВыходной сигнал $y$
Не применимо.

Параметры и состояние

Входные параметры

T$1.0$сnumberПостоянная времени
initial$0.0$ед.numberНачальное условие $y(0)$
dt$1.0$сnumberШаг интегрирования

Состояния и интегрирование

Внутреннее состояние — io.state["y"].

Математическая модель

Не применимо для логических/мета компонентов.

Алгоритм работы

\[ \begin{aligned} T \dot{y} + y &= K x, \\ W(s) &= \frac{K}{T s + 1} \end{aligned} \] На каждом тике применяется формула неявного Эйлера (безусловно устойчива при $T / dt > 0$): \[ y_{\text{new}} = \frac{K x + (T/dt) y_{\text{old}}}{1 + T/dt} \] При $T = 0$ — вырождение в y = K * x (прямое прохождение).
  1. x = io.read("in").
  2. Считать $K, T, dt, y_0$ через io.param.
  3. Получить $y = $ io.state.get("y", y0).
  4. Если T > 0 и dt > 0: применить формулу (\ref{eq:sau-first-order}). Иначе: y = K * x.
  5. io.state["y"] = y; io.write("out", y).
Дифференциатор
sau_derivative
Логический
логическийСАУ / Динамические звеньяsau/derivative/

Реальный (физически реализуемый) дифференциатор: $W(s) = s / (\tau s + 1)$. Чистое дифференцирование $W(s) = s$ нереализуемо (требует бесконечной полосы пропускания), поэтому применяется дифференцирование с ФНЧ-фильтром первого порядка с постоянной $\tau$. Используется в ПД-составляющей ПИД-регулятора, для оценки скорости изменения измеряемой величины (дифференцирование показаний датчика). Пара $\tau$ регулирует компромисс между точностью дифференцирования и шумом: меньше $\tau$ — точнее, но шумнее.

SPEC.folderСАУ / Динамические звенья
Папкаsau/derivative/
Типлогический
Уровеньlogic
sym$d/dt$
body$d/dt$

Связи компонента (порты)

outoutrightОценка производной $\dot x(t)$
Не применимо.

Параметры и состояние

Входные параметры

initial$0.0$ед.numberНачальное значение фильтрованной производной
dt$1.0$сnumberШаг интегрирования

Состояния и интегрирование

Внутренние состояния:
  • io.state["x_prev"] — предыдущее значение входа;
  • io.state["y_filt"] — фильтрованная производная.

Математическая модель

Не применимо для логических/мета компонентов.

Алгоритм работы

\[ \begin{aligned} W(s) &= \frac{s}{\tau s + 1}, \\ \tau \dot{y} + y &= \dot{x} \end{aligned} \] Реализация:
  1. x = io.read("in"), tau, dt, initial.
  2. Восстановить x_prev = io.state.get("x_prev", x), y_filt = io.state.get("y_filt", initial).
  3. Если dt <= 0: io.write("out", 0.0); выйти.
  4. Сырая производная: dy_raw = (x - x_prev) / dt.
  5. ФНЧ 1-го порядка (явный Эйлер): alpha = dt / (tau + dt); y_filt = y_filt + alpha * (dy_raw - y_filt).
  6. Сохранить x_prev = x, y_filt; io.write("out", y_filt).
Запаздывание
sau_delay
Логический
логическийСАУ / Динамические звеньяsau/delay/

Звено чистого (транспортного) запаздывания: выход равен входу, взятому с задержкой $\tau$ секунд. Используется для моделирования запаздывающих каналов регулирования (трубопроводы, камеры сгорания, ленточные конвейеры) и при исследовании систем с большим временем чистого запаздывания (критерий устойчивости — запас по фазе «на глаз» падает как $\omega \cdot \tau$). Реализация через кольцевой буфер (collections.deque); состояние инициализируется начальным значением initial.

SPEC.folderСАУ / Динамические звенья
Папкаsau/delay/
Типлогический
Уровеньlogic
sym$e^{-sT
body$\tau$

Связи компонента (порты)

outoutrightЗадержанный сигнал $y(t)=x(t-\tau)$
Не применимо.

Параметры и состояние

Входные параметры

initial$0.0$ед.numberНачальное значение (заполняет буфер при инициализации)
dt$1.0$сnumberШг модельного времени
buffer_size$10000$—intВерхняя граница длины буфера (защита от переполнения памяти)

Состояния и интегрирование

Внутреннее состояние — io.state["buf"]: кольцевой буфер (collections.deque) длины $\max(1,\;n_{\text{delay}})$, где $n_{\text{delay}} = \mathrm{round}(\tau / dt)$.

Математическая модель

Не применимо для логических/мета компонентов.

Алгоритм работы

\[ \begin{aligned} W(s) &= e^{-s\tau}, \\ y(t) &= x(t - \tau) \end{aligned} \]
  1. Считать x = io.read("in"), tau = io.param("delay", 1.0), dt = io.param("dt", 1.0), y0 = io.param("initial", 0.0), maxlen_user = max(int(io.param("buffer_size", 10000)), 1).
  2. Если dt <= 0 или n_delay == 0 — pass-through: io.write("out", x).
  3. Если буфер пуст или его длина не равна $n_{\text{delay}}$ — инициализировать: buf = deque([y0] * n_delay, maxlen=n_delay).
  4. Считать y = buf[0] (самый старый элемент); добавить buf.append(x) (самый старый автоматически вытеснится при переполнении).
  5. io.write("out", y).
Интегратор
sau_integrator
Логический
логическийСАУ / Динамические звеньяsau/integrator/

Идеальный интегратор: выход равен интегралу входа по времени плюс начальное условие. Используется как составная часть ПИ/ПИД-регулятора (интегральная компонента $I$), для получения текущего количества вещества по заданному расходу, выходящего сигнала по скорости изменения и т. п. Внутреннее состояние — единственная переменная $y$ в io.state, обновляется через единый шаг io.integrate среды (явный/неявный метод выбирается средой).

SPEC.folderСАУ / Динамические звенья
Папкаsau/integrator/
Типлогический
Уровеньlogic
sym$\int$
body$\int$

Связи компонента (порты)

outoutrightТекущее значение интеграла $y$
Не применимо.

Параметры и состояние

Входные параметры

dt$1.0$сnumberШаг интегрирования

Состояния и интегрирование

Внутреннее состояние — io.state["y"], обновляется через io.integrate("y", x, x0=initial).

Математическая модель

Не применимо для логических/мета компонентов.

Алгоритм работы

\[ \begin{aligned} \dot{y} &= x, \quad y(0) = y_0, \\ y(t) &= y_0 + \int_0^t x(\tau)\,d\tau \end{aligned} \]
  1. x = io.read("in").
  2. y0 = io.param("initial", 0.0).
  3. y = io.integrate("y", x, x0=y0) — единый шаг интегрирования с ключом "y".
  4. io.write("out", y).
Передаточная функция
sau_transfer_fcn
Логический
логическийСАУ / Динамические звеньяsau/transfer_fcn/

Обобщённое линейное динамическое звено с заданной передаточной функцией $W(s) = N(s)/D(s) = (b_m s^m + \ldots + b_0)/(a_n s^n + \ldots + a_0)$. Используется для представления линеаризованных участков САУ, для импорта коэффициентов, рассчитанных внешними инструментами (Matlab, идентификация), а также как удобный способ собрать сложную динамику из одной передаточной функции. Реализация — каноническая форма наблюдаемых состояний (observer canonical form); шаг по времени — явный Эйлер с заданным dt.

SPEC.folderСАУ / Динамические звенья
Папкаsau/transfer_fcn/
Типлогический
Уровеньlogic
sym$W(s)$
body$W(s)$

Связи компонента (порты)

outoutrightВыход $y$
Не применимо.

Параметры и состояние

Входные параметры

denominator$[1, 1]$—arrayКоэффициенты знаменателя (по убыванию степени $s$, $a_n \ne 0$)
initial$0.0$ед.numberНачальное условие для $y(0)$ (размазывается на $x_0$)
dt$1.0$сnumberШаг интегрирования

Состояния и интегрирование

Внутреннее состояние — io.state["x"], вектор длины $n = \mathrm{deg}(D)$.

Математическая модель

Не применимо для логических/мета компонентов.

Алгоритм работы

Пусть $N(s) = \sum_{i=0}^{m} b_i s^i$, $D(s) = \sum_{i=0}^{n} a_i s^i$ ($a_n \ne 0$). Нормализуем: $a := a / a_n$, $b := b / a_n$ (тогда $a[0] = 1$). Прямое прохождение $D = b[0]$, если $m = n$, иначе $D = 0$. Вектор входа состояний $B_i = b_{i+1} - a_{i+1} \cdot D$, $i = 0, \ldots, n-1$. Каноническая форма наблюдаемых состояний: \[ \begin{aligned} \dot{x}_i &= -a_{i+1} x_0 + \delta_{i+1
  • Считать $u$, $num$, $den$, $dt$ из io.
  • Если знаменатель пуст или $a_0 = 0$ — выдать $y = 0$ (вырождение).
  • Нормализовать коэффициенты по $a_0$, дополнить $b$ нулями слева до длины $n+1$.
  • При $n = 0$ — $y = b[0] \cdot u$ (прямое прохождение).
  • Инициализировать/прочитать $x \in \mathbb{R}^n$ из io.state["x"]; при первом тике $x_0 = initial$.
  • Посчитать все $\dot x_i$ по одному снимку $x$ («синхронный» явный Эйлер).
  • Обновить $x \mathrel{+}= \dot x \cdot dt$.
  • $y = x_0 + D \cdot u$; io.state["x"] = x; io.write("out", y).
  • Дисплей значений
    sau_display
    Логический
    логическийСАУ / Источники и приёмникиsau/display/

    Приёмник, отображающий текущее значение входного сигнала в числовом виде (цифровой индикатор). Выходной порт выполняет pass-through для корректной сортировки графа. Визуализация выполняется на фронтенде через отдельный виджет; сам компонент не хранит историю и не входит в DAE. Применяется для индикации текущего значения регулируемой величины, уставки, ошибки и т. п.

    SPEC.folderСАУ / Источники и приёмники
    Папкаsau/display/
    Типлогический
    Уровеньlogic
    symDisp
    bodyDisp

    Связи компонента (порты)

    outoutrightPass-through ($=x$)
    Не применимо.

    Параметры и состояние

    Входные параметры

    unit«ед.»—stringПодпись единиц измерения

    Состояния и интегрирование

    Состояние отсутствует.

    Математическая модель

    Не применимо для логических/мета компонентов.

    Алгоритм работы

    \[ \mathrm{display} = x \]
    1. io.write("out", io.read("in")).
    Импульсный источник
    sau_pulse
    Логический
    логическийСАУ / Источники и приёмникиsau/pulse/

    Периодическая последовательность прямоугольных импульсов: $y = A$ на интервале длительности $d$ каждого периода $T$, $y = 0$ в остальное время. Используется для снятия импульсных переходных характеристик, для имитации ШИМ-воздействий, для возбуждения импульсных систем автоматического регулирования. Состояние отсутствует; момент начала отсчитывается от модельного времени.

    SPEC.folderСАУ / Источники и приёмники
    Папкаsau/pulse/
    Типлогический
    Уровеньlogic
    sym$\Pi$
    bodyPulse

    Связи компонента (порты)

    Не применимо.

    Параметры и состояние

    Входные параметры

    period$10.0$сnumberПериод $T$
    duty$0.5$—numberСкважность (доля $A$ внутри периода, $0 \le \text{duty} \le 1$)
    phase$0.0$сnumberНачальная фаза (задержка первого фронта)

    Состояния и интегрирование

    Состояние отсутствует.

    Математическая модель

    Не применимо для логических/мета компонентов.

    Алгоритм работы

    \[ y(t) = \begin{cases} A, & ((t - t_{\text{phase}}) \bmod T) < d, \\ 0, & \text{иначе}, \end{cases} \qquad d = \mathrm{duty} \cdot T \]
    1. Считать A, T, duty, phase через io.param.
    2. t = io.ts.
    3. tau = (t - phase) \% T (фаза внутри периода).
    4. y = A, если tau < duty * T, иначе y = 0.
    5. io.write("out", y).
    Источник константы
    sau_const
    Логический
    логическийСАУ / Источники и приёмникиsau/const/

    Источник постоянного во времени сигнала. Используется как уставка, начальное приближение, опорное значение для компараторов и т. п. Не имеет состояния и не зависит от модельного времени.

    SPEC.folderСАУ / Источники и приёмники
    Папкаsau/const/
    Типлогический
    Уровеньlogic
    sym$K$
    body$C$

    Связи компонента (порты)

    Не применимо.

    Параметры и состояние

    Состояния и интегрирование

    Состояние отсутствует.

    Математическая модель

    Не применимо для логических/мета компонентов.

    Алгоритм работы

    \[ y(t) = C = \mathrm{const} \]
    1. C = io.param("value", 1.0).
    2. io.write("out", C).
    Осциллограф
    sau_scope
    Логический
    логическийСАУ / Источники и приёмникиsau/scope/

    Приёмник сигнала с буферизацией: каждый отсчёт входного сигнала добавляется в кольцевой буфер, который затем визуализируется фронтендом в виде графика. Выходной порт выполняет pass-through (равен входу) — это нужно только для корректной топологической сортировки графа сигналов. Не входит в DAE, не влияет на динамику модели.

    SPEC.folderСАУ / Источники и приёмники
    Папкаsau/scope/
    Типлогический
    Уровеньlogic
    symScope
    bodyScope

    Связи компонента (порты)

    outoutrightPass-through ($=x$)
    Не применимо.

    Параметры и состояние

    Входные параметры

    buffer_size$1000$—intМаксимальная длина буфера (точек)

    Состояния и интегрирование

    Внутреннее состояние — io.state["buf"]: список длиной не более buffer_size.

    Математическая модель

    Не применимо для логических/мета компонентов.

    Алгоритм работы

    \[ y_{\text{buf}}[n] = x(t_n) \]
    1. x = io.read("in").
    2. buf = io.state.setdefault("buf", []); buf.append(x).
    3. Если len(buf) > buffer_size — удалить самый старый элемент (buf.pop(0)).
    4. io.state["buf"] = buf; io.write("out", x).
    Синусоидальный источник
    sau_sine
    Логический
    логическийСАУ / Источники и приёмникиsau/sine/

    Гармонический источник: $y(t) = A \sin(2\pi f t) + b$. Используется как периодическое воздействие для снятия амплитудно-фазовых характеристик, для проверки линейности системы, для возбуждения резонансных контуров. Без состояния; модельное время берётся из шины.

    SPEC.folderСАУ / Источники и приёмники
    Папкаsau/sine/
    Типлогический
    Уровеньlogic
    sym$\sin$
    body$\sin$

    Связи компонента (порты)

    Не применимо.

    Параметры и состояние

    Входные параметры

    frequency$0.1$ГцnumberЧастота $f$
    bias$0.0$ед.numberСмещение $b$

    Состояния и интегрирование

    Состояние отсутствует.

    Математическая модель

    Не применимо для логических/мета компонентов.

    Алгоритм работы

    \[ y(t) = A \sin(2\pi f t) + b \]
    1. Считать A = io.param("amplitude", 1.0), f = io.param("frequency", 0.1), b = io.param("bias", 0.0).
    2. t = io.ts.
    3. io.write("out", A * math.sin(2 * math.pi * f * t) + b).
    Ступенчатое воздействие
    sau_step
    Логический
    логическийСАУ / Источники и приёмникиsau/step/

    Источник тестового сигнала в форме ступеньки: до момента $t_{\mathrm{step}}$ выход равен начальному уровню $y_0$, начиная с $t_{\mathrm{step}}$ выход переключается на $y_1$ и далее не меняется. Используется как стандартное воздействие при исследовании переходных процессов в САУ: снятие кривой разгона, оценка времени регулирования, перерегулирования и т. п. Без внутреннего состояния — на каждом тике значение вычисляется заново по текущему модельному времени.

    SPEC.folderСАУ / Источники и приёмники
    Папкаsau/step/
    Типлогический
    Уровеньlogic
    symStep
    bodyStep

    Связи компонента (порты)

    Не применимо — компонент логический, acausal-портов и узловых переменных не имеет. Не используется.

    Параметры и состояние

    Входные параметры

    final$1.0$ед.numberКонечный уровень $y_1$ (при $t\ge t_{\mathrm{step}}$)
    step_time$1.0$сnumberМомент переключения $t_{\mathrm{step}}$

    Состояния и интегрирование

    Внутреннее состояние отсутствует: компонент не хранит историю входов и не интегрирует значения. Модельное время берётся напрямую из шины (io.ts).

    Математическая модель

    Не применимо для логических/мета компонентов.

    Алгоритм работы

    На каждом тике вычисляется выход по правилу кусочно-постоянной функции модельного времени: \[ y(t) = \begin{cases} y_0, & t < t_{\mathrm{step}}, \\ y_1, & t \ge t_{\mathrm{step}}. \end{cases} \] Реализация (sau/step/sau_step.py):
    1. Считать текущее модельное время t = io.ts.
    2. Считать параметры t_step, y_0, y_1 через io.param(..., default).
    3. Записать в выходной порт io.write("out", y1 if t >= t_step else y0).
    <<Компаратор (>)>>
    sau_gt
    Логический
    логическийСАУ / Мат. операцииsau/gt/

    Строгий компаратор: $y = 1$, если $x_1 > x_2$, иначе $y = 0$. Используется для построения релейной логики, сигнализации превышения порога, разрешающих сигналов. Без состояния.

    SPEC.folderСАУ / Мат. операции
    Папкаsau/gt/
    Типлогический
    Уровеньlogic
    sym>
    body>

    Связи компонента (порты)

    in2inleftОпорное значение $x_2$
    outoutrightРезультат $y \in \{0, 1\}$
    Не применимо.

    Параметры и состояние

    Входные параметры

    Не имеет параметров (только входы).

    Состояния и интегрирование

    Состояние отсутствует.

    Математическая модель

    Не применимо для логических/мета компонентов.

    Алгоритм работы

    \[ y = [\,x_1 > x_2\,] \]
    1. io.write("out", 1.0 if io.read("in1") > io.read("in2") else 0.0).
    Компаратор (<)
    sau_lt
    Логический
    логическийСАУ / Мат. операцииsau/lt/

    Строгий компаратор: $y = 1$, если $x_1 < x_2$, иначе $y = 0$. Используется для построения релейной логики, сигнализации «ниже нормы» и т. п. Без состояния.

    SPEC.folderСАУ / Мат. операции
    Папкаsau/lt/
    Типлогический
    Уровеньlogic
    sym<
    body<

    Связи компонента (порты)

    in2inleftОпорное значение $x_2$
    outoutrightРезультат $y \in \{0, 1\}$
    Не применимо.

    Параметры и состояние

    Входные параметры

    Не имеет параметров.

    Состояния и интегрирование

    Состояние отсутствует.

    Математическая модель

    Не применимо для логических/мета компонентов.

    Алгоритм работы

    \[ y = [\,x_1 < x_2\,] \]
    1. io.write("out", 1.0 if io.read("in1") < io.read("in2") else 0.0).
    Компаратор (==)
    sau_eq
    Логический
    логическийСАУ / Мат. операцииsau/eq/

    Компаратор: $y = 1$, если $|x_1 - x_2| \le \varepsilon$, иначе $y = 0$. Используется для контроля попадания в допуск («сигнализация отклонения»), для построения логики разрешения при совпадении уставки и текущего значения. Без состояния.

    SPEC.folderСАУ / Мат. операции
    Папкаsau/eq/
    Типлогический
    Уровеньlogic
    sym==
    body==

    Связи компонента (порты)

    in2inleftВторое сравниваемое значение $x_2$
    outoutrightРезультат $y \in \{0, 1\}$
    Не применимо.

    Параметры и состояние

    Состояния и интегрирование

    Состояние отсутствует.

    Математическая модель

    Не применимо для логических/мета компонентов.

    Алгоритм работы

    \[ y = \begin{cases} 1, & |x_1 - x_2| \le \varepsilon, \\ 0, & |x_1 - x_2| > \varepsilon. \end{cases} \]
    1. eps = io.param("tolerance", 1e-6).
    2. io.write("out", 1.0 if abs(io.read("in1") - io.read("in2")) <= eps else 0.0).
    Логическое И
    sau_and
    Логический
    логическийСАУ / Мат. операцииsau/and/

    Конъюнкция двух бинарных сигналов: $y = 1$, если ОБА входа превышают порог threshold, иначе $y = 0$. Используется для построения релейно-логических схем, схем разрешения и блокировок. Без состояния.

    SPEC.folderСАУ / Мат. операции
    Папкаsau/and/
    Типлогический
    Уровеньlogic
    symAND
    bodyAND

    Связи компонента (порты)

    in2inleftВторой операнд $x_2$
    outoutrightРезультат $y \in \{0, 1\}$
    Не применимо.

    Параметры и состояние

    Состояния и интегрирование

    Состояние отсутствует.

    Математическая модель

    Не применимо для логических/мета компонентов.

    Алгоритм работы

    \[ y = [\,x_1 > \mathrm{threshold}\,] \wedge [\,x_2 > \mathrm{threshold}\,] \]
    1. thr = io.param("threshold", 0.5).
    2. b1 = io.read("in1") > thr, b2 = io.read("in2") > thr.
    3. io.write("out", 1.0 if (b1 and b2) else 0.0).
    Логическое ИЛИ
    sau_or
    Логический
    логическийСАУ / Мат. операцииsau/or/

    Дизъюнкция двух бинарных сигналов: $y = 1$, если хотя бы один вход превышает порог threshold, иначе $y = 0$. Используется в релейно-логических схемах, схемах разрешения, сигнализации. Без состояния.

    SPEC.folderСАУ / Мат. операции
    Папкаsau/or/
    Типлогический
    Уровеньlogic
    symOR
    bodyOR

    Связи компонента (порты)

    in2inleftВторой операнд $x_2$
    outoutrightРезультат $y \in \{0, 1\}$
    Не применимо.

    Параметры и состояние

    Состояния и интегрирование

    Состояние отсутствует.

    Математическая модель

    Не применимо для логических/мета компонентов.

    Алгоритм работы

    \[ y = [\,x_1 > \mathrm{threshold}\,] \vee [\,x_2 > \mathrm{threshold}\,] \]
    1. thr = io.param("threshold", 0.5).
    2. b1 = io.read("in1") > thr, b2 = io.read("in2") > thr.
    3. io.write("out", 1.0 if (b1 or b2) else 0.0).
    Логическое НЕ
    sau_not
    Логический
    логическийСАУ / Мат. операцииsau/not/

    Инверсия бинарного сигнала: $y = 1$, если вход НЕ превышает порог threshold, иначе $y = 0$. Без состояния.

    SPEC.folderСАУ / Мат. операции
    Папкаsau/not/
    Типлогический
    Уровеньlogic
    symNOT
    bodyNOT

    Связи компонента (порты)

    outoutrightИнвертированный сигнал $y$
    Не применимо.

    Параметры и состояние

    Состояния и интегрирование

    Состояние отсутствует.

    Математическая модель

    Не применимо для логических/мета компонентов.

    Алгоритм работы

    \[ y = \neg [\,x > \mathrm{threshold}\,] \]
    1. thr = io.param("threshold", 0.5).
    2. x = io.read("in").
    3. io.write("out", 0.0 if x > thr else 1.0).
    Ограничитель
    sau_saturation
    Логический
    логическийСАУ / Мат. операцииsau/saturation/

    Кусочно-линейная нелинейность типа «насыщение»: выход $y = \mathrm{clamp}(x, y_{\min}, y_{\max})$. Применяется для ограничения управляющего сигнала (выход ПИД-регулятора — в диапазон $0 \ldots 1$, открытие клапана — $0 \ldots 1$, и т. п.), для моделирования физических ограничений приводов. Без состояния; ставится на логическом слое после звена, дающего неограниченный сигнал.

    SPEC.folderСАУ / Мат. операции
    Папкаsau/saturation/
    Типлогический
    Уровеньlogic
    symSat
    bodySat

    Связи компонента (порты)

    outoutrightОграниченный сигнал $y$
    Не применимо.

    Параметры и состояние

    Входные параметры

    upper$\phantom{-}1.0$ед.numberВерхний предел $y_{\max}$

    Состояния и интегрирование

    Состояние отсутствует.

    Математическая модель

    Не применимо для логических/мета компонентов.

    Алгоритм работы

    \[ y = \mathrm{clamp}(x, y_{\min}, y_{\max}) = \begin{cases} y_{\min}, & x \le y_{\min}, \\ y_{\max}, & x \ge y_{\max}, \\ x, & \text{иначе}. \end{cases} \]
    1. x = io.read("in"), lo = io.param("lower", -1.0), hi = io.param("upper", 1.0).
    2. io.write("out", min(hi, max(lo, x))).
    Сумматор
    sau_sum
    Логический
    логическийСАУ / Мат. операцииsau/sum/

    Сумматор с настраиваемыми знаками слагаемых. По умолчанию сложение двух входов ($y = x_1 + x_2$), но строкой signs можно задать любую комбинацию знаков ("++", "+−", "−+", "−−"), то есть получить вычитание. Используется как типовой узел САУ для вычисления ошибки регулирования ($\varepsilon = x - x_{\text{set}}$) и алгебраической комбинации нескольких сигналов в один. Без внутреннего состояния, чистая алгебра на каждом тике.

    SPEC.folderСАУ / Мат. операции
    Папкаsau/sum/
    Типлогический
    Уровеньlogic
    sym$\Sigma$
    body$\Sigma$

    Связи компонента (порты)

    in2inleftВторое слагаемое $x_2$
    outoutrightСумма $y$
    Не применимо.

    Параметры и состояние

    Состояния и интегрирование

    Внутреннее состояние отсутствует.

    Математическая модель

    Не применимо для логических/мета компонентов.

    Алгоритм работы

    \[ y = s_1 \cdot x_1 + s_2 \cdot x_2, \quad s_i \in \{+1, -1\} \]
    1. Считать x1 = io.read("in1"), x2 = io.read("in2").
    2. Считать signs = io.text("signs", "++"), нормализовать Unicode-минус − в ASCII -.
    3. Пройти по символам signs: если "-" — вычесть соответствующий вход, иначе — прибавить.
    4. io.write("out", result).
    Умножитель
    sau_product
    Логический
    логическийСАУ / Мат. операцииsau/product/

    Поэлементное произведение двух входных сигналов: $y = x_1 \cdot x_2$. Используется для моделирования нелинейных звеньев, перемножения управляющих воздействий (например, для «холодного»/«горячего» регулирования), расчёта мощности ($P = U \cdot I$). Без состояния.

    SPEC.folderСАУ / Мат. операции
    Папкаsau/product/
    Типлогический
    Уровеньlogic
    sym$\times$
    body$\times$

    Связи компонента (порты)

    in2inleftВторой множитель $x_2$
    outoutrightПроизведение $y$
    Не применимо.

    Параметры и состояние

    Входные параметры

    Не имеет параметров (только входы).

    Состояния и интегрирование

    Состояние отсутствует.

    Математическая модель

    Не применимо для логических/мета компонентов.

    Алгоритм работы

    \[ y = x_1 \cdot x_2 \]
    1. x1 = io.read("in1"), x2 = io.read("in2").
    2. io.write("out", x1 * x2).
    Усилитель
    sau_gain
    Логический
    логическийСАУ / Мат. операцииsau/gain/

    Линейный усилитель (безынерционное пропорциональное звено): выход равен входу, умноженному на коэффициент $K$. Является самым употребительным блоком САУ — используется как П-регулятор в учебных задачах ТАУ, для масштабирования сигналов между подсистемами (например, для согласования диапазонов 0..1 и физических единиц), а также как тестовое звено при оценке статической ошибки. Без внутреннего состояния, чистая алгебра.

    SPEC.folderСАУ / Мат. операции
    Папкаsau/gain/
    Типлогический
    Уровеньlogic
    symK
    bodyK

    Связи компонента (порты)

    outoutrightВыходной сигнал $y$
    Не применимо.

    Параметры и состояние

    Состояния и интегрирование

    Внутреннее состояние отсутствует.

    Математическая модель

    Не применимо для логических/мета компонентов.

    Алгоритм работы

    \[ y = K \cdot x \]
    1. x = io.read("in").
    2. K = io.param("K", 1.0).
    3. io.write("out", K * x).
    Генератор шума
    sau_noise
    Логический
    логическийСАУ/Источники и приёмникиsau/noise/

    Источник нормально распределённого случайного сигнала: $y \sim \mathcal{N}(\mu, \sigma^2)$. Используется для тестирования устойчивости контуров регулирования и фильтров к шуму измерений, для имитации случайных возмущений, для статистического анализа (многократные прогоны с разными seed). Состояние: random.Random создаётся при первом тике и сохраняется в io.state["rng"] (это даёт детерминированный поток случайных чисел на всём прогоне; seed можно поменять через параметр seed).

    SPEC.folderСАУ/Источники и приёмники
    Папкаsau/noise/
    Типлогический
    Уровеньlogic
    symnoise
    bodynoise

    Связи компонента (порты)

    Не применимо.

    Параметры и состояние

    Входные параметры

    std$1.0$ед.numberСтандартное отклонение $\sigma$
    seed$42$—intSeed инициализации ГСЧ

    Состояния и интегрирование

    Внутреннее состояние: rng (ГСЧ). Метод интегрирования не используется (генератор не интегрируется, он выдаёт значение).

    Математическая модель

    Не применимо для логических/мета компонентов.

    Алгоритм работы

    1. rng = io.state.get("rng").
    2. Если rng is None: rng = random.Random(int(io.param("seed", 42))); io.state["rng"] = rng.
    3. $\mu$ = io.param("mean", 0.0); $\sigma$ = io.param("std", 1.0).
    4. io.write("out", rng.gauss($\mu$, $\sigma$)).
    Контроллер СГМС
    sgms_controller
    Логический
    логическийСтенды/СГМСstands/sgms_controller/

    Блок «Контроллер СГМС» — host / Renode / real (этап 14, каркас). Физика объекта живёт в схеме ORPHYS; этот блок задаёт mode исполнения (host, renode, real) и TCP-параметры подключения. Порты данных (CH1, CH2, KEY/GPIO, GEN, ADC/AIN, PWM и др.) подтягиваются при выборе блока / смене project / кнопке «Обновить порты» через derivePorts(project.io) / SgmsIoMap .toBlockNodes (на стороне Electron/renderer). В SPEC указаны только диагностические выходы; IO-пины добавляются динамически. Используется как интеграционная точка для СГМС-проектов (лабораторные работы с эмуляцией Renode или реальной платой STM32).

    SPEC.folderСтенды/СГМС
    Папкаstands/sgms_controller/
    Типлогический
    Уровеньlogic
    symeSGMS
    bodySGMS\nctl

    Связи компонента (порты)

    diag_cycleoutrightДиагностика: цикл выполнения, мкс
    diag_overdueoutrightДиагностика: флаг «overdue» (1/0)
    В SPEC перечислены только эти три порта — диагностические. Данные-порты (CH1, CH2, KEY, GPIO, GEN, ADC, AIN, PWM и др.) добавляются динамически через derivePorts(project.io). Не применимо.

    Параметры и состояние

    Входные параметры

    mode«renode»—comboРежим исполнения: host, renode, real
    host«127.0.0.1»—stringХост TCP-сервера ORPHYS
    port$1111$—numberПорт TCP
    profile«stm32-lab1»—stringПрофиль платы
    elf«»—stringПуть к .elf (renode)
    autoDeployTrue—boolАвтодеплой
    board«stm32f769-lab»—stringЦелевая плата

    Состояния и интегрирование

    Внутреннее состояние: connected, cycle_us, overdue, и др. (поднимается в Electron). Метод интегрирования не используется.

    Математическая модель

    Не применимо для логических/мета компонентов.

    Алгоритм работы

    1. io.write("status", float(io.state.get( "connected", 0.0))).
    2. io.write("diag_cycle", float(io.state.get( "cycle_us", 0.0))).
    3. io.write("diag_overdue", float(io.state.get( "overdue", 0.0))).
    Дополнительные динамические порты (KEY/GPIO, GEN, ADC/AIN, PWM) добавляются в реестре derivePorts при выборе project / смене / нажатии «Обновить порты» в UI.
    COM-источник
    com_source
    Логический
    логическийСтенды/Транспортstands/com_source/

    COM-фасад над serial-мостом (Electron IPC window.marsIO). На тике шины публикует последние принятые сигналы CH1/CH2 (если инжектированы через state / внешний runtime) и читает входы k1–k4 для кодирования KEY-команды (4-битный код). Реальный open/close COM и сериал-обмен выполняются в renderer через window.marsIO (отдельный процесс). Используется как интерфейс между схемой ORPHYS и физическим устройством по сериал-порту (COM).

    SPEC.folderСтенды/Транспорт
    Папкаstands/com_source/
    Типлогический
    Уровеньlogic
    symCOM
    bodyCOM

    Связи компонента (порты)

    k2inleftКнопка 2 (бит 2 KEY-команды)
    k3inleftКнопка 3 (бит 1 KEY-команды)
    k4inleftКнопка 4 (бит 0 KEY-команды)
    CH1outrightАналоговый канал 1 (от устройства)
    CH2outrightАналоговый канал 2 (от устройства)
    cmdoutrightKEY-команда (4-битный код: k1$\cdot$8 + k2$\cdot$4 + k3$\cdot$2 + k4)
    Не применимо.

    Параметры и состояние

    Входные параметры

    baudRate$115200$—numberBaud rate
    profile«stm32-lab1»—comboПрофиль кадра: stm32-lab1, arduino-demo, mars-bus
    autoStartTrue—boolАвтозапуск при инициализации

    Состояния и интегрирование

    Внутреннее состояние: io.state["ch1_last"], io.state["ch2_last"], io.state["keys"]. Метод интегрирования не используется (компонент транслирует state в выходы и входы в код).

    Математическая модель

    Не применимо для логических/мета компонентов.

    Алгоритм работы

    1. ch1 = io.state.get("ch1_last", 0.0); ch2 = io.state.get("ch2_last", 0.0).
    2. io.write("CH1", float(ch1)); io.write("CH2", float(ch2)).
    3. Считать k1, k2, k3, k4 из входов (с бинаризацией по 0.5).
    4. io.state["keys"] = [k1, k2, k3, k4].
    5. io.write("cmd", k1*8 + k2*4 + k3*2 + k4).
    TCP-подключение к серверу
    tcp_stand_client
    Логический
    логическийСтенды/Транспортstands/tcp_stand_client/

    TCP-клиент для подключения схемы ORPHYS к серверу другого инстанса MARS. Принимает 4 кнопки (k1–k4) и отдаёт 2 аналоговых канала (CH1, CH2). Логика обмена и формирования кадра — в Electron main (tcp-stand-client). Используется для двухмашинных стендов: один ORPHYS эмулирует оборудование, другой — управляет им через TCP.

    SPEC.folderСтенды/Транспорт
    Папкаstands/tcp_stand_client/
    Типлогический
    Уровеньlogic
    symTCPc
    bodyTCP\nCLI

    Связи компонента (порты)

    k2inleftКнопка 2 (бит 2 KEY)
    k3inleftКнопка 3 (бит 1 KEY)
    k4inleftКнопка 4 (бит 0 KEY)
    CH1outrightАналоговый канал 1 (от сервера)
    CH2outrightАналоговый канал 2 (от сервера)
    Не применимо.

    Параметры и состояние

    Входные параметры

    port$1111$—numberПорт сервера
    profile«mars-bus»—comboПрофиль кадра: mars-bus, stm32-lab1, arduino-demo
    autoStartTrue—boolАвтозапуск TCP-клиента

    Состояния и интегрирование

    Внутреннее состояние: ch1_last, ch2_last, keys.

    Математическая модель

    Не применимо для логических/мета компонентов.

    Алгоритм работы

    1. io.write("CH1", float(io.state.get( "ch1_last", 0.0))); io.write("CH2", float(io.state.get( "ch2_last", 0.0))).
    2. Считать k1, k2, k3, k4 из входов (с бинаризацией по 0.5).
    3. io.state["keys"] = [k1, k2, k3, k4].
    TCP-сервер
    tcp_stand_server
    Логический
    логическийСтенды/Транспортstands/tcp_stand_server/

    TCP-сервер: приём кадров оборудования через tcp-bridge. Поддерживает три режима (параметр mode):

    • device — устройство (STM32) подключается как клиент; декодирование кадров $\to$ CH1, CH2;
    • publish — публикация сигнальной шины mars-bus для удалённого ORPHYS;
    • isul — ответы осциллографу ИСУЛ его протоколом (запрос OSCILL_ID = 521 $\to$ FST / SND).
    Логика обмена и декодирования кадров — в Electron main (tcp-stand-server). Используется для приёма данных от реального оборудования по TCP (STM32, Arduino с Ethernet) или для связи двух ORPHYS-инстансов.

    SPEC.folderСтенды/Транспорт
    Папкаstands/tcp_stand_server/
    Типлогический
    Уровеньlogic
    symTCPs
    bodyTCP\nSRV

    Связи компонента (порты)

    k2inleftКнопка 2
    k3inleftКнопка 3
    k4inleftКнопка 4
    in_CH1inleftАналоговый вход 1 (от шины)
    in_CH2inleftАналоговый вход 2 (от шины)
    CH1outrightКанал 1 (для шины)
    CH2outrightКанал 2 (для шины)
    cmd_outoutrightСформированная команда
    Не применимо.

    Параметры и состояние

    Входные параметры

    port$1111$—numberПорт
    profile«stm32-lab1»—stringПрофиль кадра
    mode«device»—comboРежим: device / publish / isul
    autoStartTrue—boolАвтозапуск
    genFreq$50$ГцnumberЧастота генератора (для isul)
    genPhase$0$—numberФаза
    genForm$0$—numberФорма сигнала

    Состояния и интегрирование

    Внутреннее состояние: ch1_last, ch2_last, keys.

    Математическая модель

    Не применимо для логических/мета компонентов.

    Алгоритм работы

    1. io.write("CH1", ...) — выдать последний принятый сэмпл канала 1 (для режима device — от удалённого устройства; иначе — pass-through из in_CH1).
    2. Аналогично для CH2.
    3. Считать k1, k2, k3, k4 из входов (бинаризация по 0.5), in_CH1, in_CH2 (pass-through).
    4. io.state["keys"] = [k1, k2, k3, k4].
    5. io.write("cmd_out", k1*8 + k2*4 + k3*2 + k4).
    (Полный код функции занимает $\sim 17$ строк после декоратора; см. исходный файл.)
    Фейк Arduino
    arduino_fake_stand
    Логический
    логическийСтенды/Фейкиstands/arduino_fake_stand/

    Заглушка Arduino: компонент палитры, представляющий стенд-эмулятор (TCP-сервер / stub / serial-эмулятор). Реальная логика стенда живёт в Electron main (fake-bridge → arduino-fake-stand) и пишет значения в io.state (расстояние в state["distance_cm"], статус соединения в state["connected"]). На каждом тике шины компонент отдаёт эти значения как сигналы. Используется для тестирования схем ORPHYS без реального оборудования.

    SPEC.folderСтенды/Фейки
    Папкаstands/arduino_fake_stand/
    Типлогический
    Уровеньlogic
    symfARD
    bodyARD\nfake

    Связи компонента (порты)

    statusoutrightСтатус соединения (1/0)
    Не применимо.

    Параметры и состояние

    Входные параметры

    host«0.0.0.0»—stringХост (для режима tcp)
    port$9000$—numberПорт (для tcp)
    path«COM10»—stringCOM-путь (для serial)
    baud$115200$—numberBaud rate (для serial)
    hz$5$ГцnumberЧастота обновления (для runtime)
    autoStartTrue—boolАвтозапуск runtime при инициализации

    Состояния и интегрирование

    Внутреннее состояние — io.state["distance_cm"], io.state["connected"]. Метод интегрирования не используется (компонент читает чужие значения и транслирует их в выходные порты).

    Математическая модель

    Не применимо для логических/мета компонентов.

    Алгоритм работы

    1. io.write("distance", float(io.state.get( "distance_cm", 0.0))).
    2. io.write("status", float(io.state.get( "connected", 0.0))).
    Эмулятор STM32
    stm_fake_stand
    Логический
    логическийСтенды/Эмуляторыstands/stm_fake_stand/

    Заглушка STM32: компонент палитры, представляющий программный стенд-эмулятор (TCP-клиент). Эмулятор живёт в Electron main (fake-bridge → stm-fake-stand), шлёт кадры АЦП как прошивка. На тике шины отдаёт статус соединения. Каналы CH1/CH2 приходят через соседний TCP-сервер (tcp_stand_server). Внимание: для прошивки .elf используйте схему HIL (Renode), не этот блок. Используется для тестирования без реального STM32.

    SPEC.folderСтенды/Эмуляторы
    Папкаstands/stm_fake_stand/
    Типлогический
    Уровеньlogic
    symeSTM
    bodySTM\nemu

    Связи компонента (порты)

    Не применимо.

    Параметры и состояние

    Входные параметры

    port$1111$—numberПорт TCP
    hz$50$ГцnumberЧастота обновления
    samples$3600$—numberЧисло сэмплов
    autoStartTrue—boolАвтозапуск
    noiseTrue—boolДобавить шум в эмулируемый АЦП
    noiseSigma$0.13$—number$\sigma$ шума
    distortionTrue—boolНелинейное искажение

    Состояния и интегрирование

    Внутреннее состояние: io.state["connected"].

    Математическая модель

    Не применимо для логических/мета компонентов.

    Алгоритм работы

    1. io.write("status", float(io.state.get( "connected", 0.0))).