In Modelica models, we often use outer components, such as system settings and other common variables, but if I am reading a new model, is there any easy way to allow me to find which component is using outer components?
As an example, the following screenshot is Modelica.Fluid.Examples.HeatingSystem, how could I know which component is using the "system" as an outer component?
I could read the code line by line, but is there an easier way to do this?
Dymola does not provide such a feature in the gui. But with the ModelManagement library its possible to obtain such information. The library is available with the standard Dymola license and pre-installed.
Below you find the function SO.printComponents() which uses the ModelManagement library to obtain all components in a class which match the given criterias.
For your model the printed output is:
Components in Modelica.Fluid.Examples.HeatingSystem using Modelica.Fluid.System as outer:
tank<Modelica.Fluid.Vessels.BaseClasses.PartialLumpedVessel>.heatTransfer<Modelica.Fluid.Interfaces.PartialHeatTransfer>.system
tank<Modelica.Fluid.Interfaces.PartialLumpedVolume>.system
pump<Modelica.Fluid.Machines.BaseClasses.PartialPump>.heatTransfer<Modelica.Fluid.Interfaces.PartialHeatTransfer>.system
...
valve<Modelica.Fluid.Interfaces.PartialTwoPort>.system
...
You see all components in Modelica.Fluid.Examples.HeatingSystem which contain an instance of Modelica.Fluid.System with the prefix outer.
If a component inherited the system instance from a base class via extends, the base class is given between angle brackets in the form <base-class>.
The first component in the output for example means, that the tank component is extending the PartialLumpedVessel class, which contains a component heatTransfer which extends the PartialHeatTransfer which contains the system instance we are looking for.
Note that for multilevel inheritance only the final base class is given, not the full path to it. This is e.g. relevant for the valve, which inherits the system instance from PartialTwoPort via PartialValve and PartialTwoPortTransport.
Background information
The ModelManagement library features some interesting functions in the package ModelManagement.Structure which allow to obtain information about Modelica classes.
The functions are divided into two different types:
ModelManagement.Structure.AST: For these functions, only Modelica code of the selected class is considered. Everything that is inherited is not considered.
ModelManagement.Structure.Instantiated: All functions in this package operate on the translated model
At the first glance ModelManagement.Structure.Instantiated.UsedModels seems a good starting point. But its documentation says:
Optionally disabled components, base-classes, and called functions are not included.
As the Fluid package makes heavy use of inheritance, we would easily miss a component when inheritance is not considered.
Fortunately the Testing library contains a function, which returns all inherited base classes Testing.Utilities.Class.getExtendedClasses. In addition ModelManagement.Structure.AST.ComponentsInClassAttributes allows us to retrieve all components of a given class (but not the inherited ones).
With those two functions, we can build a new function that:
Retrieves all components of a class, including the ones inherited from base classes
Checks if a component matches our search criteria (in your case it should be an outer instance of Modelica.Fluid.System)
Recursively check sub-components of all components, as the usage of system could happen further down in the hierarchy
Here is the code. Additionally a small print function is included, which prints the found components nicely to the terminal.
package SO
function getComponentsUsingClass "Get all components in class c1 which use class c2 as local component"
import ModelManagement.Structure.AST;
import Testing.Utilities.Class.getExtendedClasses;
import Testing.Utilities.Vectors.catStrings;
input String c1 "Full path of class of which components are retrieved";
input String c2 "Full path of class which must be used in the components";
input Boolean isOuter=false "Return only components where c2 is used as an outer instance";
output String cmp[:] "All components in c1 using c2";
protected
String sub[:] "Sub-components";
Boolean baseClass "True if current class is a baseClass of c1 (c1 extends it)";
String prefix "Used to give base classe";
algorithm
cmp :=fill("", 0);
// loop over class c1 and all its base classes
for c in cat(1,{c1}, getExtendedClasses(c1)) loop
baseClass :=c <> c1;
prefix :=if baseClass then "<" + c + ">." else ".";
// loop over all components in the current class
for cmpa in AST.ComponentsInClassAttributes(c) loop
if cmpa.fullTypeName==c2 and cmpa.isOuter==isOuter then
cmp :=cat(1, cmp, {prefix+cmpa.name});
end if;
// if the current component is a Modelica class, obtain all sub-components using c2
// (Mon-Modelica classes would e.g. be the attributes min, max, stateSelect etc. of built in classes)
if cmpa.fullTypeName<>"" then
sub := getComponentsUsingClass(cmpa.fullTypeName, c2, isOuter);
sub :=catStrings(fill(prefix+cmpa.name, size(sub, 1)), sub);
cmp :=cat(1, cmp, sub);
end if;
end for;
end for;
end getComponentsUsingClass;
function printComponents
import Modelica.Utilities.Streams.print;
import DymolaModels.Utilities.Strings.stripLeft;
input String c1="Modelica.Fluid.Examples.HeatingSystem" "Full path of class of which components are retrieved";
input String c2="Modelica.Fluid.System" "Full path of class which must be used in the components";
input Boolean isOuter=true "Return only components where c2 is used as an outer instance";
algorithm
print("Components in " + c1 + " using " + c2 + (if isOuter then " as outer" else "") + ":");
for c in getComponentsUsingClass(c1, c2,isOuter) loop
print(" " + stripLeft(c, "."));
end for;
end printComponents;
annotation (uses(
Modelica(version="4.0.0"),
Testing(version="1.3.1"),
ModelManagement(version="1.2.0"),
DymolaModels(version="1.2")));
end SO;
Related
I have developed a model which uses replaceable records quite extensively. The parameters are passed down to arrays of components. I tried two approaches to do this, but neither of them work in all my test environments SimulationX (SimX), OpenModelica (OM), Dymola and Wolfram SystemModeler (WSM).
Let's consider a simplified package to illustrate my problem. Depending on the chosen sumbodel, the model calculates either the sum or the product of a replaceable record's parameters. I tried two approaches for changing the record within the sub-models.
approach 1: redeclare sub-model record by modification (MOD)
approach 2: redeclare a corresponding inner record to change the outer record within the sub-model (IO)
The package contains an example with 4 models with different record/sub-model combinations for MOD and IO each.
Both approaches work fine in SimX
Dymola correctly simulates with the MOD approach, but produces errors for IO
OM can simulate the model, but does not produce the correct results for all scenarios
WSM produces warning messages concerning the records I mentioned, but simulates the correct results.
I removed the example and annotations from the shown code for readability, but the full package can be downloaded here: https://hessenbox.tu-darmstadt.de/getlink/fiEgNjMEBZpSyJWHKafmoYYS/RedeclareTestPackage.rar
package RedeclareTestPackage "redeclarationTest"
partial record datasetPartial "NewRecord1"
parameter Real whatever;
parameter Integer idontcare;
end datasetPartial;
record datasetA "record A"
extends datasetPartial(
whatever=1.0,
idontcare=2);
end datasetA;
record datasetB "record B"
extends datasetPartial(
whatever=2.0,
idontcare=3);
end datasetB;
partial model partialSubModelType_Mod "NewModel1"
replaceable parameter datasetA subModelDataset constrainedby datasetPartial;
Real C;
end partialSubModelType_Mod;
model subModelTypeMultiply_Mod "sub-model type Multiply"
extends partialSubModelType_Mod;
equation
C=subModelDataset.whatever*subModelDataset.idontcare;
end subModelTypeMultiply_Mod;
model subModelTypeAdd_Mod "sub-model type Add"
extends partialSubModelType_Mod;
equation
C=subModelDataset.whatever+subModelDataset.idontcare;
end subModelTypeAdd_Mod;
partial model partialSubModelType_innerOuter "NewModel1"
outer replaceable parameter datasetA subModelDataset constrainedby datasetPartial;
Real C;
end partialSubModelType_innerOuter;
model subModelTypeMultiply_innerOuter "sub-model type Multiply"
extends partialSubModelType_innerOuter;
equation
C=subModelDataset.whatever*subModelDataset.idontcare;
end subModelTypeMultiply_innerOuter;
model subModelTypeAdd_innerOuter "sub-model type Add"
extends partialSubModelType_innerOuter;
equation
C=subModelDataset.whatever+subModelDataset.idontcare;
end subModelTypeAdd_innerOuter;
model modificationModel "NewModel1"
replaceable parameter datasetA modelData constrainedby datasetPartial;
replaceable subModelTypeAdd_Mod submodel[1,1] constrainedby
partialSubModelType_Mod(each subModelDataset=modelData);
Real test=submodel[1,1].C;
end modificationModel;
model innerOuterModel "NewModel1"
inner replaceable parameter datasetA subModelDataset constrainedby datasetPartial;
replaceable subModelTypeAdd_innerOuter submodel[1,1] constrainedby
partialSubModelType_innerOuter;
Real test=submodel[1,1].C;
end innerOuterModel;
end RedeclareTestPackage;
The error message in OM reads:
Translation Warning [RedeclareTestPackage: 39:3-39:84]: An inner
declaration for outer component subModelDataset could not be found and
was automatically generated.
Strangely, it is only the combination of redeclaring both the dataset and the sub-model with inner/outer which produces the message and fails to simulate correctly. Everything else works fine.
WSM produces error messages for both approaches. The MOD approach producing e.g.:
Warning: In variable or component
'MOD_ADD_1and2.submodel.subModelDataset': Duplicate modification of
element =(untyped) 1.0 and each = (typed)1.0 DAE.PROP(Real,
C_CONST), value: 1.0 on component whatever.
Warning: Duplicate
modification of element =(untyped) 1.0 and each = (typed)1.0
DAE.PROP(Real, C_CONST), value: 1.0 on component whatever.
A WSM example for the IO approach reads:
Warning: Ignoring the modification on outer element:
IO_ADD_1and2.submodel[1,1].subModelDataset .
All models simulate the correct results in WSM though. Dymola only works for the modification examples, the inner/outer redeclaration producing errors like:
Replaceable must be a subtype of constraining class. But different
inner/outer qualifiers for component subModelDataset. In declaration
of component subModelDataset: File:
C:/.../RedeclareTestPackage/RedeclareTestPackage.mo, line 39 Original
declaration of subModelDataset: File:
C:/.../RedeclareTestPackage/RedeclareTestPackage.mo, line 39 Context:
RedeclareTestPackage.partialSubModelType_innerOuter.subModelDataset
If someone could enlighten me, I would be very grateful. My actual model works just fine in SimX, but in my opinion one of the main reasons for Modelica is to develop vendor independent models, to be able to share them with a large community.
Best regards
Julian
Problem Description
I would like to use Non-SI-Units for time in economical modeling (e.g. System Dynamics). While of course I could go for seconds (s) and then use displayUnit there is to my knowledge no nice way to modify displayUnit for time in System Modeler, which I am mainly using.
So, writing a library I would like the user to make a choice of a global type called ModelTime which ideally would be declared as inner and replaceable at some top-level class. Then any component within a model could use the global type to consistently treat any time-related vars.
Minimal Example
The following example shows how I would like to implement this.
package Units declares two Non-SI Unit types( Time_year, Time_month)
package Interfaces contains a partial model class GenericSimulationModel which will be the top-level scope for any model written using the library. It is supposed to provide the type ModelTime as an inner and replaceable class
package Components defines a simple block class that uses ModelTime via an outer definition to define its output y that simple shows time in the globally chosen units of time
model Example ties all of this together to provide an example how any model using the library should work out
Here is the code:
model MinimalExample
package Units
type Time_year = Real(final quantity = "Time", final unit = "yr");
type Time_month = Real(final quantity = "Time", final unit = "mo");
end Units;
package Interfaces
partial model GenericSimulationModel "Top-level model scope providing global vars"
inner replaceable type ModelTime = Years "Set to : Months, Years";
protected
type Years = Units.Time_year;
type Months = Units.Time_month;
end GenericSimulationModel;
end Interfaces;
package Components
block ComponentUsingTime
outer type ModelTime = MinimalExample.Units.Time_year;
output ModelTime y;
equation
y = time;
end ComponentUsingTime;
end Components;
model Example
extends Interfaces.GenericSimulationModel(
redeclare replaceable type ModelTime = Months
);
Components.ComponentUsingTime c;
end Example;
equation
end MinimalExample;
While everything compiles without error in System Modeler and OpenModelica, it unfortunately does not work out: The redeclared type is not used within the component c in the Example model given above.
What can I do to achieve what I want to do?
I have received some feedback on Wolfram Community from someone at Wolfram MathCore (developers of the System Modeler):
The behavior you see for MinimalExample.example and MinimalLibrary.Example are bugs, and from what I can see they should work, I have forwarded them to a developer working on these things.
Connections have at least two important variables. The flow variable and not-flow variable (and then stream stuff but lets not talk about those). For clarity I will reference the Fluid connector and its variables m_flow (flow variable) and p (not flow variable).
When your building components it is important to specify if that component is setting the value for the m_flow or p. For example, you do not want to connect two pressure loss components (sets m_flow) together.
The fluid connectors in MSL are defined as port_a (design inlet) and port_b (design outlet). To specify if a port sets m_flow or not, DynamicPipe opts to use the PartialTwoPort component that has an object (black ellipse) in the icon layer that toggles its visibility based on a parameter (port_a_exposesState) which can be modified when PartialTwoPort is extended (i.e., PartialTwoPortFlow).
However, this feature does not work. The parameter will not change its display when the parameter is changed (i.e., the black ellipse on DynamicPipe never goes away).
Below is a simple example demonstrating the concept. When the model "RunMe" is simulated the parameter showBall = false because number <> 1. However, the ball is still visible.
Partial Model setting the object that should appear/disappear:
partial model ballIcon
// input Boolean showBall; // Tried this as well to no avail.
protected
parameter Boolean showBall = true;
annotation (Icon(coordinateSystem(preserveAspectRatio=false), graphics={
Ellipse(
extent={{-40,40},{40,-40}},
lineColor={0,0,0},
fillPattern=FillPattern.HorizontalCylinder,
fillColor={255,255,0},
visible=showBall)}),
Diagram(coordinateSystem(
preserveAspectRatio=false)));
end ballIcon;
Extending model:
model extendsBallIcon
extends ballIcon(showBall=(number==1));
parameter Real number = 1;
end extendsBallIcon;
Model that should show a ball that appears or disappears based on "number":
model RunMe
extendsBallIcon Ball(number=3)
annotation (Placement(transformation(extent={{-10,-10},{10,10}})));
end RunMe;
Is there something that can be corrected to this approach so the GUI of a component when used (i.e., on the diagram layer) will work? Or do connectors GUI need to capture the flow/not-flow element that is defined by a component to assist in model usage (i.e., change port_a from description design inlet to defined flow variable).
Using Dymola 2017 (tested with the same results on Dymola 2016 as well)
Update:
Knowing the simple case works using DynamicSelect leads me to a real application. The following change appears to cause the icon to not toggle its visiblity.
In extendsBallIcon replacing:
extends ballIcon(showBall=(number==1));
parameter Real number = 1;
with
// Boolean Example
extends ballIcon(showBall=(number==true));
final parameter Boolean number = (modelStructure==Modelica.Fluid.Types.ModelStructure.av_b);
parameter Modelica.Fluid.Types.ModelStructure modelStructure = Modelica.Fluid.Types.ModelStructure.av_b;
Modelica language specification states in section 18.6.6: "Any value (coordinates, color, text, etc) in graphical annotations can be dependent on class variables using the DynamicSelect expression." That is, visible=DynamicSelect(true, showBall) in your example will display the ellipse only if showBall is true.
A program I am working on performs calculations that involve objects that can only have several possible sets of values. These parameter sets are read from a catalogue file.
As an example say the objects are representing cars and the catalogue contains a value set {id: (name, color, power, etc.)} for each model. There are however many of these catalogues.
I use Matlab's unittest package to test if the calculations fail for any of the property combinations listed in the catalogues. I want to use this package, because it provides a nice list of entries that failed. I already have a test that generates a cell array of all ids for a (hardcoded) catalogue file and uses it for parameterized tests.
For now I need to create a new class for each catalogue file. I would like to set the catalogue file name as a class parameter and the entries in it as method parameters (which are generated for all class parameters), but I cannot find a way to pass the current class parameter to the local method for creating the method parameter list.
How can I make this work?
In case it is important: I am using Matlab 2014a, 2015b or 2016a.
I have a couple thoughts.
Short answer is no this can't be done currently because the TestParameters are defined as Constant properties and so can't change across each ClassSetupParameter value.
However, to me creating a separate class for each catalogue doesn't seem like a bad idea. Where does that workflow fallover for you? If desired you can still share code across these files by using a test base class with your content and an abstract property for the catalogue file.
classdef CatalogueTest < matlab.unittest.TestCase
properties(Abstract)
Catalogue;
end
properties(Abstract, TestParameter)
catalogueValue
end
methods(Static)
function cellOfValues = getValuesFor(catalog)
% Takes a catalog and returns the values applicable to
% that catalog.
end
end
methods(Test)
function testSomething(testCase, catalogueValue)
% do stuff with the catalogue value
end
function testAnotherThing(testCase, catalogueValue)
% do more stuff with the catalogue value
end
end
end
classdef CarModel1Test < CatalogueTest
properties
% If the catalog is not needed elsewhere in the test then
% maybe the Catalogue abstract property is not needed and you
% only need the abstract TestParameter.
Catalogue = 'Model1';
end
properties(TestParameter)
% Note call a function that lives next to these tests
catalogueValue = CatalogueTest.getValuesFor('Model1');
end
end
Does that work for what you are trying to do?
When you say method parameters I assume you mean "TestParameters" as opposed to "MethodSetupParameters" correct? If I am reading your question right I am not sure this applies in your case, but I wanted to mention that you can get the data from your ClassSetupParameters/MethodSetupParameters into your test methods by creating another property on your class to hold the values in Test[Method|Class]Setup and then referencing those values inside your Test method.
Like so:
classdef TestMethodUsesSetupParamsTest < matlab.unittest.TestCase
properties(ClassSetupParameter)
classParam = {'data'};
end
properties
ThisClassParam
end
methods(TestClassSetup)
function storeClassSetupParam(testCase, classParam)
testCase.ThisClassParam = classParam;
end
end
methods(Test)
function testSomethingAgainstClassParam(testCase)
testCase.ThisClassParam
end
end
end
Of course, in this example you should just use a TestParameter, but there may be some cases where this might be useful. Not sure if its useful here or not.
Two R questions:
What is the difference between the type (returned by typeof) and the class (returned by class) of a variable? Is the difference similar to that in, say, C++ language?
What are possible types and classes of variables?
In R every "object" has a mode and a class. The former represents how an object is stored in memory (numeric, character, list and function) while the later represents its abstract type. For example:
d <- data.frame(V1=c(1,2))
class(d)
# [1] "data.frame"
mode(d)
# [1] "list"
typeof(d)
# list
As you can see data frames are stored in memory as list but they are wrapped into data.frame objects. The latter allows for usage of member functions as well as overloading functions such as print with a custom behavior.
typeof(storage.mode) will usually give the same information as mode but not always. Case in point:
typeof(c(1,2))
# [1] "double"
mode(c(1,2))
# [1] "numeric"
The reasoning behind this can be found here:
The R specific function typeof returns the type of an R object
Function mode gives information about the mode of an object in the sense of Becker, Chambers & Wilks (1988), and is more compatible with other implementations of the S language
The link that I posted above also contains a list of all native R basic types (vectors, lists etc.) and all compound objects (factors and data.frames) as well as some examples of how mode, typeof and class are related for each type.
type really refers to the different data structures available in R. This discussion in the R Language Definition manual may get you started on objects and types.
On the other hand, class means something else in R than what you may expect. From
the R Language Definition manual (that came with your version of R):
2.2.4 Classes
R has an elaborate class system1, principally controlled via the class attribute. This attribute is a character vector containing the list
of classes that an object inherits from. This forms the basis of the “generic methods” functionality in R.
This attribute can be accessed and manipulated virtually without restriction by users. There is no checking that an object actually contains the components that class methods expect. Thus, altering the class attribute should be done with caution, and when they are available specific creation and coercion functions should be preferred.