3

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

gwr
  • 465
  • 6
  • 17
  • I cross-posted a similar (albeit broader) question on Wolfram Community [(1451968)](http://community.wolfram.com/groups/-/m/t/1451968). – gwr Sep 13 '18 at 11:17

3 Answers3

5

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

Community
  • 1
  • 1
marco
  • 5,944
  • 10
  • 22
  • 1
    +1 for making me consider an *operator record*. It looks indeed elegant for conversions. The deeper problem with my kind of models (quite often defined in years) seems to be one of *scaling* as having computations work with 1.585489e-7 instead of 5 units per year seems a bit absurd in the end. – gwr Sep 13 '18 at 14:03
  • 2
    For scaling problems the nominal attribute was invented, maybe this could help. – marco Sep 13 '18 at 14:25
4

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.

Markus A.
  • 6,300
  • 10
  • 26
  • Thank you, that is helpful. Since I have read some post on Wolfram Community about "asking the developers" with regard to including automatic conversions (displayUnit) I am not sure about the applicability in System Modeler. "Seconds" are akward for System Dynamics models (which I am mostly doing) and for a strategic model spanning 10+ years the inaccuracy regarding months is rather negligible (in finance it is far from clear how days in a year are to be counted and distributed: [day count convention](https://en.wikipedia.org/wiki/Day_count_convention) ). – gwr Sep 13 '18 at 12:57
  • Is there a way to modify the built-in variable *time* in this regard (e.g. displayUnit)? – gwr Sep 13 '18 at 14:36
  • Not that I'd know from the code directly. In Dymola at least it can be changed in the simulation setup by changing the unit of the start/stop time. As well it can be changed by right-clicking in the plot and changing "Time Unit"... – Markus A. Sep 13 '18 at 15:21
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).

sjoelund.se
  • 3,468
  • 12
  • 15
  • Well, I wanted to have a bit more convenience there and to avoid numerical errors maybe due to unnecessary operations. But, of course, I may always reach any time base by going `TimeBaseA` >> `Time [s]` >> `TimeBaseB`. – gwr Sep 14 '18 at 13:11