Message Boards Message Boards

Scripting the creation of FMU:s

GROUPS:

I recently got a question if it is possible to programmatically create SystemModeler models and export them as FMU:s. It is indeed possible and can be made quite efficient using Mathematica and WSMLink.

A bit of background: FMU:s are models which share a common interface, called FMI (Functional Mock-up Interface). Since these models contain a standard interface, they can be imported and exported to and from a wide variety of different Modeling & Simulation tools. This allows you to take a model with inputs and outputs and connect them with models created in other tools.

exportFMU

For a more detailed introduction, I suggest reading this excellent blog post by @Johan Rhodin and @Henrik Tidefelt. More information can also be found in the documentation.

Now, if parameters are exposed in the model, you will be able to change them after exporting the FMU. The structure of the model however cannot be changed. That is one of the reasons for why one might want to automate the creation of these FMUs, so that by giving the script a few arguments, you can vary the structure of your model.

If we have a model in SystemModeler, we can export it as an FMU from Mathematica with the export command.

Export["HelloWorld.fmu", "IntroductoryExamples.HelloWorld", "FMU"]

The actual models to be exported can be created through various commands. Here I will use WSMConnectComponents, however functions such as WSMCreateModel or WSMSetValues (to change structural constants) could also be of use.

Say we want to create an FMU like this:

geardiagram

It consists of a gear train with a variable input speed and a temperature measurement as output. Potentially, we could want to change the number and type of gears.

We can create a function that does that in Mathematica:

First define the components that don't change between calls:

componentsBase = {
  "w_ref" \[Element] "Modelica.Blocks.Interfaces.RealInput",
  "T" \[Element] "Modelica.Blocks.Interfaces.RealOutput",
  "temp" \[Element] 
   "Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor",
  "speed" \[Element] "Modelica.Mechanics.Rotational.Sources.Speed",
  "inertia" \[Element] 
   "Modelica.Mechanics.Rotational.Components.Inertia",
  "gearbox" \[Element] 
   "Modelica.Thermal.HeatTransfer.Components.HeatCapacitor"}

And the connections:

connectionsBase = {
  "w_ref" -> "speed.w_ref",
  "gearbox.port" -> "temp.port",
  "temp.T" -> "T",
  "speed.flange" -> "gear1.flange_a"}

If we have an argument called components which contain the type of components and the order in which they should appear we can programmatically create them:

componentsVariable = 
 MapIndexed[StringJoin["gear", ToString[#2[[1]]]] \[Element] #1 &, 
  components]

and the connections:

connectionsVariable =
 Join[
  Table[
   StringJoin["gear", ToString[i], ".flange_b"] -> 
    StringJoin["gear", ToString[i + 1], ".flange_a"],
   {i, 1, Length[components] - 1}
   ],
  {StringJoin["gear", ToString[Length[components]], ".flange_b"] -> 
    "inertia.flange_a"},
  MapIndexed[
   If[#1 == "Modelica.Mechanics.Rotational.Components.LossyGear",
     StringJoin["gear", ToString[#2[[1]]], ".heatPort"] -> 
      "gearbox.port",
     Nothing] &,
   components]
  ]

The last part, which checks if the component is of the class "LossyGear" conditionally connects its heat port to the heat port of the gearbox component.

Then we can create the model using:

model = WSMConnectComponents["TempGearTrain",
   Join[componentsBase, componentsVariable],
   Join[connectionsBase, connectionsVariable]
   ];

and export it into the same directory as the notebook using:

Export[
 FileNameJoin[{NotebookDirectory[], "TempGearTrain.fmu"}],
 model,
 "FMU"]

In the attached notebook I collected all of this into a function:

exportGearTrainModel[components_] := ...

This function could for example be used in a Mathematica script and call it with different arguments.

We could also create a small UI that can be used to export the FMU (also included in the attached notebook):

n = 1;
gearList = {"Modelica.Mechanics.Rotational.Components.IdealGear", 
   "Modelica.Mechanics.Rotational.Components.LossyGear"};
gearMenu = 
  Map[# -> ImageResize[WSMModelData[#, "Icon"], 250] &, gearList];
comps = Table[gearList[[1]], n];

Column[{
  Row[{
    "Number of gears: ",
    InputField[
     Dynamic[
      n, (n = #; 
        If[# > Length[comps], 
         comps = PadRight[comps, n, gearList[[1]]]]) &],
     Number,
     FieldSize -> 3]
    }],
  Dynamic[
   If[Not[And[IntegerQ[n], n >= 1]],
    Style["Number of gears must be an integer larger than 0", Red],
    Column[
     Table[With[{j = i},
       Row[{
         StringJoin["Gear ", ToString[j], ": "],
         PopupMenu[Dynamic[comps[[j]]], gearMenu]
         }]
       ], {i, 1, n}]
     ]]],
  Button["Export FMU", exportGearTrainModel[comps[[1 ;; n]]], 
   Method -> "Queued"]}]

model creation ui

Potentially, you can also create a Modelica model that has a variable number of gears and connections. If anyone is interested, I could make another post detailing the Modelica conceptes to use for this. Also, if you have any question about parts of the code, please don't hesitate to ask!

Attachments:
POSTED BY: Patrik Ekenberg
Answer
1 year ago

enter image description here - another post of yours has been selected for the Staff Picks group, congratulations! We are happy to see you at the top of the "Featured Contributor" board. Thank you for your wonderful contributions, and please keep them coming!

POSTED BY: Moderation Team
Answer
1 year ago

Group Abstract Group Abstract