Modelica

3 апрель, 2008 - 11:18Андрей Зубинский

Мы завершаем краткое знакомство с Modelica. На самом же деле это знакомство – всего лишь шаг на самую первую ступеньку к искусству моделирования систем, именно искусству – любая действительно хорошая модель входит в историю культуры с именами ее создателей (например, модель Эберса–Молла).

(Окончание. Начало в номере 8, 2008 г.).

Прежде чем мы перейдем пусть к поверхностному, но все же обзору деталей языка Modelica, давайте попробуем отыскать ответ на традиционный вопрос. А именно, «зачем это надо?». В первой части статьи мы сказали пару слов даже не столько о тех, кому может быть нужен Modelica-инструментарий, сколько о тех, кому он может быть забавен. Теперь же поговорим серьезно (что, впрочем, ни в коей мере не отрицает возможности использования Modelica для забавы как головоломку, например). Способности к построению моделей на базе знаний, полученных изучением уже существующего, и к использованию этих моделей для создания еще не существующего – это фундаментальные свойства того, чем мы, человеки разумные, гордимся. А именно, разума. Соответственно, все придуманные человеком формы описания моделей на протяжении почти всей истории человечества (фактически за исключением несущественных в исторических масштабах последних нескольких десятилетий) были ориентированы на единственного потребителя специфического продукта моделирования – человеческий разум. К сожалению (или к счастью, как знать), машинные заменители или акселераторы мышления не созданы, а существующие акселераторы вычислений крайне слабы в области «понимания», точнее, вообще не способны к последнему. Иными словами, мы имеем накопленный потенциал знаний в виде моделей, форма представления которых не позволяет эффективно использовать этот потенциал при решении задач создания чего-то нового (синтеза); мы, люди, очень плохо, медленно и неточно считаем, сильно устаем от выполнения монотонных действий, и при этом не можем сравнительно легко трансформировать эти задачи в алгоритмы и программы и перепоручить их выполнение машинам только потому, что решение задачи декомпозиции понятных нам высокоуровневых моделей само по себе далеко не тривиально. Речь, само собой, идет не о частных сложностях некоторой «проектной конторы», а о фундаментальной проблеме инженерной системотехники в целом. И это весьма неприятно. Особенно с точки зрения «обычного человека», потребителя того, что эта самая инженерная системотехника довела до масштабов промышленного производства (гордость за достижения науки, безусловно, греет его душу, но только когда ноги – в тепле, сытость – в желудке, и не приходится вздрагивать от мысли, что завтра этого нехитрого счастья уже не будет).

Итак, на самом деле речь идет о решении задач создания унифицированного языка моделирования систем и формирования инфраструктуры, сокращающей дистанции между наукой, инженерией и потребителем. Согласитесь, не игрушечные задачи. И когда мы говорим о Modelica как о языке, не стоит забывать не только о том, для чего он на самом деле создавался и продолжает совершенствоваться, но и о том, что этим «нюансом» Modelica отличается от сотен возникающих то тут то там языков программирования, мотивация авторов которых стала уже идиомой – «just for fun».

Modelica-язык можно охарактеризовать в общепринятых терминах, определяющих свойства машинных языков. Во-первых, Modelica – представитель класса декларативных языков. Класс этот, мягко говоря, не обширен, а уж «чистому» программисту и вообще знаком разве что понаслышке. Принципиальное отличие декларативного языка от прочих (будь то императивных или функциональных) – описание не способа решения задачи, а самой задачи. Впрочем, мы это отличие уже показали в первой части статьи примером Hello World – запись дифференциального уравнения ни в коей мере не является записью программы его решения. Во-вторых, Modelica – объектно-ориентированный язык, в котором классами реализовано все, включая встроенные типы данных. В-третьих, Modelica не соответствует представлениям «пуристов» о декларативности – несмотря на мощные (и основные для класса решаемых задач) механизмы декларативного программирования, язык более чем неплохо поддерживает и императивную, и функциональную технику, и наконец, сверхвысокоуровневое программирование. Последнее, несмотря на сходство с декларативным стилем, заслуживает нескольких слов. Так как в мире системного моделирования заслуженным и бесспорным даже не лидером, а эталоном является пакет MATLAB, приближение к уровню его языка – обязательное условие успеха любой системы моделирования. В Modelica сверхвысокоуровневый синтаксис используется при описании матричных и скалярных операций (т. е. в той области, где в полной мере этот синтаксис нужен). Иными словами, в Modelica вы оперируете матрицами и многомерными массивами с теми же свободой и минимальными затратами символов, которые дают более традиционные языки при работе со скалярными переменными. Правда, в отличие от MATLAB в Modelica массивы строго типизованы – это означает, что во время исполнения количество измерений Modelica-массивов не может изменяться (потому как оно определяет тип), а конкретное значение каждого измерения – динамически изменяемая величина. Таким образом, Modelica позволяет оперировать скалярными величинами (количество измерений равно 0), векторами (количество измерений равно 1, вектор-строка и вектор-столбец не различаются), матрицами (количество измерений равно 2) и массивами с произвольным числом измерений – все это строго определенные фундаментальные типы языка.

К слову, о типах. Их система в Modelica реализована в полном соответствии с теорией Абади–Карделли, желающие могут легко найти статью An Imperative Object Calculus (авторы – Abadi и Cardelli) в Сети.

Теперь немного поговорим об отклонениях от принятых смыслов. Самых фундаментальных. Следует понимать, что объектная ориентация в Modelica, несмотря на «пронизывающий», сквозной характер, относится, по большому счету, исключительно к структурированию моделей. Забудьте о «передаче сообщений» между объектами, о динамическом создании объектов – это все из иных миров. И еще один важный нюанс – в терминах Modelica принято говорить не об экземплярах класса (объектах), а о «компонентах» (components).

Итак, класс языка Modelica. Ничего принципиально «взрывного» в синтаксисе его описания любой программист, знакомый практически с любым ОО-языком, не найдет. Так, класс описывается в более чем традиционной форме (и это замечательно):

class имя_класса
объявления
equation
уравнения
end имя_класса ;

Самое интересное же – семантика производных от класса class типов, и в первую очередь тех, которые описаны в разделе 4.6 спецификации языка – специализированных классов. В их небольшом списке непосредственно относящимися к предметной области (решению задач моделирования) являются connector, model и block. Так как по сути это ключевые понятия Modelica и для понимания их придется выходить за рамки сугубо «программно-языковой» области, ознакомимся с ними подробнее.

Рассмотрим специализированный класс connector. Объекты реального мира, которые мы собираемся моделировать, характеризуются возможностью взаимодействия, и для изучения многих систем, образованных объектами, при ряде допущений справедливым оказывается предположение «точечного», локального взаимодействия объектов (речь, конечно, идет не о математически абстрактной точке). Например, электронную цепь образуют разные компоненты – резисторы, конденсаторы, транзисторы etc, а точками взаимодействия в такой цепи являются выводы компонентов. Или, как их часто называют на инженерном сленге, – коннекторы. Класс сonnector в Modelica как раз и предназначен для моделирования точек взаимодействия физических объектов, выводов компонентов и т. д. Так как все эти сущности не обладают никакой функциональностью, класс connector в Modelica принципиально не может содержать секции описания уравнений, т. е. является сокращенной, ограниченной разновидностью общего class (ранее в терминологии Modelica специализированные классы, такие как connector, принято было называть ограниченными, restricted). Единственная возможность, которую дает класс connector, – описание переменных, измерение (наблюдение) которых доступно в моделируемой системе в данной точке взаимодействия. Например, в электронной схеме, какими бы ни были образующие ее элементы, в любой точке взаимодействия (т. е. на любом выводе элемента) можно фактически измерить две величины – ток и напряжение. Значение тока измеряется «в сечении проводника», т. е. – непосредственно в точке взаимодействия, напряжение же характеризует точку взаимодействия относительно некоторой («общей») точки. Эта разница между измеряемым в точке (потоковым) и относительно другой точки (потенциальным) – фундаментальна, а для описания физических величин различной природы, различающихся таким «нюансом» в Modelica, предусмотрено специальное ключевое слово – префикс flow (поток, измеряемый в точке). Теперь мы можем описать некоторую типовую точку взаимодействия (вывод, контакт) в электронной схеме и заодно ознакомиться с еще одним специфическим типом:

type U = Real(unit="V")
type I = Real(unit="A")
connector P
U vp ;
flow I ip ;
end P;

Итак, наш пример начинается с двух вполне C-подобных по стилю строк, в которых объявлены два новых типа – напряжения U и ток I. На самом деле в этих строках мы создали два объекта специализированного класса type, позволяющего создавать новые типы единственно возможным методом – уточнением встроенных типов. Таким образом, новый тип U (напряжение) – по сути, тот же базовый Real, но с уточненной единицей измерения, обозначаемой символом V. Тип I отличается от него, очевидно, единицей измерения – A. И наконец, мы объявили тип P – точку взаимодействия, характеризующуюся двумя величинами – напряжением vp и током ip, при этом явно указали с помощью префикса flow, что ток ip измеряется в самой точке (по умолчанию наблюдаемая в точке взаимодействия величина в Modelica считается потенциальной).

Итак, мы задали необходимые типы и построили модель точки взаимодействия электрической цепи. Теперь попробуем построить модель некоторого элемента с двумя выводами – не конкретного резистора-конденсатора-индуктивности, а двухвыводного элемента вообще (в теории цепей именуемого двухполюсником). Это построение знакомит нас с преимуществом сочетания объектно-ориентированного структурирования и декларативного программирования:

partial class TwoTerminates
P p1, p2 ;
U dv ;
I ithru;
equation
dv = p1.vp – p2.vp ;
0 = p1.ip + p2.ip ;
ithru = p1.ip ;
end TwoTerminates;

В этом примере минимум незнакомых понятий. Частично определенный класс (class с префиксом partial) содержит не все уравнения, необходимые для решения задачи моделирования объекта – этим он в чем-то похож на абстрактные классы традиционных объектно-ориентированных языков. В секции объявлений данного класса мы объявили два «вывода» моделируемых устройств (p1 и p2), разницу (падение) напряжений на их выходах (dv) и ток через устройство (ithru). В секции уравнений фактически записаны общие для любых двухполюсных элементов фундаментальные законы Кирхгофа (падение напряжения на двухполюснике равно разности напряжений на его выходах, разность втекающего в двухполюсник и вытекающего из него токов равна нулю).

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

class R "модель идеального резистора"
extends TwoTerminates ;
parameter Real Resist(unit="Ohm") "Номинал" ;
equation
Resist*ithru = dv ;
end R;

Здесь мы использовали аналог наследования, указав, что новый класс R «расширяет» (extends) класс TwoTerminates, и детально описав, чем именно «расширяет» – параметром Resist, значением сопротивления моделируемого резистора и известным со школьной скамьи уравнением – законом Ома (падение напряжения dv на резисторе равно произведению протекающего через него тока ithru на сопротивление Resist). Учтите, что ключевое слово parameter указывает и на специфическое, «с отклонениями от правил Modelica», поведение символа «=» в выражении.

Аналогично, расширением базового класса «двухполюсник» TwoTerminates можно описать модели конденсатора (назовем ее нехитро – C), катушки индуктивности (L) и источника энергии, например источника напряжения (Usupp, источник напряжения – идеальное устройство, напряжение на выходах которого не зависит от сопротивления подключенной нагрузки), они будут отличаться только значением и размерностью номинала и уравнением.

Ну а теперь с помощью тех же самых механизмов языка можно перейти от разрозненных моделей к моделируемой схеме и показать, как на самом деле «работают» точки взаимодействия (класс connector) и зачем, собственно говоря, городился весь этот «огород»:

class Scheme1 "очень важная схема"
R r01;
R r02;
R r03;
C c01;
C c02;
L l01;
Usupp VS01;
equation
connect(VS01.p1, r01.p1);
connect(VS01.p1, r02.p1);
connect(VS01.p1, c01.p1);
...
end Scheme1;

В этом «примере» все понятно и без объяснений. В сегменте объявлений класса Scheme1 теперь фактически перечень материалов, необходимых для «сборки» схемы – три резистора r01-r02, два конденсатора с01-с02, одна катушка индуктивности l01 и один источник напряжения VS01. В языковых терминах – это экземпляры объектов соответствующих классов. А вот в сегменте уравнений появляется нечто новое – так называемое «соединительное уравнение», connect equation (раздел 9.1 спецификаций Modelica). Представьте себе, что это уравнение – робот-паяльник, которому вы указываете, какие выводы каких элементов надо соединить. И? И, собственно, все. Схема собрана. Остается только запустить ее на исполнение, и все остальные заботы инструментарий Modelica возьмет на себя. Кто изучал теорию цепей, тот понимает, насколько велика разница между таким описанием схемы и традиционным путем формирования системы уравнений с последующим их решением машинными методами.

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