Как использовать атрибут модуля в модели в Modelica?
мотивация
Modelica хранит единицы измерения (например, единицы СИ и не-СИ) в качестве атрибута относительно переменной. Вот пример для не-SI-модуля:
type Time_months = Real( quantity = "Time", unit = "mo", displayUnit = "months" )
Так как для моделей в экономике было бы довольно неудобно давать показатели в секундах, я хотел бы написать довольно общую функцию преобразования единиц, которая позволит преобразовывать единицы времени. Поэтому в идеале функция для преобразования в другую временную базу должна работать с тремя входами и одним выходом:
input Real timeValue "the value of time to be converted";
input String timeBaseA "the time base for timeValue, e.g. \"mo\" ";
input String timeBaseB "the time base to convert to, e.g. \"yr\" ";
output Real convertedTimeValue "the result of the conversion";
Вопросы
Если мы предположим, что переменная для некоторого значения времени уже имеет определенный атрибут единицы (например, "mo"), имеет смысл использовать эту метаинформацию в модели.
Вопрос 1: Как получить доступ к метаинформации, такой как единица измерения, внутри модели?
В идеале было бы что-то вроде следующего:
String timeBaseA := timeValue.unit;
или же
String timeBaseA := getUnit( timeValue ) "some function to read unit information";
Вопрос 2: Как мета-информация, такая как единица измерения, может быть назначена внутри функции?
В примере мы, конечно, хотели бы вернуть выходное значение с правильной единицей времени. Так что в идеале мы хотели бы иметь:
output Real convertedTime( quantity = "Time", unit = strTimeBaseB )
К сожалению, используя input
приведет к ошибке, поскольку изменчивость отличается: атрибут единицы должен иметь постоянную изменчивость, но входная переменная имеет изменчивость параметра. (Использование функции - что было бы неплохо - также не удается по той же причине.)
3 ответа
По вопросу 1:
Я никогда не использовал Wolfram SystemModeler, но спецификация языка Modelica 3.4 говорит в главе 4.8 (Предопределенные типы и классы):
Атрибуты предопределенных типов переменных (Real, Integer, Boolean, String) ... не могут быть доступны с помощью точечной нотации и не ограничены уравнениями и разделами алгоритма.
По вопросу 2:
Я думаю, что можно определить единицу переменной при объявлении только из литерала или из конечного параметра - по крайней мере, это то, что я наблюдал в Dymola.
Альтернатива - использовать записи оператора
Вы можете использовать записи оператора для вашей задачи. Это позволит вам сохранить время в секундах и преобразовать его в то, что когда-либо понадобится, когда значение будет использовано.
Записи оператора позволяют определить несколько функций для их создания, сравнения или добавления, преобразования в строку и т. Д.
См. Краткий пример ниже, где определена запись оператора Time, которая может быть создана с помощью двух разных функций конструктора из секунд или дней и может быть преобразована в строки с помощью дня или секунд.
operator record Time
Integer s "Second";
encapsulated operator 'constructor'
import Time;
function from_s
input Integer s "Seconds";
output Time t(s=s);
algorithm
end from_s;
function from_d
input Integer d "Days";
output Time t(s=d*24*3600);
algorithm
end from_d;
end 'constructor';
encapsulated operator 'String' "Convert Time to string"
import Time;
function formated
input Time t;
input String format = "s" annotation(choices(choice="s" "seconds", choice="d" "days"));
output String str;
algorithm
if format == "d" then
str :=String(t.s/24/3600);
else
str :=String(t.s);
end if;
end formated;
end 'String';
encapsulated operator function '==' "Compare time records"
import Time;
input Time t1;
input Time t2;
output Boolean result "= t1 == t2";
algorithm
result := t1.s == t2.s;
end '==';
end Time;
Использование:
import Modelica.Utilities.Streams.print
t1 = Time(d=12) // create record using day constructor
t2 = Time(s=3600*24*2) // create record using second constructor
print(String(t1, format="s")) // prints 1036800
print(String(t1, format="d")) // prints 12
print(String(t2, format="s")) // prints 172800
print(String(t2, format="d")) // prints 2
Подробности см. В разделе "Modelica Spec 3.4", глава 14 "Перегруженные операторы".
Примечание: это было протестировано с Dymola 2019, а не с Wolfram SystemModeler
В Modelica обычно каждая переменная вычисляется на основе единиц СИ. Тогда у вас есть displayUnits
построить их в другом блоке (не влияя на фактические вычисления).
Я не знаю о SystemModeler, но в Dymola преобразование между unit
(вычислений) и displayUnit
(только для печати) обрабатывается с помощью предварительно определенного сценария (displayUnit.mos
). Пользователь может расширить его, чтобы он содержал пользовательские единицы отображения. Код для единиц измерения, относящихся ко времени, показан ниже. Я продлил его на неделю (w
) в дополнение к предопределенным.
// Syntax:
// defineUnitConversion(<unit>, <derived unit>, <scale>, <opt. offset>);
// Time
defineUnitConversion("s", "ms", 1000);
defineUnitConversion("s", "min", 1/60);
defineUnitConversion("s", "h", 1/3600);
defineUnitConversion("s", "d", 1/86400);
defineUnitConversion("s", "w", 1/604800);
Это может быть выбрано на графиках вручную или по умолчанию "DisplayUnit" через Modelica.SIunits.Time t(displayUnit = "w") = ...;
Недостатком является то, что это расширение должно быть сделано в файле в каталоге установки. Поэтому его необходимо изменить после повторной установки инструмента или использования другого компьютера.
Если есть численные причины не вычислять решения за считанные секунды (например, из-за того, что значения станут большими), решение будет nominal
атрибут, который позволяет масштабировать переменные.
Кстати, я думаю, что месяцы не очень хорошая единица времени, поскольку они могут иметь от 28 до 31 дня. Вот почему я выбрал недели в моем примере.
Вы можете использовать преобразование, как это делается в MSL, например, функцию Modelica.SIunits.Conversions.to_degC, которая имеет подпись:
function to_degC
input Temperature Kelvin "Kelvin value";
output NonSIunits.Temperature_degC Celsius "Celsius value";
end to_degC;
Это работает, но вам нужна одна такая функция для каждой единицы, между которой вы хотите конвертировать (именно поэтому большинство вычислений выполняется с использованием единиц СИ).