аннотация
Рассматривается принцип работы солвера технологических схем класса non-sequential modular: класса, к которому в литературе относят HYSYS[1] и к которому принадлежит открытый симулятор Sim42. Sim42 — магистерский проект Университета Калгари (2001–2003); в разработке его солвера определяющую роль сыграл Крейг Моррис, по собственному свидетельству ранее создавший симулятор Hysim в компании Hyprotech[1, 3, 4]. На примере Sim42 описаны ключевые механизмы: хранение значения вместе со статусом происхождения, распространение частичной информации вперёд и назад по схеме, очерёдность решения аппаратов по стеку LIFO, инвалидация устаревших результатов без графа зависимостей (двухпроходная схема «забывание → решение») и итерации по рециклу методом Бройдена. Изложение ведётся на двух уровнях — для специалиста по моделированию (степени свободы, порядок ввода, диагностика) и для разработчика (структуры данных и алгоритмы). К статье прилагается интерактивная лаборатория: собственная реализация алгоритма, проверенная автоматическими тестами. История проекта восстановлена по открытым источникам; различаются подтверждённые факты и гипотезы. Исходный код Sim42 анализировался статически, без запуска[12, 13].
Abstract. The paper explains how a non-sequential modular flowsheet solver — the class to which HYSYS is assigned in the literature — works, using the open-source Python simulator Sim42 (University of Calgary, 2001–2003) as a worked example. We describe value-with-status storage, bidirectional propagation of partial information, LIFO dispatch of unit operations, dependency-graph-free invalidation (a two-pass “forget → solve” scheme) and recycle convergence with Broyden’s method. The text has two layers (for the simulation practitioner and for the developer) and is accompanied by an interactive lab, an independent re-implementation of the algorithm verified by automated tests. The history of the project is reconstructed from open sources, with confirmed facts distinguished from hypotheses.
Ключевые слова: солвер технологических схем; непоследовательно-модульный подход; распространение ограничений; степени свободы; рецикл; метод Бройдена; Sim42; HYSYS.
1Введение: где задавать данные и в каком порядке считать
Любой симулятор технологических процессов решает одну и ту же задачу: по набору заданных величин (спецификаций) найти остальные параметры схемы так, чтобы выполнялись материальные и энергетические балансы и условия фазового равновесия. Различие между симуляторами — в том, кто и как определяет порядок вычислений. От этого зависят очень практичные вещи: можно ли задать температуру продукта вместо температуры сырья, что происходит при смене одной спецификации и что видит пользователь, если данных не хватает или их слишком много.
В литературе выделяют два основных подхода к решению схемы[1, §1.1.1]. В уравнение-ориентированном (equation-oriented) подходе уравнения всех аппаратов собираются в одну систему и решаются одновременно методами типа Ньютона. В модульном (modular) подходе схема решается аппарат за аппаратом; модульные симуляторы делятся на последовательные (sequential modular), где расчёт идёт строго от сырья к продукту, и непоследовательные (non-sequential modular), способные распространять информацию вперёд и назад, так что пользователь может задать данные в любой точке схемы. Определение последнего типа диссертация, на которую опирается эта статья, связывает с работой Svrcek и соавторов 1984 года[1, §1.1.1].
| Подход | Достоинства | Недостатки |
|---|---|---|
| Уравнение-ориентированный | Новые аппараты добавляются описанием уравнений; рецикл — часть решения; быстрое повторное решение; данные можно задавать где угодно при выполнении степеней свободы | Сложно решить в первый раз и инициализировать; при сбое решателя результата нет вовсе; трудно отлаживать |
| Последовательно-модульный | Малые системы уравнений, проще инициализация; частичный результат есть, даже если вся схема не решена | Для спецификаций на выходе нужны алгоритмы подбора; рецикл — итерации по всей схеме |
| Непоследовательно-модульный | То же, плюс свобода задавать свойства на выходах и получать требования к сырью расчётом | Медленен для динамики; рецикл сходится итерациями по всей схеме |
Строки сформулированы по перечню преимуществ и недостатков в тексте диссертации; для непоследовательно-модульных симуляторов добавлено отличие от последовательно-модульных.
Коммерческие солверы закрыты, и разобраться, как именно «думает» непоследовательно-модульное ядро, по документации обычно нельзя. Этим объясняется выбор объекта изучения: Sim42 — открытый симулятор, исходный код которого доступен[7], а принцип работы солвера описан в диссертации его автора[1] и подробно разобран в аналитических материалах по коду[12, 13]. Это позволяет показать механизм «изнутри» и на конкретных числах.
2История: от Hyprotech к Sim42
2.1. Калгарийская школа и HYSYS
Компания Hyprotech создана исследователями Университета Калгари; профессор В. Свэрчек (W. Y. Svrcek) значится партнёром Hyprotech с момента её учреждения в 1976 году[9, 10]. Продукты компании — HYSIM, затем HYSYS (справочник по HYSYS версии 1.1 издан в 1996 году). В мае 2002 года Hyprotech приобрела AspenTech; впоследствии, по решению Федеральной торговой комиссии США, права на часть активов, включая исходный код HYSYS, были переданы Honeywell, на базе чего появился UniSim[9]. Цены и даты сделок в источниках расходятся, подробности для сути статьи несущественны.
Для истории солвера важнее другое: в диссертации по Sim42 HYSYS перечислен среди «непоследовательно-модульных» симуляторов (Канада), а само понятие со ссылкой на Svrcek et al. (1984)[1, §1.1.1–1.1.2]. Оригинал этой работы найти в открытых источниках не удалось, поэтому ссылка приводится «через диссертацию».
2.2. Рождение Sim42 (2001–2003)
Хронологию проекта дают две группы первоисточников: страница «The History of Sim42» на сайте проекта (архивная копия от 9.12.2002)[2] и благодарности в диссертации[1]. В мае 2001 года Raúl Cota (Рауль Кота) начал в Университете Калгари магистерскую работу — создание симулятора с открытым кодом на Python; руководителями были Marco Satyro, Bill Svrcek и Brent Young[1, 2].
| Дата | Событие | Источник |
|---|---|---|
| Май 2001 | Cota начинает магистерский проект: открытый симулятор на Python | [2] |
| Лето 2001 | Каркас симулятора: структура аппаратов, порты и их соединения; принцип «ядро независимо от интерфейсов» | [2] |
| Лето–осень 2001 | Satyro (VMG) передаёт термо-пакет; знакомит с проектом Craig Morris — тот берётся написать «более изощрённый солвер», размещает репозиторий и рассылку | [2] |
| Осень 2001 | VMG решает использовать Sim42 как ядро коммерческой VMGSim (Excel/Visio); передаёт систему единиц; к проекту присоединяется Yau Kun Li | [2] |
| Зима 2001/02 | Новые аппараты, работающая колонна, тестовый набор; рабочее имя Awesim занято — Cota предлагает «Sim42»; в конце марта 2002 получен домен sim42.org | [2] |
| Лето 2002 | GUI-версия командной строки, первый сайт; создаётся Sim42 Software Foundation (общество в Британской Колумбии) — хранитель лицензии и имени | [1, 2] |
| Осень 2002 | Satyro пишет руководство; сайт на 8–9.12.2002 зафиксирован в веб-архиве | [2, 5] |
| Апрель 2003 | Диссертация Cota на степень M.Sc. | [1] |
Участники проекта и их вклад по тем же источникам[1, 2, 3, 4]: Raúl Cota — исходная архитектура (аппараты, порты, интерфейс к термодинамике), графическая версия командной строки, испанские сообщения; Craig Morris — солвер, колонна, контроллеры и скриптовые аппараты, первый командный интерфейс, хостинг репозитория; Marco Satyro — термодинамика (через VMG), руководство пользователя, научное руководство; Bill Svrcek и Brent Young — научное руководство; Yau Kun Li — насосы с кривыми, конверсионные реакторы, гипотетические компоненты; Andrew Liu и Rajeev Agarwal — код и спецификации аппаратов; Patrick O’Brien — интерпретатор PyCrust; Jose-Henrique Pinto — переводы сообщений и FAQ. Систему единиц передала компания VMG[2].


2.3. Откуда пришли идеи солвера
Автор солвера Craig Morris в профиле на сайте проекта пишет, что «когда-то написал симулятор Hysim» и покинул Hyprotech за десять с лишним лет до 2002 года; об устройстве будущего солвера у него были «довольно необычные идеи», и он вызвался написать солвер для проекта Cota[3]. Satyro, в свою очередь, отмечает, что Sim42 «унаследовал» от Morris двунаправленную передачу информации, вложенность аппаратов и командный язык[4]. Эти свидетельства самоописательны (профили участников, написанные ими самими), но совпадают с описанием солвера в диссертации и по коду[1, 13].
Следовательно, правомерно сказать: принцип непоследовательно-модульного солвера, реализованный в Sim42, — того же класса, что и в HYSYS, и создан в среде и руками людей, связанных с Hyprotech. Утверждать идентичность алгоритмов или происхождение кода HYSYS в Sim42 открытые источники оснований не дают.
2.4. Лицензия и сообщество
Код Sim42 распространялся по «SIM42 Software License, Version 1.0» (© 2002 SIM42 Software Foundation)[6]. Сайт и FAQ называли её BSD-подобной: разрешены использование и изменение без обязательной передачи изменений, что важно для компаний, добавляющих собственные модели[1, 5]. Текст лицензии содержит условия атрибуции: в документации распространяемого продукта должно быть указание на разработку SIM42 Software Foundation; производные продукты нельзя называть SIM42 без письменного разрешения[6]. В 2008 году участники проекта OpSim сочли эти условия несовместимыми с GPL и разработали собственный симулятор «с нуля», используя Sim42 как «источник идей»[11] — это мнение участников, а не юридическое заключение.
2.5. После 2004 года
Дальнейшую судьбу проекта в открытых источниках удаётся проследить лишь фрагментарно. Домен sim42.org с 2013 года в веб-архиве представлен страницами-«парковками». Дистрибутив версии 2.0.0.0 сохранился на SourceForge в проекте OllinTS (зарегистрирован в 2007 году, последнее обновление — 2015; лицензия GPLv2) — это термодинамический сервер, который создавался «для завершения Sim42»[8]. В 2011 году этот дистрибутив был импортирован в репозиторий GitHub, после чего автором импорта начата конвертация с устаревшего расширения Numeric на NumPy[7]; последний коммит — ноябрь 2011 года. Остатки обращений к Numeric в отдельных модулях, отмеченные в аналитических материалах[12, 13], — следы этой незавершённой миграции. Признаков активного сопровождения в открытых источниках не обнаружено.
3Принцип для специалиста по моделированию: «решает всё, что может»
3.1. Главная идея
В классическом последовательном симуляторе пользователь подаёт сырьё, затем считается первый аппарат, потом второй и так далее. Если нужно «задать то, что на выходе», схему приходится перекраивать вручную. Солвер класса Sim42 работает иначе. Представьте большую таблицу судоку: каждая клетка — параметр потока (температура, давление, расход, состав), а каждый аппарат — набор правил («сумма на входе равна сумме на выходе»). Как только в какой-то клетке появляется число, все правила, которым хватает данных, срабатывают и заполняют соседние клетки — в любую сторону[1, 12].
- Информация идёт в обе стороны. Если известны состав и расход смеси на выходе смесителя и один из двух входов, второй вход вычисляется как разность.
- Порядок ввода не важен. Можно сначала задать давление на выходе, потом температуру на входе — результат тот же.
- Расчёт идёт сразу после каждого ввода (если не включён режим паузы), поэтому схему можно собирать и «чувствовать» в интерактивном режиме.
- Часто рецикл считать не нужно. Авторы отмечают, что за счёт распространения частичной информации во все стороны многие сложные задачи решаются без итераций по рециклам[5].
- Защита от переопределения. Если одно значение получено из двух источников и они не совпали, программа не молчит, а сообщает о противоречии.
3.2. Из каких «кирпичиков» состоит модель
Аппарат (unit operation) — любой объект схемы: смеситель, нагреватель, колонна, насос, контроллер. В Sim42 потоки — тоже аппараты, а не отдельная сущность; аппараты можно вкладывать друг в друга (теплообменник собран из нагревателя и охладителя)[1]. Порт (port) — «розетка» аппарата, через которую течёт информация; бывают материальные порты (T, P, расход, состав), энергетические и сигнальные (одно число — перепад давления, управляющий сигнал). Направление порта — это направление потока вещества, а не порядок расчёта: информация через порт ходит в обе стороны.
Каждое значение хранится вместе со статусом происхождения — откуда оно взялось. Именно статус позволяет программе решать, что можно перезаписывать, а что нельзя.
| Статус | Что означает | В интерфейсе (по руководству) |
|---|---|---|
| Задано (FIXED) | Пользователь ввёл значение. Это «спецификация»; программа сама её не изменит. | значение со звёздочкой * |
| Рассчитано (CALCULATED) | Значение вычислил аппарат, которому принадлежит порт. | значение со знаком = |
| Передано (PASSED) | Значение пришло из соседнего порта по соединению. | отображается как рассчитанное |
| Оценка (ESTIMATED) | Начальное приближение для итераций по рециклу (оператор ~=). | может перезаписываться при итерациях |
| Неизвестно (UNKNOWN) | Данных пока недостаточно. | значение None |
| Новое (NEW) | Служебная отметка: значение получено в текущем проходе расчёта. | не отображается |
3.3. Степени свободы и контроль избыточности
У каждого аппарата есть физически обоснованное число параметров, которые нужно задать: например, у нагревателя — состав и расход на входе, T и P на входе, плюс одно из трёх: температура на выходе, тепловая нагрузка или перепад давления. Когда задано ровно столько, сколько нужно, аппарат считает остальное. Если задано меньше — часть значений останется None. Если больше — возможны противоречия. Руководство Sim42 называет это «Interactive Simulation and Degrees of Freedom Monitoring»[12].
Правило, на котором это построено, универсально: если в балансе не хватает ровно одного значения — оно вычисляется; если не хватает двух и более — программа ждёт новых данных; если известно всё — проверяется, что баланс сошёлся. Расчётное значение никогда не перезаписывает уже известное: оно только сверяется с ним. Расхождение сверх допуска считается противоречием (ConsistencyError). Сверка идёт не в абсолютных единицах, а в долях масштаба переменной, чтобы 0,1 К и 0,1 Па не считались равнозначными[12, 13].
| Переменная | Масштаб | Что считается «одинаковым» |
|---|---|---|
| Температура | 100 | расхождение менее 0,01 К |
| Давление | 1000 | менее 0,1 кПа |
| Молярная энтальпия | 10 000 | менее 1 кДж/кмоль |
| Мольный расход | 1000 | менее 0,1 кмоль/ч |
| Массовый расход | 50 000 | менее 5 кг/ч |
| Мольная/массовая доля, доля пара | 1 | менее 0,0001 |
3.4. Порядок ввода не важен, история — тоже
Расчёт запускается не кнопкой «Рассчитать», а любым изменением данных. Команда hold приостанавливает расчёт — удобно, когда нужно ввести много данных; go возобновляет его. После go результат должен совпасть с вводом тех же данных без паузы. Самый сильный тест на корректность: «задал 80, потом изменил на 90» и «сразу задал 90» обязаны дать один и тот же результат[12]. Как солвер этого добивается, не храня граф зависимостей, разобрано в разделе 5.5.
3.5. Как читать результат
| Сообщение | Что случилось | Типичная причина |
|---|---|---|
| MaxSolverIterExceeded | Схема не сошлась за максимальное число итераций | Слабая оценка рецикла, слишком жёсткий допуск |
| UnresolvedRecycles | Перечень портов рецикла, которые не сошлись | Показывает, где «застряло» |
| ConsistencyError | Остались противоречия между значениями | Лишние или несовместимые спецификации |
| ControllerConvergeFail | Контроллеры не достигли цели | Цель вне диапазона [min; max], мал шаг |
| CouldNotConverge | Внутренний решатель аппарата не сошёлся | Плохое начальное приближение |
| CompNotNormalized | Сумма долей состава не равна 1 | Ошибка ввода состава |
Полный набор: схема считается целиком.
Спецификации (10 задано)
| S1 Feed | T25P100F100H0 |
|---|---|
| Heater | ΔP нагревателя20Q нагревателя414150 |
| S2 | T80P80F100H4141.5 |
| Pump | ΔP насоса500КПД η0.75Q идеальной работы900Q потерь (в тепло)300Q на валу1200 |
| S3 | T80.2P580F100H4153.5 |
| Cooler | ΔP холодильника30Q холодильника302400 |
| S4 Product | T40P550F100H1129.5 |
Синий — задано (FIXED), зелёный — рассчитано (CALCULATED), серый — не определено. Результат не зависит от того, в каком порядке вы вводите данные: попробуйте включать и выключать галочки.
Песочница использует модель из раздела 4. Попробуйте набор «Только с выхода»: по продукту и параметрам аппаратов солвер полностью восстановит вход; «Недоопределён» — получите None без ошибки; «Лишняя, конфликтная» — получите ConsistencyError.
4Разбор расчёта схемы Heater → Pump → Cooler
4.1. Постановка
Схема из трёх аппаратов: нагреватель (Heater), насос (Pump) и холодильник (Cooler). Вместо перепадов температур заданы температуры на выходе (Heater — 80 °C, Cooler — 40 °C), у насоса задан КПД. Рабочее тело — вода; расход и прочие условия приведены в таблице 6. Реализованные в лаборатории правила соответствуют описанию в [12] и [13]: флэш при двух известных из (T, P, H) и составе, мольный и энергетический балансы по принципу «ровно одно неизвестное», расчёт перепада давления, изоэнтропа насоса.
| Спецификация | Значение | Рассчитано | Значение |
|---|---|---|---|
| Feed: T, P, состав | 25 °C, 100 кПа, z | Q нагревателя | 414 150 кДж/ч |
| Heater: ΔP; T на выходе | 20 кПа; 80 °C | P на выходе Heater | 80 кПа |
| Pump: ΔP; КПД η | 500 кПа; 0,75 | Работа на валу = Q_ид/η | 1200 кДж/ч |
| Cooler: ΔP; T на выходе | 30 кПа; 40 °C | Идеальная работа Q_ид = F·v·ΔP | 900 кДж/ч (v = 0,018 м³/кмоль) |
| Product: расход F | 100 кмоль/ч | Потери в тепло Q_пот = Q_вал − Q_ид | 300 кДж/ч |
| T после насоса | 80,16 °C | ||
| P: S1→S2→S3→S4 | 100 → 80 → 580 → 550 кПа | ||
| Q холодильника (отведено) | 302 400 кДж/ч |
Упрощения: вода с cp = 75,3 кДж/(кмоль·К), H = cp·(T − 25); насос идеальный по изоэнтропе ΔH = v·ΔP. Единицы: T °C, P кПа, F кмоль/ч, H кДж/кмоль, Q кДж/ч.
Насос — составной аппарат: Ideal (идеальное сжатие по изоэнтропе), Waste (потери превращаются в тепло, ΔP = 0), Set (связывает КПД с энергиями: Q_ид = η·Q_вал) и Mix (энергобаланс Q_вал = Q_ид + Q_пот). Каждая часть решает только свою маленькую задачу; вместе они дают полный расчёт. Это пример вложенности аппаратов, о которой говорится в описании Sim42[1].
4.2. Что происходит при вводе
- T и P питания ничего не дают: для флэша нужен ещё состав. Аппарат — Feed — ставится в очередь, расчёт стоит.
- Состав питания завершает тройку (T, P, z): сработал флэш, появилась энтальпия H(S1). Heater получает «новое знание» и встаёт на вершину стека.
- ΔP нагревателя и T на выходе: давление на выходе Heater = 100 − 20 = 80 кПа; по (T, P) выхода — энтальпия H(S2). Расхода пока нет, поэтому тепловая нагрузка ещё неизвестна — в энергобалансе два неизвестных (F и Q).
- Насос и холодильник заполняют то, что можно вычислить без расхода: давления (580 и 550 кПа) и энтальпии потоков. Температура после насоса пока неизвестна: она зависит от потерь в тепло, а потери — от расхода.
- Последним вводится расход продукта F = 100 кмоль/ч — на выходе схемы. Мольный баланс отправляет его назад по всей схеме до питания; как только расход известен, в каждом энергобалансе остаётся ровно одно неизвестное, и один за другим определяются Q нагревателя, энергии насоса и нагрузка холодильника.
Именно пятый шаг показывает отличие от последовательного расчёта: расход, заданный в конце схемы, вычисляет всё в её начале. Модель можно пройти по шагам: фазы расчёта подсвечены, стек решения виден, значения вспыхивают в момент вычисления.
S1 Feedвход
Heaterнагреватель
S2выход Heater
IdealS=const
Piпосле Ideal
WasteΔP=0
Setη
MixE-баланс
S3выход насоса
Coolerхолодильник
S4 Productпродукт
Схему можно прокрутить по горизонтали →
Стек решения _solveStack · LIFO
root.Solve()).Результаты расчёта
| Q нагревателя, кДж/ч | ? |
| Работа на валу насоса, кДж/ч | ? |
| из них идеальная | ? |
| потери в тепло | ? |
| T после насоса, °C | ? |
| Q холодильника, кДж/ч | ? |
| Расход F, кмоль/ч | ? |
| P: S1→S2→S3→S4, кПа | ? → ? → ? → ? |
Неопределённых величин на этом шаге: 29
Упрощения модели: вода (cp = 75,3 кДж/кмоль·К, H = cp·(T−25)), насос идеальный по изоэнтропе ΔH = v·ΔP; поток показан одной карточкой (в коде у потока отдельные порты In/Out); датчики энергии сведены в «провода». Единицы: T °C, P кПа, F кмоль/ч, H кДж/кмоль, Q кДж/ч. Клавиши: ← → шаг, пробел — пуск/пауза.
4.3. Порядок ввода меняет путь, но не итог
Если вводить те же данные от выхода к входу, число крупных шагов (один шаг — один решённый аппарат или ввод) меняется, а итоговые значения остаются теми же. В режиме hold → go очередь копится и решается одним проходом, поэтому шагов заметно меньше.
| Порядок ввода | Расчёт после каждого ввода | hold → go |
|---|---|---|
| от входа к выходу | 73 | 33 |
| от выхода к входу | 70 | 36 |
Итоговые значения во всех четырёх вариантах совпадают; это проверяется автоматическим тестом.
4.4. Смена спецификации: забывание и решение
Что произойдёт, если после полного расчёта изменить температуру выхода Heater с 80 до 90 °C? Все значения, полученные из 80 °C, устарели — но солвер не хранит карту зависимостей. Сначала выполняется фаза «забывания»: новая спецификация на время притворяется неизвестной, старые рассчитанные значения скрыты, аппараты решаются «вхолостую» и подтверждают только то, что от изменённой величины не зависит; остальное стирается. В лаборатории (сценарий «+ смена T выхода 80→90») при этом стирается 23 величины в шести проходах; затем новая спецификация записывается, и обычный расчёт заново заполняет схему. Результат — тепловая нагрузка 489 450 кДж/ч — в точности совпадает с расчётом «с чистого листа» при 90 °C. Подробнее — в разделе 5.5.
4.5. Рецикл: оценка на потоке и итерации
Если выход схемы возвращается на вход, возникает замкнутый круг: чтобы посчитать смеситель, нужно знать рецикл, а чтобы знать рецикл — посчитать смеситель. Волна распространения упирается в неизвестное. В Sim42 специального аппарата «Recycle» нет: пользователь ставит оценку (оператор ~=) в любой порт — он становится точкой разрыва[1]. Оценочные значения не передаются дальше как факт; направление вычислений определяется тем, во входном или в выходном порту поставлена оценка (рисунок 4).
Далее схема считается целиком, результат сравнивается с оценкой, ошибка нормируется масштабом переменной, и оценка обновляется: первый шаг — простая подстановка, затем метод Бройдена (квази-ньютоновский, без расчёта производных)[12, 13]. Автоматического выбора разрывного потока в коде не обнаружено — это решает пользователь[12].
Оценка на потоке рецикла
Параметры солвера
Масштабы переменных: R — 1000, T — 100. Допуск (MaxError) 0.0001. Точное решение схемы: R ≈ 255.5 кмоль/ч, T ≈ 298.6 °C. Решение одно, независимо от начальной оценки.
| Итер. | R оценка | T оценка | R получено g(x) | T получено g(x) | Ошибка | Шаг (доли масштаба) | |
|---|---|---|---|---|---|---|---|
| 1 | 10.00 | 300.00 | 78.84 | 299.73 | 6.88e-2 | 0.0500; -0.0020 | шаг ограничен |
| 2 | 60.00 | 299.80 | 114.71 | 299.65 | 5.47e-2 | 0.0500; -0.0012 | шаг ограничен |
| 3 | 110.00 | 299.68 | 150.59 | 299.54 | 4.06e-2 | 0.0500; -0.0015 | шаг ограничен |
| 4 | 160.00 | 299.53 | 186.51 | 299.40 | 2.65e-2 | 0.0500; -0.0020 | шаг ограничен |
| 5 | 210.00 | 299.32 | 222.48 | 299.22 | 1.25e-2 | 0.0446; -0.0028 | |
| 6 | 254.56 | 299.05 | 254.61 | 298.96 | 8.54e-4 | 0.0003; -0.0009 | |
| 7 | 254.83 | 298.96 | 254.85 | 298.89 | 6.87e-4 | 0.0006; -0.0030 | |
| 8 | 255.42 | 298.66 | 255.42 | 298.65 | 1.04e-4 | 0.0001; -0.0005 | |
| 9 | 255.52 | 298.60 | 255.52 | 298.60 | 8.27e-7 | 0.0000; 0.0000 |
Схема считается целиком при заданной оценке потока рецикла; результат g(x) сравнивается с оценкой x, ошибка нормируется шкалой переменной. Первый шаг — подстановка (единичный якобиан), дальше метод Бройдена обновляет обратный якобиан по предыдущим шагам. Шаг ограничен параметром MaxRecycleStep (по умолчанию 0,05 шкалы) — при срабатывании ограничения строка таблицы помечена.
Модель рецикла — учебная (смеситель, реактор, сепаратор, линия возврата), а не схема из Sim42. Параметры солвера соответствуют значениям по умолчанию: MaxNumIterations = 20, MaxRecycleStep = 0,05, MaxError = 0,0001[12, 13]. При них простая подстановка не укладывается в 20 итераций и завершается сообщением MaxSolverIterExceeded, а метод Бройдена сходится за 9.
5Устройство ядра: структуры данных и алгоритмы
Раздел адресован разработчикам. Описание построено по аналитическому разбору исходного кода Sim42 v2.0.0.0 (каталоги sim/solver, sim/unitop; репозиторий[7]) и тексту диссертации[1]; анализ статический, программа не запускалась[13]. Имена классов и методов (BasicProperty, Flowsheet.InnerSolve и т. п.) даны по коду; номера строк в источнике могут смещаться. В режиме «Специалист» подразделы свёрнуты; режим «Разработчик» раскрывает их.
для разработчика5.1. Архитектура слоёв
Ядро — событийный решатель «распространения ограничений» (constraint propagation) над графом аппаратов и портов[13]. Порядок расчёта нигде не предопределён: он задаётся динамически стеком аппаратов, «получивших новую информацию». В README проекта заявлено, что ядро спроектировано независимым и от интерфейсов пользователя, и от поставщиков термодинамики; в коде это подтверждается: ядро общается с внешним миром через InfoMessage (callback сообщений), ThermoAdmin (термодинамика) и S42Glob.unitSystem (единицы)[5, 13].
Схема (Flowsheet) — тоже аппарат, поэтому вложенные «flowsheet» работают по тем же правилам; поток — тоже аппарат с портами In и Out. Это позволяет собирать сложные аппараты из простых: теплообменник — из нагревателя и охладителя[1].
для разработчика5.2. Модель данных: переменная, статус, тип
Каждое значение — объект BasicProperty с полями value, calcStatus и ссылкой на PropertyType (имя, флаги флэша, тип единиц, scaleFactor, minValue, maxValue). Экземпляры типов лежат в глобальном словаре и разделяются всеми переменными данного типа[13].
| Константа | Бит | Назначение |
|---|---|---|
| UNKNOWN_V | 1 | значения нет (value is None) |
| FIXED_V | 2 | задано пользователем (спецификация) |
| CALCULATED_V | 4 | вычислено аппаратом-владельцем порта |
| PASSED_V | 8 | передано через соединение от соседнего порта |
| NEW_V | 16 | установлено в текущем проходе (сбрасывается стеками ResetNew*) |
| ESTIMATED_V | 32 | оценка для итерации рецикла (комбинируется с FIXED) |
| PARENT_V | 64 | вычислено родительским аппаратом в порт дочернего |
Логика записи BasicProperty.SetValue(value, calcStatus):
| Ветка | Поведение |
|---|---|
| Без порта | просто присвоить (вспомогательные переменные) |
| FIXED_V | приведение к float; None → UNKNOWN; режим порта (FIXALL/ESTIMATEALL); если статус и значение не изменились — выход; пометка NEW_V; вызов port.PropertyModified; нулевой расход заменяется на 1e-40 |
| UNKNOWN_V | стирание: допустимо только value=None; ставит UNKNOWN|NEW, вызывает PropertyModified |
| CALCULATED_V / PASSED_V | None игнорируется. Если значение уже есть (и нет «забывания») — CheckTolerance: старое сохраняется, при расхождении выше допуска — PushConsistencyError. Если значения нет — запись с NEW|status и PropertyModified |
Метод GetValue() зависит от режима солвера: вне «забывания» возвращает value; в режиме «забывания» (op.IsForgetting()) новые FIXED-значения выглядят как None, а старые CALCULATED скрыты, если не помечены NEW[13]. Эта подмена видимости превращает обычный op.Solve() в «холостой» прогон (см. 5.5).
для разработчика5.3. Порты и распространение: PropertyModified и UpdateConnection
Порты бывают материальные (Port_Material: состав, массивные свойства, кэш флэша), энергетические и сигнальные; типы — битовые флаги IN, OUT, MAT, ENE, SIG. Порт соединяется только с одним портом совместимого типа, вход — только с выходом; ConnectTo разрывает старое соединение, оповещает аппарат и сразу вызывает UpdateConnection()[13].
Точка, где изменение значения превращается в события солвера, — Port.PropertyModified(property, calcStatus):
| Новый статус | Действие |
|---|---|
| FIXED_V | PushSolveOp(владелец) + PushResetFixedPort(порт) + PushForgetOp(владелец) |
| UNKNOWN_V | PARENT_V | PushForgetOp и PushSolveOp родителя владельца |
| PASSED_V или PARENT_V | PushSolveOp(владелец) + PushResetCalcPort(порт): сосед получил знание → его надо считать |
| CALCULATED_V | PushResetCalcPort(порт): только сброс отметки NEW |
| UNKNOWN_V | PushForgetOp(владелец) + PushSolveOp(владелец) |
Port.UpdateConnection() вызывается солвером после op.Solve() для всех портов аппарата:
- Нет соединения — стираются свои значения со статусом PASSED.
- Порт «оценочный» (
_estimated) ничего не передаёт: у соседа стираются PASSED-значения — так «разрывается» рецикл. - Если у соседа значение ESTIMATED, а у себя значение известно — новое значение попадает в стек итераций (
PushIterationProperty). - Обычный режим: если у себя значение известно и оно не PASSED —
connProp.SetValue(myValue, PASSED_V); дальше сработаетPropertyModified(PASSED_V)соседа, и он встанет в очередь.
В режиме «забывания» переданное значение соседа стирается, если у источника оно неизвестно, «новое-несчитанное», «старое-считанное» либо не совпадает[13].
для разработчика5.4. Шесть стеков и главный цикл Flowsheet
Flowsheet владеет шестью списками-стеками; аппарат любого уровня вложенности помещается в стек корневого солвера (методы Push* у не-Flowsheet аппарата делегируют вызов родителю). Флаги _stackStatus (ON_SOLVE_STACK и др.) исключают двойное помещение; BlockPush(1) блокирует помещение аппарата в стеки, пока он решается — иначе его собственные записи в порты ставили бы его в очередь заново и цикл стал бы бесконечным[13].
| Стек | Элементы | Заполняется | Потребляется |
|---|---|---|---|
| _solveStack | аппараты | PropertyModified (FIXED/PASSED/UNKNOWN), смена параметров | InnerSolve: op.Solve() |
| _forgetStack | аппараты | PropertyModified (FIXED/UNKNOWN), SetState | SolverForget: холостой Solve + Forget |
| _resetNewCalcStack | порты | PropertyModified (CALCULATED/PASSED) | после прохода: снять NEW_V с расчётных значений |
| _resetNewFixedStack | порты | PropertyModified (FIXED) | после forget-прохода: снять NEW_V со спецификаций |
| _iterationStack | свойства ESTIMATED с новым значением | UpdateConnection (оценочная сторона) | цикл рециклов (Бройден) |
| _consistencyErrorStack | свойства с конфликтом | CheckTolerance | блокируют сходимость; в конце — ConsistencyError |
InnerSolve():
consistencyErrorStack.clear()
iter = 0; jacobian = None
while iter < maxIter:
iter += 1
SolverForget()
if hold: return
while op = PopSolveOp(): # LIFO
if op is not self and op.Ignored is None:
op.BlockPush(1)
try:
op.Solve()
for port in op.GetPorts(): port.UpdateConnection()
finally:
op.BlockPush(0)
ResetNewCalc()
n = len(iterationStack)
if n == 0: break # рециклов нет — готово
maxError = max(prop.CalculateError(newValue)) # итерации + consistency
if maxError < tol and no consistencyErrors: break
if iter + 1 < maxIter: BroydenStep() # см. 5.6
if iter >= maxIter: raise SimError("MaxSolverIterExceeded")
if consistencyErrorStack: raise ConsistencyError(...)После InnerSolve вызывается ControllerSolver.Solve() (не в режиме «забывания»). Командный интерфейс после каждой команды вызывает root.SolverForget(), в конце цепочки команд — root.Solve(); hold ставит флаг, go снимает его и запускает расчёт[13].
для разработчика5.5. «Забывание → решение»: инвалидация без графа зависимостей
Самое нетривиальное решение ядра. При изменении или удалении спецификации устаревшие рассчитанные значения нужно стереть, но граф зависимостей «кто от кого получен» не хранится. Вместо него — двухпроходная схема[13]:
SolverForget():
_isForgetting = 1
while op = PopForgetOp(): # LIFO
if op is not self:
op.BlockPush(1)
try:
if op.Ignored is None: op.Solve() # "холостой" прогон
for port in op.GetPorts():
if port.GetParentOp() == op: port.UpdateConnection()
op.Forget() # стереть CALCULATED без NEW
while port = PopResetCalcPort(): port.ResetNewCalc()
finally:
op.BlockPush(0)
while port = PopResetFixedPort(): port.ResetNewFixed()
_isForgetting = 0Пока _isForgetting = 1, GetValue() скрывает старые расчётные значения и новые спецификации (5.2). Аппарат, решаясь «вхолостую», выводит только то, что не зависит от изменённой информации, — всё выведенное получает NEW_V. Затем op.Forget() стирает все CALCULATED без NEW_V, то есть именно устаревшее, а стёртое значение через PropertyModified(UNKNOWN) будит соседей. Граф зависимостей нигде не хранится — он «открывается» самими аппаратами. В аппаратах на системах уравнений (теплообменники, реакторы) в фазе «забывания» тяжёлый итерационный расчёт не выполняется, они лишь подтверждают известное — ради скорости интерактивной работы[13].
для разработчика5.6. Рециклы и метод Бройдена
Пользователь разрывает цикл, задавая оценку в порту (~=, статус FIXED|ESTIMATED). Решается (в коде «ошибка» — это оценка минус полученное значение), где — значения, вычисленные схемой при оценках . Переменные нормируются: . Первый шаг — простая подстановка (обратный якобиан — единичная матрица, ); далее — «хороший» метод Бройдена, где — приближение обратного якобиана [13, 14]:
где , . Если , матрица не меняется. Шаг ограничивается: если (по умолчанию 0,05 шкалы), вектор масштабируется целиком до этого предела; доли ограничиваются диапазоном . Новые оценки записываются как FIXED|ESTIMATED[13].
| Параметр | Значение | Смысл |
|---|---|---|
| MaxError | 0,0001 | допуск сходимости рециклов и проверки согласованности (в долях масштаба) |
| MaxNumIterations | 20 | максимум итераций по рециклам |
| MaxControllerIter | 20 | максимум итераций решателя контроллеров |
| MaxRecycleStep | 0,05 | ограничение размера шага (в долях масштаба) |
| RecycleDetails | выкл. | подробный вывод ошибок по каждой переменной рецикла |
Нюанс реализации: ошибка считается как ; если у типа переменной scaleFactor равен None, ошибка = 0, а при нормировке возможно деление на None — для пользовательских типов, участвующих в рециклах, масштаб обязателен[13]. Автоматического выбора разрывных потоков в коде нет.
для разработчика5.7. Контроллеры: внешний слой над InnerSolve
Controller — аппарат без собственной физики с сигнальными портами In (наблюдаемая), Out (управляемая), Target, StepSize, Minimum, Maximum. Его Solve() только регистрирует контроллер в ControllerSolver корневой схемы[13]. Решатель контроллеров работает снаружи основного расчёта: строит обратный якобиан численным дифференцированием (возмущение 0,1 шага; на каждое возмущение выполняется InnerSolve), затем итерирует до MaxControllerIter: шаг с ограничением по StepSize и границам, пересчёт схемы, деление шага на 4 при неубывании ошибки, обновление якобиана по Бройдену. При неудаче — ControllerConvergeFail. Вариант «деление пополам» реализован, но принудительно отключён (выбор сведён к Бройдену), — кандидат на удаление или доработку[13].
для разработчика5.8. Аппараты с внутренним решателем: уравнения и колонна
Большинство аппаратов — простые правила баланса, решаемые за один проход. Но теплообменники, реакторы (CSTR, PFR), трубы и эжекторы наследуют EquationBasedOp: шаблон Solve() — флэш портов; сбор вектора неизвестных; проверка достаточности данных; в режиме «забывания» — выход; отключение лишних спецификаций; решение нелинейной системы (Ньютон–Рафсон, Бройден или метод секущих; линейный поиск с уменьшением шага в 4 раза; запуск с последнего сошедшегося решения). Для ОДУ (PFR) — интеграторы Рунге–Кутты 4-го порядка и Эйлера[13].
Колонна (Tower) реализует алгоритм inside/out Рассела: внешний цикл обновляет упрощённые модели K-значений и энтальпий, внутренний решает систему по ступеням (неизвестные — логарифмы факторов отпарки и отборов; якобиан обновляется по Бройдену). Решение колонны встроено в общий протокол: результаты кладутся в порты со статусом CALCULATED, поэтому остальной солвер видит колонну как обычный аппарат и спецификации могут распространяться по схеме вверх и вниз от неё[1, 13].
для разработчика5.9. Шаблон простого аппарата: Heater, Balance, формулы
def Solve(self):
self.FlashAllPorts() # что можно — сфлэшить
self.balance.DoBalance() # мольный + энергетический баланс
hadPressure = self.CalcDP() # перепад давления
didBalance = 0
while self.FlashAllPorts(): # новый флэш дал новое знание?
self.balance.DoBalance() # → снова баланс
didBalance = 1
if not didBalance: self.balance.DoBalance()
if not hadPressure: self.CalcDP()Паттерн «флэш ↔ баланс до неподвижной точки» повторяется почти во всех аппаратах: аппарат не знает, какие значения известны, — он просто пробует применить правила, а неизвестное остаётся None. Balance объединяет входные и выходные порты и выполняет материальный, мольный (покомпонентно, линейной системой) и энергетический балансы по правилу «ровно одно неизвестное»; при полной информации проверяет согласованность через CheckTolerance. Формулы (Dyadic, Set: ) двунаправлены: при известных результате и одном входе вычисляется второй вход[13].
6Сравнение подходов и ограничения
6.1. Что отличает непоследовательно-модульный солвер
| Свойство | Последовательно-модульный | Уравнение-ориентированный | Непоследовательно-модульный (Sim42) |
|---|---|---|---|
| Порядок расчёта | задан схемой: от сырья к продукту | не нужен: единая система уравнений | динамический: стек аппаратов, получивших новую информацию |
| Где задавать данные | на входах; для выходов нужны алгоритмы оценок | где угодно при выполнении степеней свободы | где угодно; нехватка — None, избыток — проверка |
| Сбой решения | результаты решённых аппаратов остаются доступными | решателя нет — решения нет вовсе | частичный результат: считается всё, что можно |
| Рецикл | итерации по всей схеме | часть общего решения | итерации по всей схеме, только где без них нельзя |
| Отладка | по аппаратам | трудна | видна «волна» расчёта: что, откуда и почему не вычислено |
| Динамика | — | естественна, быстро после первого решения | дорога (итерации по всей схеме) |
| Избыточные данные | — | — | ConsistencyError: расхождение сверх допуска |
| Смена спецификации | — | — | забывание устаревшего + решение, без графа зависимостей |
Столбцы «последовательный» и «уравнение-ориентированный» опираются на перечень достоинств и недостатков в [1, §1.1.1]; прочерк — в источниках не рассматривалось. Последний столбец — по [12], [13]. Внутренний алгоритм HYSYS закрыт, поэтому перенос выводов о Sim42 на HYSYS остаётся гипотезой.
6.2. Ограничения и технический долг Sim42
По результатам статического анализа кода[13] отмечены наблюдения, существенные и для самого алгоритма, и для возможного переноса.
| Наблюдение | Влияние |
|---|---|
| Python 2 (print-оператор, has_key, cPickle, rexec и т. д.) | Нужна миграция на Python 3; rexec отсутствует — заменить механизм версионирования сохранённых случаев |
| Остатки обращений к Numeric без импорта в EquationSolver, Controller, Tower | NameError в чистой среде; следы незавершённой миграции на NumPy [7] |
| Термодинамический провайдер — бинарная библиотека (vmg.pyd, Windows) | Привязка к платформе и лицензии; нужен адаптер к другому провайдеру через ThermoAdmin |
| Глобальные PropertyType (масштабы, границы общие) | Нельзя иметь две симуляции с разными допусками в одном процессе |
| Побочные эффекты в стеках и флагах (_isForgetting, BlockPush) | Затрудняют параллелизм и тестирование; потокобезопасности нет |
| Порядок обхода dict.values() в Python 2 влияет на порядок портов | При миграции порядок станет «порядком вставки»: возможны различия в последовательности расчёта — нужна регрессия |
| Разрыв рециклов только пользователем (~=) | Нет автоматического выбора разрывных потоков (анализа контуров) |
| Исключения в фазе «забывания» проглатываются | Скрываются дефекты; добавить отладочное логирование |
| Монолитные модули (Tower — около 6600 строк, Heater — около 6100) | Разбиение по ответственности перед сопровождением |
6.3. Что стоит сохранить в любом наследнике
- Идея статусов значений и принцип «расчёт не перезаписывает известное, а проверяет» — простая модель защиты от переопределения.
- Инвалидация без графа зависимостей: экономит память и код, хорошо работает при интерактивной работе.
- Нормировка ошибок шкалами типов — единый механизм допусков.
- Слабая связность ядра с термодинамикой, интерфейсами и единицами.
- Протокол
Solve(), позволяющий подключать аппараты любой сложности — от формулы до колонны[13].
7Практические следствия
7.1. Для специалиста по моделированию
None— не ошибка. Это сигнал о недоопределённости: сосчитайте, каких спецификаций не хватает (например, давление на выходе холодильника). Ошибка — сообщение о противоречии или несходимости.- Противоречие — это лишняя спецификация. Если солвер сообщил о конфликте по порту, ищите значение, которое задано дважды разными путями (например, P на выходе и ΔP одновременно).
- Порядок ввода не должен менять результат. Если меняет — подозревайте оценки рецикла, ограничение числа итераций или дефект, а не «особенность» программы. Сравнивайте «изменил A→B» с «сразу B».
- Для рецикла задавайте оценку близко к ожидаемой и проверяйте масштаб переменных: слишком жёсткий допуск или далёкая оценка приводят к
MaxSolverIterExceeded. Перечень несошедшихся портов показывает, где искать. - Отличайте ошибки термодинамики от ошибок солвера. Если одна и та же термодинамическая задача решается стабильно при ручном вводе, но «плывёт» в схеме с рециклом — проблема в солвере; если не решается вовсе — в термо-пакете[12].
- Нулевой расход — «почти ноль». Внутри он заменяется на 10−40 ради избежания деления на ноль, это нужно учитывать при проверке нулевых потоков[12, 13].
7.2. Для разработчика: свойства, которые стоит проверять
Для солвера с динамическим порядком расчёта главная гарантия — детерминизм результата. Поэтому тестировать нужны метаморфические свойства, а не только значения[13]:
- независимость от порядка ввода и от режима hold/go;
- «задал A → изменил на B» равно «сразу B» (проверка фазы «забывания»);
- идемпотентность
Solve(): повторный вызов не меняет состояния; - недоопределённость даёт
Noneбез ошибки, избыточная несовместимая спецификация — ConsistencyError; - сходимость рецикла к одному решению при разных начальных оценках;
- модульные тесты ветвей
SetValue/GetValue,UpdateConnection(оценочный порт, режим «забывания»),UpdateJacobian.
В репозитории Sim42 уже есть регрессионные сценарии (.tst — текстовые команды CLI: рециклы, колонны, реакторы, компрессоры); при миграции их следует прогнать как «золотой» набор — сначала на оригинале, затем на переносе — сравнивая результаты с допуском шкал[13]. Именно эти свойства (кроме последнего) проверяют тесты лаборатории: набор из 17 автоматических тестов к двум моделям запускается встроенным тест-раннером Node.js.
8Заключение
Принцип непоследовательно-модульного солвера укладывается в несколько идей: значение хранится вместе со статусом происхождения; расчёт распространяет частичную информацию в обе стороны и никогда не перезаписывает известное, а лишь сверяет; порядок вычислений определяет стек аппаратов, получивших новое знание; устаревшее находится не обходом графа, а «холостым» прогоном; итерации нужны только там, где информация упирается в замкнутый контур, — и тогда применяется подстановка и метод Бройдена. Эти идеи делают поведение программы предсказуемым для специалиста: результат определяется набором спецификаций, а не историей ввода.
История проекта показывает, что открытый Sim42 создан в среде калгарийской школы моделирования и при участии автора симулятора Hysim; это объясняет, почему описанный принцип совпадает по классу с принципом HYSYS. Одновременно важно не выдавать гипотезу за факт: прямой связи кода Sim42 и HYSYS открытые источники не подтверждают. Интерактивная лаборатория, сопровождающая статью, — самостоятельная реализация идей, проверенная автоматическими тестами; она позволяет увидеть механизм на конкретных числах и проверить свойства, которые обычно принимают на веру.
9Методика и ограничения исследования
- Анализ кода — статический. Исходный код Sim42 (Python 2, бинарные зависимости) не запускался; описание поведения построено по тексту кода и встроенному руководству[12, 13]. Подробно разобраны
Variables.py,Ports.py(ключевые методы),Flowsheet.py,EquationSolver.py,Controller.py,UnitOperations.py, частьBalance.py,Mixer.py,Stream.py,Equation.py,Heater.py(HeaterCooler) и структураTower.Solve. Не разбирались построчно: телаDoMoleBalance/DoVolBalance,SolveFlowMatrix,Heater.HeatExchanger,KineticReactor,PipeSegment, GUI и веб-интерфейс. - Лаборатория — упрощённая модель. Рабочее тело — вода; потоки показаны одной карточкой; рецикл — учебная схема, а не пример из Sim42. Корректность проверена тестами на свойства алгоритма, но не сверкой с оригинальным кодом (он не запускался).
- История — по открытым источникам. Первоисточники — страницы sim42.org из веб-архива (2002–2004) и диссертация; профили участников — их самоописания. Не найдены: оригинал работы Svrcek et al. (1984); независимое подтверждение роли Morris в Hyprotech; судьба проекта после 2004 года (репозиторий создан энтузиастом в 2011 году, активное сопровождение не обнаружено).
- Лицензионные замечания носят справочный характер и не являются юридическим заключением. Рисунки из диссертации не воспроизводятся (на титульном листе указан запрет воспроизведения), схемы выполнены заново; два снимка веб-архива приведены уменьшенными как историческая иллюстрация со ссылкой на источник.
- Связь с HYSYS — гипотеза на уровне людей и идей (раздел 2.3), а не утверждение о коде.
Список источников
- [1]Cota Elizondo R. C. Development of an Open Source Chemical Process Simulator: M.Sc. thesis. Calgary: University of Calgary, Department of Chemical and Petroleum Engineering, 2003. DOI 10.11575/PRISM/15852. https://ucalgary.scholaris.ca/handle/1880/42288 первоисточник
- [2]The History of Sim42 [Электронный ресурс] // sim42.org. Архивная копия Internet Archive от 09.12.2002. https://web.archive.org/web/20021209085237/http://sim42.org/cast_profiles/history.html первоисточник
- [3]Cast Profiles: Craig Morris [Электронный ресурс] // sim42.org. Архивная копия Internet Archive от 09.12.2002. https://web.archive.org/web/20021209020008/http://sim42.org/cast_profiles/craig.html первоисточник
- [4]Cast Profiles: Marco Satyro [Электронный ресурс] // sim42.org. Архивная копия Internet Archive от 09.12.2002. https://web.archive.org/web/20021209015736/http://sim42.org/cast_profiles/marco.html первоисточник
- [5]Simulator42 [Электронный ресурс] // sim42.org. Главная страница. Архивная копия Internet Archive от 08.12.2002. https://web.archive.org/web/20021208125037/http://www.sim42.org/ первоисточник
- [6]The SIM42 Software License, Version 1.0. © 2002 SIM42 Software Foundation. Цит. по: [1], Приложение 1 (дословная копия). первоисточник
- [7]jonathanxavier/sim42 [Электронный ресурс]: репозиторий, импорт Sim42 v2.0.0.0 с SourceForge (OllinTS), 24.07.2011; конвертация Numeric → NumPy, 2011. GitHub. https://github.com/jonathanxavier/sim42 код
- [8]OllinTS — thermodynamic server for chemical simulation [Электронный ресурс] // SourceForge. Проект зарегистрирован 02.10.2007, последнее обновление 28.11.2015, лицензия GPLv2. https://sourceforge.net/projects/ollints/ вторичный
- [9]Aspen HYSYS [Электронный ресурс] // Wikipedia, the free encyclopedia. https://en.wikipedia.org/wiki/Aspen_HYSYS вторичный
- [10]About the Authors: W. Y. Svrcek [Электронный ресурс] // A Real-Time Approach to Process Control. University of Calgary, Department of Chemical and Petroleum Engineering. https://www.ucalgary.ca/ENCH/realtime/authors-svrcek.htm вторичный
- [11]Why not contribute to Ascend? Sim42 license issue [Электронный ресурс] // группа opsim, Google Groups, 18.03.2008. https://groups.google.com/g/opsim/c/v-aJMVa79bE вторичный
- [12]Петрусенко В. С. Как работает солвер Sim42: руководство для бизнес-аналитика (по исходному коду sim/solver, sim/unitop). 16.10.2026. материалы автора
- [13]Петрусенко В. С. Солвер Sim42: техническое описание реализации (структуры данных, алгоритмы, потоки управления, риски). 16.10.2026. материалы автора
- [14]Press W. H., Teukolsky S. A., Vetterling W. T., Flannery B. P. Numerical Recipes: The Art of Scientific Computing. Cambridge University Press (разд. 9.7 — метод Бройдена; на раздел ссылается исходный код Sim42 [13]). вторичный
Глоссарий
- Непоследовательно-модульный солвер (non-sequential modular)
- модульный солвер, распространяющий информацию вперёд и назад по схеме, так что данные можно задавать в любой точке
- Аппарат (unit operation)
- любой объект схемы с правилами расчёта; поток — тоже аппарат
- Порт (port)
- точка аппарата, где хранятся значения и через которую они передаются соседям
- Статус значения
- метка происхождения значения: задано, рассчитано, передано, оценка, неизвестно
- Степени свободы
- число величин, которые нужно задать, чтобы схема считалась однозначно
- Спецификация
- значение, заданное пользователем (статус FIXED)
- Флэш (flash)
- расчёт фазового равновесия и свойств по двум независимым из (T, P, H…) и составу
- Забывание (forget)
- фаза инвалидации: стирание устаревших рассчитанных значений без графа зависимостей
- Рецикл / поток разрыва (tear stream)
- замкнутый контур в схеме; точка, в которой задаётся оценка для итераций
- Метод Бройдена
- квази-ньютоновский метод решения систем нелинейных уравнений без расчёта производных
- Масштаб переменной (scaleFactor)
- величина, на которую нормируются ошибки, чтобы разные переменные сравнивались справедливо
- hold / go
- команды приостановки и возобновления расчёта