Modelica: Is there a possibility to keep old default values in model in case of changed or deleted values in a newer library version? - modelica

Developing a model library in modelica we decided to change and delete some default values. At the moment I'm writing the conversion script to make sure that there are no problems updating the library while using it in another model.
Is there a possibility to keep old default values in case of deleted or changed and renamed values in a newer library version?
I'm aware of the possibility to keep the model values given in the model using the library with convertElement/ convertModifiers.
Example:
Old version of the model in the library exampleLib:
model modelLib
parameter Real exampleVal = 2;
end modelLib;
New version of the model in the library exampleLib:
model modelLib
parameter Real exampleValNew;
end modelLib;
If the value would be given in 'exampleModel' with
model exampleModel
exampleLib.modelLib exampleLibTest(exampleVal = 4)
end exampleModel;
a conversion script like
convertModifiers("exampleLib.modelLib", {"exampleVal"}, {"exampleValNew=%exampleVal%"});
would make sure that the parameter in exampleModel would be kept.
Is there any possibility to keep the default if there is no value given in 'exampleModel'?
In this case the code of the model using the library would be:
model exampleModel
exampleLib.modelLib exampleLibTest;
end exampleModel;
I'm grateful for any help!

In some cases the following might also be an option.
It's a completely different solution as it keeps the default value in the library - but in a different way. That is done by having the following in the new library:
model modelLib
parameter Real exampleValNew(start=2);
end modelLib;
Your old models that don't specify a value will run, but generate a warning.

To rename a parameter or a variable without touching any of its modifiers use
convertElement("exampleLib.modelLib", "exampleVal", "exampleValNew");

I solved the problem with:
convertModifiers("exampleLib.modelLib", fill("",0), {"exampleValNew=2"});

Related

Cannot initialize model with custom CombiTable1D without instance of Modelica CombiTable1D

My objective is to create a 1D/2D lookup table that can read a variable sized array in JSON file without having to specify a statically sized Modelica array parameter.
I started out by trying to extend ExternData to implement a custom table based on a suggestion in a github issue: https://github.com/modelica-3rdparty/ExternData/issues/34#issuecomment-718552210
The steps outlined were:
Create your own copy of a CombiTable, modify it to accept a data access object and a stable name, see code snippet below
Create a duplicate of the ExternalCombiTable1D, which instead references your own CombiTable data object
Create a c-function which reads the data directly from the json and stores it into a table object, which is then passed to the MSL
function ModelicaStandardTables_CombiTable1D_init2
I've implemented all three steps to make a custom CombiTable1D block which populates a dynamic sized table from ExternData JSON functions. Package code below:
https://github.com/vsskanth/ExternData.CustomTable
In this package, there are 3 experiments of relevance to this issue. All experiments compile but only only one experiment works:
ExternData.Examples.JSONTestVariableArrayBroken.mo - Single instance of custom CombiTable1D - does not initialize
ExternData.Examples.JSONTestArrayCombi2DBroken.mo - Custom CombiTable1D and instance of Modelica CombiTable2D - does not initialize
ExternData.Examples.JSONTestVariableArrayWorking.mo - Custom CombiTable1D and a couple instances of Modelica CombiTable1D - works as expected
I am trying to find out why my custom CombiTable1D implementation initializes and runs only when there is at least one instance of Modelica CombiTable1D present in the experiment. I made sure to link ModelicaStandardTables.h and ModelicaStandardTables.lib libraries in my own implementation, checked for warnings in dsbuild.txt and compilation seems to be fine.
For some reason, the constructor function for the custom ExternalCombiTable1D external object (ExternData.Types.ExternalCombiTable1D) does not return and hence the custom CombiTable1D block (ExternData.Tables.CombiTable1D) is not able to initialize when there are no instances of Modelica CombiTable1D tables in the model.
I would appreciate any thoughts on why this is happening and how to overcome it.
IDE - Dymola 2021x
OS - Windows 10
Compiler - Visual Studio 2019
#tbeu has been generous enough to add JSON support to
https://github.com/tbeu/ModelicaTableAdditions which renders this issue moot since I can just use that package with ExternData.
Its still interesting as to why this happens though. Seems like a Dymola translation bug.

Anylogic: can you type a probability distribution into an edit box?

In the simulation window, before the run, I'd like to change some probability distributions (e.g. delay time) by typing, for example, "triangular(5, 20, 15)" into a specific editbox linked to a variable.
I know how to do this with static values, but I couldn't figure out how to do the same with probability distributions.
AnyLogic offers a built-in functionality for that with com.anylogic.engine.database.CodeValue.
It is originally meant that a distribution function stored as text in the internal database can be parsed to java code and executed, but it actually also works without the database and for any kind of code. It is the same idea as in Benjamin's answer, just that you do not need to add any external java library.
Use it like this:
CodeValue myCode = new CodeValue(this,"....java code to be executed");
myCode.execute();
And in your specific case, assuming you have a variable named variableA and an editbox named editbox, use the following to evaluate the expression, get a value and set it for the variable:
CodeValue myCode = new CodeValue(this,"variableA = "+editbox.getText());
myCode.execute();
Obviously, allowing the user to type any command there and running it without a check or error treatment is a bad idea, be aware of that.
This is a Java issue. You need to convert a String (your editbox content) to executable code. Not straight-forward but also not impossible, see Convert String to Code and similar posts.

How to programmatically configure the tunability of model parameters?

I'm porting a large Simulink model from Simulink R2010a → R2017b.
The main model is basically a glue-layer for many interwoven reference models. My objective is to generate a standalone executable out of this main model using Coder.
Parameter tunability in this context is not done via the Signals and Parameters section on the Optimization tab in the Model Configuration Parameters dialog (as is the case in stand-alone models), but rather, via constructing Simulink.Parameter objects in the base workspace, and referencing those in the respective referenced models, or in their respective model workspaces.
Now, AFAIK, in R2010a it was enough to set
new_parameter.RTWInfo.StorageClass = 'Auto';
new_parameter.RTWInfo.CustomStorageClass = 'Define';
to make the parameter non-tunable and convert it into a #define in the generated code. In R2017b, this is no longer allowed; the StorageClass must be 'Custom' if you set a non-empty CustomStorageClass:
new_parameter.CoderInfo.StorageClass = 'Custom'; % <- can't be 'Auto'
new_parameter.CoderInfo.CustomStorageClass = 'Define';
But apparently, this does not make the parameter non-tunable:
Warning: Parameter 'OutPortSampleTime' of '[...]/Rate Transition1' is non-tunable but refers to tunable variables (Simulation_compiletimeConstant (base workspace))
I can't find anything in the R2017b documentation on making parameters non-tunable, programatically; I can only find how to do it in stand-alone models via the dialog, but that's not what I want here.
Can anyone point me in the right direction?
NOTE: Back in the day, Simulink Coder was called Real-Time Workshop (well, Real-time Workshop split into Coder and several other things), hence the difference RTWInfo vs. CoderInfo. Note that RTWInfo still works in R2017b, but issues a warning and gets converted into Coderinfo automatically.
In generated code it should appear as #define, the way you specified it.
https://www.mathworks.com/help/rtw/ug/choose-a-built-in-storage-class-for-controlling-data-representation-in-the-generated-code.html
Btw, yes, it's a bit confusing, because in m-file you specify CustomStorageClass = 'Define';, in GUI you specify Storage class as Define (custom), but in documentation they say Storage Class as Defined.
I am not sure why warning about tunability shows up.

How to get an OrderedSet of OccurrenceSpecifications from a Lifeline in QVTo?

From the diagram on page 570 of the UML spec I concluded that a Lifeline should have the events property, holding an OrderedSet(OcurrenceSpecification). Unfortunately it is not there, at least in the QVTo implementation that I use.
All I have is the coveredBy property, serving me with an (unordered) Set(InteractionFragment). Since my transformation relies on the correct order of MessageOcurrenceSpecification I somehow need to implement myself what I expected to be implemented by the missing events property.
This is what I have so far:
helper Lifeline::getEvents (): OrderedSet(OccurrenceSpecification) {
return self.coveredBy->selectByKind(OccurrenceSpecification)->sortedBy(true);
}
Obviously sortedBy(true) does not get me far, but I don't know any further. Who can help?
All I could find so far were other people struggling with the same issue years ago, but no solution:
https://www.eclipse.org/forums/index.php/m/1049555/
https://www.eclipse.org/forums/index.php/m/1050025/
https://www.eclipse.org/forums/index.php/m/1095764/
Based on the answer by Vincent combined with input from a colleague of mine I came up with the following solution for QVTo:
-- -----------------------------------------------------------------------------
-- Polyfill for the missing Lifeline::events property
query Lifeline::getEvents (): OrderedSet(OccurrenceSpecification) {
return self.interaction.fragment
->selectByKind(OccurrenceSpecification)
->select(os: OccurrenceSpecification | os.covered->includes(self))
->asOrderedSet();
}
I don't know if it is possible, using directly coveredBy to get an ordered collection. As coveredBy is unordered, if you access it directly by the feature, you will have an unpredictible order and if you try to access it using the eGet(...) stuff, the same result will occur.
However, if I understood correctly, there is a "trick" that could work.
It relies on the assumption that each OccurrenceSpecification instance you need is held by the same Interaction that contains the Lifeline and uses the way EMF stores contained elements. Actually, each contained element is always kind of 'ordered' relatively to its parent (and for each collection so EMF can find elements back when XMI references are expressed using the element position in collections). So, the idea would be to access all the elements contained by the Interaction that owns the lifeline and filter the ones that are contained in coveredBy.
Expression with Acceleo
This is easy to write in MTL/Acceleo. In know you don't use it, but it illustrates what the expression does:
# In Acceleo:
# 'self' is the lifeline instance
self.interaction.eAllContents(OccurrenceSpecification)->select(e | self.coveredBy->includes(e))->asOrderedSet()
with self.interaction we retrieve the Interaction, then we get all the contained elements with eAllContents(...) and we filter the ones that are in the self.coveredBy collection.
But it is way less intuitive in QVT as eAllContents(...) does not exist. Instead you have to gain access to eContents() which is defined on EObject and returns an EList which is transtyped to a Sequence (in QVT,eAllContents() returns a ETreeIterator which is not transtyped by the QVT engine).
So, how to gain access to eContents() in the helper? There is two solutions:
Solution 1: Using the emf.tools library
The emf.tools library give you the ability to use asEObject() which casts your object in a pure EObject and give you more methods to access to (as eClass() for example...etc).
import emf.tools; -- we import the EMF tools library
modeltype UML ...; -- all your metamodel imports and stuffs
...
helper Lifeline::getEvents (): OrderedSet(OccurrenceSpecification) {
return self.interaction.asEObject().eContents()[OccurrenceSpecification]->select(e | self.coveredBy->includes(e))->asOrderedSet();
}
Solution 2: Using oclAstype(...)
If for some reason emf.tools cannot be accessed, you can still cast to an EObject using oclAsType(...).
modeltype UML ...; -- all your metamodel imports and stuffs
modeltype ECORE "strict" uses ecore('http://www.eclipse.org/emf/2002/Ecore'); -- you also register the Ecore metamodel
...
helper Lifeline::getEvents (): OrderedSet(OccurrenceSpecification) {
return self.interaction.oclAsType(EObject).eContents()[OccurrenceSpecification]->select(e | self.coveredBy->includes(e))->asOrderedSet();
}
Limitation
Ok, let's be honest here, this solution seems to work on the quick tests I performed, but I'm not a 100% sure that you will have all the elements you want as this code relies on the strong assumption that every OccurrenceSpecification you need are in the same Interaction as the Liteline instance. If you are sure that all the coveredBy elements you need are in the Interaction (I think they should be), then, that's not the sexiest solution, but it should do the work.
EDIT>
The solution proposed by hielsnoppe is more eleguant than the one I presented here and should be prefered.
You are correct. The Lifeline::events property is clearly shown on the diagram and appears in derived models such as UML.merged.uml.
Unfortunately Eclipse QVTo uses the Ecore projection of the UML metamodel to UML.ecore where unnavigable opposites are pruned. (A recent UML2Ecore enhancement allows the name to be persisted as an EAnnotation.) However once the true property name "events" has been pruned the implicit property name "OccurrenceSpecification" should work.
All associations are navigable in both directions in OCL, so this loss is a bug. (The new Pivot-based Eclipse OCL goes back to the primary UML model in order to avoid UML2Ecore losses. Once Eclipse QVTo migrates to the Pivot OCL you should see the behavior you expected.)

Why does the Function Module name of a Smartform change (sometimes)?

Not really critical question, but i'm curious
I am working on a form and sometimes the generated function name is /1BCDWB/SF00000473, and sometimes /1BCDWB/SF00000472. This goes back and forth.
Does anyone know what's the idea behind this? Cuz i'm quite sure it's not a bug (or i might be wrong on that).
It is not a bug. You always have to use SSF_FUNCTION_MODULE_NAME to determine the actual function module name and call it dynamically using CALL FUNCTION l_function_module.
Smartform FMs are tracked by internal numbering and thats saved in the table STXFADMI. You would always notice the different number in Development System if you have deleted any existing Form. Similarly, you would also notice the different number in your Quality system based on the sequence the forms are imported in QAS and the forms as well (as test forms are not migrated to QAS.
Similar behavior is also true for Adobe Form generated FMs.
You need to understand that every smartform has a different interface and hence the automatically generated function module needs to have different import parameters.
Due to this reason the 'SSF*' FMs generate a FM specific for your smartform. The name of the 'generated' FM changes when you migrate from one system to another. And that's the reason why you should use a variable while calling the 'generated' fm and not hardcode it.
The same goes with Adobe form as someone has rightly said in this thread.