I have the following code:
parameter Boolean Powerplant_on_Bus=true
"Activate/Deactivate Conventional Energy Generation on Bus" annotation (
Evaluate=true,
HideResult=true,
choices(__Dymola_checkBox=true),
Dialog(group="Generation"));
parameter Integer n[:]=
"Number of Conventional Powerplants connected to the Bus" annotation (
Dialog(group="Generation", enable=Powerplant_on_Bus));
The result is this:
Parameter Window
With the following code I want to generate an option for choosing a profile table for the Powerplant in the paramter window:
replaceable model Powerplant_Profile =
IntegraNet.HighVoltage.Basics.Tables.ElectricGrid.GenericPowerDataTable "Load-profile data table for Powerplant Generation" annotation (choicesAllMatching=false,Dialog(tab="Powerplant",enable=Powerplant_on_Bus));
The result looks like this:
Parameter Window "Powerplant"
My goal is to generate n options for choosing a powerplant-profile. For example: When I have choosen 5 Powerplants in the first parameter window in the field "n" I want to have 5 fields for choosing a profile in the parameter window "powerplant".
Thanks you for help.
As far as I know dynamically changing the shown parameters directly in the parameter dialog is not possible currently in Dymola.
As a workaround - although I'm not sure if this is exactly what you need - there is the following way to do something similar.
package N_options
package Data
record Default "Default Record"
parameter Real p1=1;
end Default;
record Data1 "Data set 1"
extends Default(p1 = 2.3);
end Data1;
record Data2 "Data set 2"
extends Default(p1 = 4.5);
end Data2;
end Data;
record DataSelection
parameter N_options.Data.Default data annotation (choicesAllMatching);
end DataSelection;
model MyModel
parameter DataSelection vectorizedData[:];
end MyModel;
model MyExample
MyModel myModel(vectorizedData={
N_options.DataSelection(data=N_options.Data.Default()),
N_options.DataSelection(data=N_options.Data.Data1()),
N_options.DataSelection(data=N_options.Data.Data2())})
annotation (Placement(transformation(extent={{-10,-10},{10,10}})));
end MyExample;
end N_options;
This comes down to:
A package containing your data
An (intermediate) record where you can select which data you want to be able to choose
A model containing the data as a parameter
An example
Double-clicking the myModel in the example gives you the following dialog in Dymola:
Clicking the 'Edit' button then shows:
In the second dialog you can select the number of data-sets you want to use and which one should be chosen.
Related
I am trying to make the field 'all names' display all of the selected 'names' (letters in this case) in the order they have been selected (assumed left-to-right). What I have written seems to only be functional if only the first transformation is applied and for the case A + E/F/G. All the rest do not work and I do not understand why.
The code for the first dropdown is this:
% Value changed function: DropDown
function DropDownValueChanged(app, event)
app.FirstName = app.DropDown.Value;
switch app.FirstName
case 'A'
app.FinalNameDrop.Value='A';
case 'B'
app.FinalNameDrop.Value='B';
case 'C'
app.FinalNameDrop.Value='C';
end
end
I was advised by an internet stranger that I can "define a property on the class itself!" and so I tried
properties (Access = private)
Property % Description
FirstName
SecondName
end
However, I am unsure how this can help me. How would I go about making this functional?
just put this in all callback methods:
app.dropDownFinal.Value = sprintf('%s + %s + %s',app.dropDown.Value app.dropDown2.Value app.dropDown3.Value);
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.)
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
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.
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).
I have created a very simple GUI in appdesigner (Matlab) with one dropdown menu. Additionally, I took the code that got generated (under 'Code View' tab) and pasted that in a normal .m file (because, I want to further add some more contents to this code). My question is how can I access certain variable from this self generated code, so that I can play with that value outside of the main class?
For example:
In App class, for this dropdown menu section, following line of code got generated:
app.ColorDropDown = uidropdown(app.UIFigure);
app.ColorDropDown.Items = {'Red', 'Blue', 'Yellow'};
app.ColorDropDown.Position = [277 178 140 22];
app.ColorDropDown.Value = 'Red';
Outside of this app class: Depending upon the value that was selected in this dropdown menu, I want to capture that in a normal variable, and show some other results based on the color selected
Thanks
It seems like the essence of your question is about sharing data between GUIs as you state you want to "show some other results based on the color selected".
Note: MATLAB documentation cautions: Do not use UserData property in apps you create with App Designer.
The documentation goes on to recommend approaches for sharing data among app designer apps and creating multiwindow apps in app designer.
For the sake of completeness, I'll provide a detailed example, different from the MATLAB documentation, of how this can be accomplished entirely within the App Designer.
Setup
I created 2 app designer apps, controlGUI.mlapp and plotGUI.mlapp. Running controlGUI() loads both mlapp files at once and then allows you to control aspects of the plotGUI from callbacks in the controlGUI. Of course, the plotGUI could do anything but I have it changing visual aspects of the plotted data.
Details
For simplicity, I am setting the controlGUI be the main application. I can do this be adding a property in controlGUI named PlotWindow and having it contain the output of calling plotGUI(); the other mlapp. I also have a callback function I recycle for the three dropdowns that updates a DATA property in the plotGUI. This DATA property has a listener attached that fires a callback to update the uiaxes object.
The plotGUI
The magic is really in this object. That is, I created a property named DATA and altered the access. By setting the property access SetObservable = true, we can then add a PostSet event listener, which I stored in a private property called dataChangedListener during the startupFcn.
properties (Access = public, SetObservable=true)
DATA % A struct with xdata, ydata, fields. Other line prop fields ok
end
properties (Access = private)
dataChangedListener % listener object for when DATA is changed.
end
Then the startup function, startupFcn, initializes the DATA property with some (arbitrary) data as struct then adds and enables the PostSet listener.
% Code that executes after component creation
function startupFcn(app, data)
if nargin < 2
% create a default dataset
data = struct();
data.xdata = linspace(0,1,1001);
data.ydata = sin(data.xdata.*2*pi*10);%10hz signal
data.color = app.Axes.ColorOrder(1,:);
end
app.DATA = data;
% plot the data
line(data, 'parent', app.Axes);
% add and enable PostSet event listener for the DATA property
app.dataChangedListener = addlistener(...
app,'DATA','PostSet', ...
#(s,e) app.updatePlot ...
);
app.dataChangedListener.Enabled = true;
end
The PostSet listener calls the method app.updatePlot(), so we have to add this method to our app. This method will get called whenever anything in app.DATA gets modified. So I created the "function" (as the Code Browser calls it) which simply deletes the axes children (the existing line) and uses the low-level version of line() to draw a primitive line based on the app.DATA struct object:
function updatePlot(app)
%clear plot
delete(app.Axes.Children);
% draw the new plot
line(app.DATA, 'parent', app.Axes);
end
Yes, line() will accept a struct which has field names that correspond to valid line properties but I can't seem to find the reference specifically. But if you read about the inputParser object... sorry, getting off topic.
The controlGUI
This object is simpler. It holds the plotGUI in a property, allowing direct access to the plotGUI properties and public methods.
properties (Access = public)
PlotWindow = plotGUI() % holds the reference to the plot window
end
I also created an updatePlot callback method here, different from the plotGUI method, bad naming on my part. Nonetheless, this control method takes the values from controlGUI dropdowns and then modifies the plotGUI DATA struct, stored in app.PlotWindow.DATA. So this one callback is called whenever any of the dropdowns are changed (ValueChangedFcn). Consequently, the PostSet listener for the DATA struct will fire the callback to update the plot accordingly.
% Value changed function: LineColor, LineStyle, MarkerStyle
function updatePlot(app, event)
mStyle= app.MarkerStyle.Value;
lStyle= app.LineStyle.Value;
lColor = app.LineColor.Value;
d = app.PlotWindow.DATA;
switch mStyle
case 'none'
d.marker = 'none';
case 'circle'
d.marker = 'o';
case 'diamond'
d.marker = 'diamond';
case 'point'
d.marker = '.';
end
switch lStyle
case 'none'
d.linestyle = 'none';
case 'solid'
d.linestyle = '-';
case 'dashed'
d.linestyle = '--';
case 'dotted'
d.linestyle = ':';
end
d.color = lColor;
% set the data back into the plotGUI
% The plotGUI's DATA PostSet listener will update the actual plot
app.PlotWindow.DATA = d;
end
You aren't supposed to copy/paste the code outside of the code editor with App Designer. If you want to add your own code, you should add a new function to your class using the "Function" button in App Designer. The app can also call any other matlab function so you could just pass the information from the app to another function by calling it inside the App Designer code
For example see this example from a demo I created. It uses a drop down ChartTypeDropDown to determine what type of chart it should draw. I added a new function called DrawChart which uses the data from the GUI to draw a graph depending on the values selected/entered in to the various boxes.
function results = DrawChart(app)
chartType = app.ChartTypeDropDown.Value;
chartTime = app.TimeEditField.Value;
chartA = app.AEditField.Value;
chartB = app.BEditField.Value;
chartC = app.CEditField.Value;
t = [0:0.1:chartTime];
if strcmp(chartType,'Sin')
y = chartA * sin(chartB*t)+chartC;
elseif strcmp(chartType,'Cos')
y = chartA * cos(chartB*t)+chartC;
elseif strcmp(chartType,'Exp')
y = exp(t);
else
y = x;
end
ax = app.UIAxes;
ax.Title.String = chartType;
plot(ax,t,y);
end
This function is called by the callback linked to a button
function DrawButtonPushed(app, event)
DrawChart(app);
end
Note how I call regular matlab functions such as sin/cos/exp. These could just as easily be a Matlab function that you have written.
I have developed a model for a configurable variable, meaning that the variable may be defined by a parameter, specified via a real input, or left undefined so that it can be solved for.
model ConfigurableReal
"Configurable variable that can be set by parameter, by real input, or left undetermined"
parameter Boolean isSpecifiedByParameter = false
"true if value is specified by paramter"
annotation(Dialog(group="Specify by Parameter", enable = not isSpecifiedByInput));
parameter Boolean isSpecifiedByInput = false
"true if value is specified by real input"
annotation(Dialog(group = "Specify by Real Input", enable=not isSpecifiedByParameter));
parameter Real specifiedValue(unit=unitString) = 0
"specified value to use if isSpecifiedByParameter == true"
annotation(Dialog(group="Specify by Parameter", enable = isSpecifiedByParameter));
// Conditionally declare the realInput
Modelica.Blocks.Interfaces.RealInput realInput if isSpecifiedByInput
annotation (Placement(transformation(extent={{-140,-20},{-100,20}})));
Real value "active value";
protected
Modelica.Blocks.Sources.Constant constantBlock(final k = specifiedValue) if isSpecifiedByParameter
"constant block to hold specified input";
Modelica.Blocks.Interfaces.RealInput value_ "connected value";
equation
value = value_;
// Connect the real input to the value if the real input exists
// This only happens if isSpecifiedByInput==true, because the RealInput is
// conditionally declared above
connect(realInput, value_);
// Connect the constant block to the value if the value is specified by parameter
// This only happens if isSpecifiedByParameter == true, because the Constant is
// conditionally declared above
connect(constantBlock.y, value_);
annotation (Icon(coordinateSystem(preserveAspectRatio=false), graphics={
Rectangle(
extent={{-80,40},{80,-40}},
lineColor={28,108,200},
fillColor={255,255,255},
fillPattern=FillPattern.Solid),
Text(
extent={{-70,30},{70,-32}},
lineColor={28,108,200},
textString="Config
Variable"),
Text(
extent={{-100,80},{100,50}},
lineColor={28,108,200},
fillColor={255,255,255},
fillPattern=FillPattern.Solid,
textString=DynamicSelect("", "value = " + String(value, significantDigits=4))),
Text(
extent={{-100,-50},{100,-80}},
lineColor={28,108,200},
fillColor={255,255,255},
fillPattern=FillPattern.Solid,
textString="%name")}),
Diagram(
coordinateSystem(preserveAspectRatio=false)));
end ConfigurableReal;
Now I would like to extend the model in a way that leverages the quantity/unit types instead of only Reals. Is it possible to assign variable type via a parameter? For instance, is it possible to declare a variable as shown below?
parameter String variableType = "Pressure";
parameter typeOf(variableType) variableType;
If so, I could define the variable types in the ConfigurableReal model to reference the variableType object and write a configurable pressure model by:
model ConfigurablePressure extends ConfigurableReal(final variableType="Pressure");
If not, is there something in Modelica like C# Type Parameters?
In the end, I am really trying to find a way to extend my ConfigurableReal model so that it can be a ConfigurablePressure, ConfigurableTemperature, ConfigurableMassFlowRate,..., containing units in parameter inputs and simulation results without having to copy and paste the model code for each variable type and manually assign the variable types in each class.
Any help is greatly appreciated.
Thanks,
Justin
Justin, what you really want is to use redeclare + replaceable. You mention C# type parameters. Modelica does have something like that, but they don't quite look (syntactically) the same and they have some additional constraints because of the mathematical constraints of Modelica.
If you haven't already read the chapter on Architecture in my book, that will give you a start.
To complete the analogy to C# type parameters, consider this model:
model Circuit
replaceable Resistor R constrainedby Element;
...
end Circuit;
This says that R must have be a subtype of Element. By default, it is a Resistor, but you can change that. Note that Element and Resistor or both types. So you are changing the type of R by changing them. You change the with redeclare (see book).
In that case, the "type parameter" only applies to one variable, R. But you can do this in a more C# like way with:
model Circuit
replaceable model X = Resistor constrainedby Element;
X R1;
X R2;
}
In C#, this would be something like:
class Circuit<X> where X : Element {
X R1;
X R2;
}
(except that C# doesn't defaults as far as I can remember)
I have to run, but I hope that helps.
In our model of a physical system, we modify one flux value by a factor from a look-up table. The LUT itself is selected from a catalog of LUTs based on an integer index. We're currently loading the table data into CombiTable2D components. What is the correct way to select/define the correct LUT? If we have them all as named tables in one input data file, is there a way to pick an LUT based on it's tableName (the CombiTable parameter)? I've been playing with For loops in either equation or algorithm formats, but haven't found a syntax that works yet.
Thanks in advance for the thoughts...
I think it only works with one table per file so you can have an array of tables, something like:
parameter Integer N = 3;
parameter String selectTable = "tab2";
Modelica.Blocks.Tables.CombiTable2D tableArray[N](
each tableOnFile = true,
fileName = {"file1", "file2", "file3"},
tableName={"tab1", "tab2", "tab3"});
// use the tableArray
for i in 1:N loop
// note that N and selectTable need to be known at compile
// time so that the if and the for loop can be expanded
if (tableArray[i].tableName == selectTable)
then
connect(tableArray[i].u1, u1);
connect(tableArray[i].u2, u2);
connect(tableArray[i].y, y);
endif;
end for;