How to make use of the unit attribute within a model in Modelica?

本秂侑毒 提交于 2019-12-11 02:48:44

问题


Motivation

Modelica does store units of measurement (e.g. SI units and Non-SI units) as an attribute with regard to a variable. Here is an example for a Non-SI-unit:

type Time_months = Real( quantity = "Time", unit = "mo", displayUnit = "months" )

Since for models in economics it will be rather akward to give rates in seconds, I would like to write a rather general unit conversion function that will allow to convert units of time. So ideally a function to convert to another time base should work with three inputs and one output:

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";

Questions

If we assume that a variable for some time value already has a specific unit attribute (e.g. "mo") it would make sense to use that meta information within a model.

Question 1: How can meta information like unit be accessed within a model?

Ideally something like the following would be great:

String timeBaseA := timeValue.unit;

or

String timeBaseA := getUnit( timeValue ) "some function to read unit information";

Question 2: How can meta information like unit be assigned within a function?

In the example we would of course like to return the output value with the correct unit of time. So ideally we would like to have:

output Real convertedTime( quantity = "Time", unit = strTimeBaseB )

Unfortunately, using an input will give rise to an error as the variability is different: The unit attribute should have constant variability but the input variable has parameter variability. (Using a function - which would be nice - also fails for the same reason.)


回答1:


Regarding Question 1:

I have never used Wolfram SystemModeler, but the Modelica Language Specification 3.4 says in chapter 4.8 (Predefined Types and Classes):

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.

Regarding Question 2:

I think it is only possible to define the unit of a variable on declaration from a literal or from a final parameter - at least this is what I observed in Dymola.

Alternative - use operator records

You could use operator records for your task. This will allow you to store the time in seconds and convert it to what ever needed when the value comes to use.

Operator records allow you to define several function to create them, compare or add them, convert to String, etc.

See the brief example below, where a operator record Time is defined, which can be created with two different constructor functions from seconds or days and can be converted to Strings with day or seconds

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;

Usage:

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

See Modelica Spec 3.4 Chapter 14 "Overloaded Operators" for details.

Note: This was tested with Dymola 2019, not with Wolfram SystemModeler




回答2:


In Modelica usually every variable is computed based on SI units. Then you have displayUnits to plot them in a different unit (not affecting the actual computation).

I don't know about SystemModeler, but in Dymola the conversion between the unit (of computation) and the displayUnit (only for plotting) is handled by a pre-defined script (displayUnit.mos). It can be extended by the user to contain custom displayUnits. The code for the display units related to time is shown below. I extended it to have week (w) additionally to the predefined ones.

// 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);

This can then be selected in plots manually or as the default ´displayUnit´ via Modelica.SIunits.Time t(displayUnit = "w") = ...;

The disadvantage is, that this extension has to be done in a file in the install directory. So it has to be changed again after re-installing the tool or when using a different computer.

If there are numerical reasons to not compute solutions in seconds (e.g. because values would get to big), the solution would be the nominal attribute, which enables a scaling of the variables.

BTW: I think months are not a very good unit of time as they can have 28 to 31 days. That's why I chose weeks in my example.




回答3:


You could use conversion like is done in the MSL, for example the function Modelica.SIunits.Conversions.to_degC which has the signature:

function to_degC
  input Temperature Kelvin "Kelvin value";
  output NonSIunits.Temperature_degC Celsius "Celsius value";
end to_degC;

This works, but you need one such function for each unit you want to convert between (which is why most calculations are done using SI-units).



来源:https://stackoverflow.com/questions/52312562/how-to-make-use-of-the-unit-attribute-within-a-model-in-modelica

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!