如何在 Modelica 模型中使用单位属性?
How to make use of the unit attribute within a model in Modelica?
动机
Modelica 确实将测量单位(例如 SI 单位和非 SI 单位)存储为变量的属性。以下是非 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:如何在模型中访问像 unit 这样的元信息?
理想情况下像下面这样的东西会很棒:
String timeBaseA := timeValue.unit;
或
String timeBaseA := getUnit( timeValue ) "some function to read unit information";
问题2:unit这样的元信息如何在函数内赋值?
在示例中,我们当然希望 return output 值具有正确的 unit 时间。所以理想情况下我们希望:
output Real convertedTime( quantity = "Time", unit = strTimeBaseB )
不幸的是,使用 input
会产生错误,因为可变性不同:unit 属性应具有 恒定可变性 但输入变量具有 参数可变性 。 (使用函数 - 这会很好 - 也会因同样的原因而失败。)
关于问题 1:
我从未使用过 Wolfram SystemModeler,但 Modelica 语言规范 3.4 在第 4.8 章(预定义类型和 类)中说:
The attributes of the predefined variable types (Real, Integer, Boolean, String) ... cannot be accessed using dot notation, and are not constrained by equations and algorithm sections.
关于问题 2:
我认为只有在从文字或最终参数声明时才能定义变量的单位 - 至少这是我在 Dymola 中观察到的。
备选方案 - 使用操作员记录
您可以将操作员记录用于您的任务。这将允许您以秒为单位存储时间,并在使用该值时将其转换为任何需要的时间。
运算符记录允许您定义多个函数来创建它们、比较或添加它们、转换为字符串等。
参见下面的简短示例,其中定义了一个运算符记录时间,它可以用秒或天的两个不同的构造函数创建,并且可以转换为带有天的字符串或秒
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 规范 3.4 第 14 章“重载运算符”。
注意:这是使用 Dymola 2019 测试的,而不是 Wolfram SystemModeler
在 Modelica 中,通常每个变量都是根据 SI 单位计算的。然后你有 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);
然后可以在绘图中手动选择或通过 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;
这行得通,但是您需要为每个要转换的单位使用一个这样的函数(这就是为什么大多数计算都使用 SI 单位完成的原因)。
动机
Modelica 确实将测量单位(例如 SI 单位和非 SI 单位)存储为变量的属性。以下是非 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:如何在模型中访问像 unit 这样的元信息?
理想情况下像下面这样的东西会很棒:
String timeBaseA := timeValue.unit;
或
String timeBaseA := getUnit( timeValue ) "some function to read unit information";
问题2:unit这样的元信息如何在函数内赋值?
在示例中,我们当然希望 return output 值具有正确的 unit 时间。所以理想情况下我们希望:
output Real convertedTime( quantity = "Time", unit = strTimeBaseB )
不幸的是,使用 input
会产生错误,因为可变性不同:unit 属性应具有 恒定可变性 但输入变量具有 参数可变性 。 (使用函数 - 这会很好 - 也会因同样的原因而失败。)
关于问题 1:
我从未使用过 Wolfram SystemModeler,但 Modelica 语言规范 3.4 在第 4.8 章(预定义类型和 类)中说:
The attributes of the predefined variable types (Real, Integer, Boolean, String) ... cannot be accessed using dot notation, and are not constrained by equations and algorithm sections.
关于问题 2:
我认为只有在从文字或最终参数声明时才能定义变量的单位 - 至少这是我在 Dymola 中观察到的。
备选方案 - 使用操作员记录
您可以将操作员记录用于您的任务。这将允许您以秒为单位存储时间,并在使用该值时将其转换为任何需要的时间。
运算符记录允许您定义多个函数来创建它们、比较或添加它们、转换为字符串等。
参见下面的简短示例,其中定义了一个运算符记录时间,它可以用秒或天的两个不同的构造函数创建,并且可以转换为带有天的字符串或秒
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 规范 3.4 第 14 章“重载运算符”。
注意:这是使用 Dymola 2019 测试的,而不是 Wolfram SystemModeler
在 Modelica 中,通常每个变量都是根据 SI 单位计算的。然后你有 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);
然后可以在绘图中手动选择或通过 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;
这行得通,但是您需要为每个要转换的单位使用一个这样的函数(这就是为什么大多数计算都使用 SI 单位完成的原因)。