modelicasystemmodeler

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


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.)


Solution

  • 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