Is there a way to load a record in Modelica, and manipulating part of the record directory with a string from an array?
I included the minimal working example below, in summary, I have two different records with parameter values named Cylinder and Board and I have an array containing their names. I would like to access one set of parameter values in a function, depending on the user input "ShapeNumber". So when the user is putting a 1, load the parameters from the Cylinder record, when the user puts a 2, load the parameters from the Board record. Ideally, I would like to add the string "Cylinder" or "Board" into the dot notation for loading the record, as indicated in the code below.
package Test_things
record General_parameters
parameter Real Length "Length in m";
end General_parameters;
record Cylinder_parameters
extends General_parameters;
parameter Real Diameter "Diameter in m";
end Cylinder_parameters;
record Board_parameters
extends General_parameters;
parameter Real Width "Width in m";
parameter Real Depth "Depth in m";
end Board_parameters;
record Parameter_values
constant Test_things.Cylinder_parameters Cylinder(Length=10, Diameter=0.5);
constant Test_things.Board_parameters Board(
Length=20,
Width=1,
Depth=0.01);
constant String[2] ShapesArray = {"Cylinder","Board"};
end Parameter_values;
function Length_tester
input Integer ShapeNumber; //Which shape from within the shape array is tested
output Boolean LongEnough;
Test_things.Parameter_values Values; //Gives Values.Cylinder, Values.Board, Values.ShapesArray
protected
String ShapeName;
Real Length;
algorithm
//Load correct shape
ShapeName := Values.ShapesArray[ShapeNumber];
// This is what I would like: Length := Values.{ShapeName}.Length;
// which results in one of the two lines of code below.
// Length := Values.Cylinder.Length;
// Length := Values.Board.Length;
// This works for now, but has to be adjusted whenever a new shape is used
if ShapeName == "Cylinder" then
Length := Values.Cylinder.Length;
elseif ShapeName == "Board" then
Length := Values.Board.Length;
end if;
if Length > 15 then
LongEnough := true;
else
LongEnough := false;
end if;
end Length_tester;
model Main
Boolean LongEnoughCylinder;
Boolean LongEnoughBoard;
equation
// Test Cylinder
LongEnoughCylinder = Length_tester(1);
// Test Board
LongEnoughBoard = Length_tester(2);
end Main;
end Test_things;
I found the work around with the if-statement, but when I add a new record, I have to update that if-statement in every function I have, rather than only updating the array with the names of the records.
Thank you for any help!
What you try to do is currently not possible, as Modelica does not support dynamic creation of class paths. All class paths must be hard coded.
Therefore your if/else workaround is a legit solution. To keep the maintainability low, you could create a function getLength
with contains the if/else logic and all your other functions will use this one.
Below you can find the solution which I would use. Everything is made as generic as possible, so you can use an arbitrary number of shapes of any kind. To do so, there is the generic shape type Shape
with all possible shape parameters. Then there are the specialized shapes Board
and Cylinder
, which set not needed parameters on 0 in combination with the keyword final
, so users can not modify them. This allows to create arrays of shapes, containing the basic shape type, as well as the specialized shapes. The testLength
function will then be very simple, accepting an array of shapes. The downside of this solution is (for you as developer), that new shapes with new parameter require an update the basic shape type and the specialized shapes.
package Test_things
record Shape "Generic shape with all possible parameters"
import SI = Modelica.SIunits;
// Common parameters
parameter ShapeTypes shapeType "Kind of shape";
parameter SI.Length length;
// Board parameters
parameter SI.Length width;
parameter SI.Length depth;
// Cylinder parameters
parameter SI.Diameter diameter;
end Shape;
type ShapeTypes = enumeration(Cylinder, Board);
record Cylinder "Cylinder shape with unneeded parameters set on final 0"
extends Shape(final shapeType=ShapeTypes.Cylinder, final width=0, final depth=0);
end Cylinder;
record Board "Board shape with unneeded parameters set on final 0"
extends Shape(final shapeType=ShapeTypes.Board, final diameter=0);
end Board;
function testLength "Check if length of a specific shape in an array of shapes is bigger than 15"
input Integer shapeNumber "Shape of interest";
input Shape shapes[:] "Array of shapes";
output Boolean longEnough;
algorithm
longEnough := shapes[shapeNumber].length > 15;
end testLength;
model Main
constant Shape shapes[:] = {
Board(length=20, width=1, depth=0.01),
Cylinder(length=1, diameter=2)};
// note: this works in OpenModelica only with the prefix constant, parameter does not compile
// in Dymola it works with both
Boolean longEnoughCylinder;
Boolean longEnoughBoard;
equation
// Test Cylinder
longEnoughCylinder = testLength(1, shapes);
// Test Board
longEnoughBoard = testLength(2, shapes);
end Main;
end Test_things6;
Some notes on style: