I try to make a parametrised library. I works fine using packages and connectors as parameters.
Using a model as a parameter is also possible.
However, if the model is used in the library to build new models using extend, then it is not allowed, what I understand.
I wonder if the library contains a model with inner/outer style of connector, is it then allowed to let the inner model to be a parameter of the library?
Below a simple example to illustrate the problem. TEST is the library and Fish3b is an application. When I run the Example in the library TEST it all works, but when I have a separate application file it does not.
The error text is: cannot find class declaration for AquariumType running JModelica 2.4
package TEST
model FishType1
outer Real T;
Real health;
equation
health = 30-T;
end FishType1;
model FishType2
outer Real T;
Real health;
equation
health = 32-T;
end FishType2;
package Equipment
model AquariumType
replaceable model FishType
end FishType;
FishType fish;
inner Real T;
equation
T = 29;
end AquariumType;
end Equipment;
// Adapt AquariumType model to actual fish
model Aquarium
import TEST.Equipment.AquariumType;
extends AquariumType(redeclare model FishType=FishType2);
end Aquarium;
// Example
model Example
Aquarium aquarium;
end Example;
end TEST;
And below an example of application code that import from library above
- and here is some error I think.
encapsulated package Fish3b
model FishType3
outer Real T;
Real health;
equation
health = 34-T;
end FishType3;
// Adapt package Equipment with AquariumType model to actual fish
package Equipment3
import TEST.Equipment;
extends Equipment.AquariumType(redeclare model FishType=FishType3);
end Equipment3;
// Example
model Example
import Fish3b.Equipment3;
Equipment3.AquariumType aquarium;
end Example;
end Fish3b;
Thanks “tbeu” for the comment!
I have modified the code so that package Equipment is not extended from a model. The updated code below also represent my underlying “true” problem much better. And it all works.
Thanks!
The updated library file TEST2.mo:
package TEST2
model FishType1
outer Real T;
Real health;
equation
health = 30-T;
end FishType1;
model FishType2
outer Real T;
Real health;
equation
health = 32-T;
end FishType2;
package Equipment
replaceable model FishType
end FishType;
constant Integer dummy = 1;
model AquariumType
FishType fish;
inner Real T;
equation
T = 29;
end AquariumType;
end Equipment;
// Adapt package Equipment to the actual fish
package Equipment1
import TEST2.Equipment;
extends Equipment(redeclare model FishType=FishType1);
end Equipment1;
// Example
model Example
Equipment1.AquariumType aquarium;
end Example;
end TEST2;
And the application code T2_Fish3 that now uses the above library TEST2:
encapsulated package T2_Fish3
model FishType3
outer Real T;
Real health;
equation
health = 34-T;
end FishType3;
// Adapt package Equipment to the actual fish
package Equipment3
import TEST2.Equipment;
extends Equipment(redeclare model FishType=FishType3);
end Equipment3;
// Example
model Example
Equipment3.AquariumType aquarium;
end Example;
end T2_Fish3;
The answer from janpeter works.
Another alternative that avoids introducing models called "FishType1", "FishType3" etc is to use "redeclare model extends" as follows (the Test2 can be unchanged or same change for Equipment1), but it uses more advanced constructs.
encapsulated package T2_Fish3
// Adapt package Equipment to the actual fish
package Equipment3
import TEST2.Equipment;
extends Equipment;
redeclare model extends FishType
outer Real T;
Real health;
equation
health = 32-T;
end FishType;
end Equipment3;
// Example
model Example
Equipment3.AquariumType aquarium;
end Example;
end T2_Fish3;
Additionally it would be possible to move the common "outer Real T" to the base-model FishType leading to:
package TEST2
package Equipment
replaceable model FishType
outer Real T;
end FishType;
constant Integer dummy = 1;
model AquariumType
FishType fish;
inner Real T;
equation
T = 29;
end AquariumType;
end Equipment;
// Adapt package Equipment to the actual fish
package Equipment1
import TEST2.Equipment;
extends Equipment;
redeclare model extends FishType
Real health;
equation
health = 30 - T;
end FishType;
end Equipment1;
// Example
model Example
Equipment1.AquariumType aquarium;
end Example;
end TEST2;
and
encapsulated package T2_Fish3
// Adapt package Equipment to the actual fish
package Equipment3
import TEST2.Equipment;
extends Equipment;
redeclare model extends FishType
Real health;
equation
health = 32-T;
end FishType;
end Equipment3;
// Example
model Example
Equipment3.AquariumType aquarium;
end Example;
end T2_Fish3;
Updated the code with input from Hans Olsson before, but slightly different with the corrections suggested above.
And I manage to avoid “extend from a replaceable model”, but I am not too sure how critical that is, see my previous comment.
Also, I think I would like to keep the clear identity of the different fishes, and also declare them outside package Equipment.
package TEST3
partial model FishBase
outer Real T;
end FishBase;
model FishType1
extends FishBase;
Real health;
equation
health = 30 - T;
end FishType1;
model FishType2
extends FishBase;
Real health;
equation
health = 32 - T;
end FishType2;
package Equipment
replaceable model FishType
end FishType;
constant Integer dummy = 1;
model AquariumType
FishType fish;
inner Real T;
equation
T = 29;
end AquariumType;
end Equipment;
// Adapt package Equipment to the actual fish
package Equipment3
import TEST3.Equipment;
extends Equipment;
redeclare model FishType=FishType2;
end Equipment3;
// Example
model Example
Equipment3.AquariumType aquarium;
end Example;
end TEST3;
And an example of application code:
encapsulated package T3_Fish3
model FishType3
import TEST3.FishBase;
extends FishBase;
Real health;
equation
health = 34-T;
end FishType3;
// Adapt package Equipment to the actual fish
package Equipment3
import TEST3.Equipment;
extends Equipment(redeclare model FishType=FishType3);
end Equipment3;
// Example
model Example
Equipment3.AquariumType aquarium;
end Example;
end T3_Fish3;
Related
I have a package for representing certain media, built similar to but not exactly like Modelica.Media. I want to allow the user to choose between different functions for a particular property without having to change the way the remaining properties are calculated.
The package is:
package myPac
replaceable function prop = F1 constrainedby partialF
annotation (choicesAllMatching=true);
function partialF
input Real x;
output Real y;
end partialF;
function F1
extends partialF;
algorithm
y := x;
end F1;
function F2
extends partialF;
algorithm
y := 2*x;
end F2;
end myPac;
I want to then choose F1 or F2 from inside a model.
model myModel
replaceable package pack1 = myPac(redeclare function prop = pack1.F2);
Real x;
Real y;
equation
y = pack1.prop(x);
end myModel;
works as expected. How can I parametrise the redeclaration of prop so that it can be set from the parameters dialog box?
I was looking of something like:
model myModel2
replaceable package pack1 = myPac(redeclare function prop = if a == 1 then pack1.F1 else pack1.F2);
parameter Integer a = 1;
Real x;
Real y;
equation
y = pack1.prop(x);
end myModel2;
which is clearly incorrect syntax. Also, the choicesAllMatching=true in the declaration of prop does show me a drop-down menu, but there are no choices listed.
How can I parametrise the redeclaration of prop so that it can be set
from the parameters dialog box?
You don't need the additional parameter a to select the functions. Simply click the edit button of prop to get the parameter window of the selected replaceable package, where you can choose between F1 and F2.
I added an animation below to make this clearer.
Also, the choicesAllMatching=true in the declaration of prop does show
me a drop-down menu, but there are no choices listed.
This works for me, as you can see in the animation. Note that I declared the function partialF as partial, so it is not included in the drop-down menu. And I added another choicesAllMatching annotation to the replaceable package pack1 in myModel to create another drop-down for the selection of pack1 (like Hans did in his answer).
You cannot currently have redeclarations depending on parameter-values.
However, you can make a choice between two packages that will appear in the dialog box:
package myPac
replaceable function prop = F1 constrainedby partialF
annotation (choicesAllMatching=true);
function partialF
input Real x;
output Real y;
end partialF;
function F1
extends partialF;
algorithm
y := x;
end F1;
function F2
extends partialF;
algorithm
y := 2*x;
end F2;
end myPac;
package myPac1=myPac(redeclare function prop=myPac.F1);
package myPac2=myPac(redeclare function prop=myPac.F2);
model myModel
replaceable package pack1 = myPac annotation(choicesAllMatching=true);
Real x;
Real y;
equation
y = pack1.prop(x);
end myModel;
I have a question around structuring Modelica code in a re-usable library part and a specific application part. The question concerns medium and equipment that depends on medium and I am inspired by some of the structure in MSL fluid library but I want to make something much smaller and adapted to my needs, but that I also can grow with.
The question is about how to conveniently adapt the library to a new medium defined in the application code. Since there are several models of different pieces of equipment it is natural to have a partial model that defines the type of connectors the equipment should have and then one only make changes in the partial model when adaptation of connectors are needed.
To me it looks like I need a three-step adaptation process of the library, instead of one-step that I hope for. I have a detailed example below that makes it possible ask the question more clearly.
The example is a model for pumping liquid from one vessel to another, i.e. we have a feed tank, a pump and a harvest tank. The liquid medium contains originally two substances and now in the application we want to model seven substances.
In the application code the new medium with seven substances are declared as a package Medium7. The adaptation of the library models for pump, feed and harvest tanks are made in the following three steps:
Define a connector LiquidCon7 as an extension of import of standard connector LiquidCon from the library and redeclare the medium to Medium7
Define a partial model EquipmentMedium7 as an extension of import of standard partial model EquipmentMedium and where the connector is redeclared LiquidCon to LiquidCon7
Define a package Equipment7 as an extension of import of the standard package Equipment where the partial model is redeclared from EquipmentMedium to EquipmentMedium7.
First now a system can be defined in the application code that is tailored to Medium7 using equipment from Equipment7.
—
I wish I could do the adaptation more direct than described above. If I avoid dividing the code in library and application like I do here then it is much more easy to switch from Medium2 to Medium7, by just changing the medium used in the LiquidConType and then that change propagate through the whole system.
When I read text book material on the subject by Tiller and Fritzson or when I try to understand MSL code I find similar structures but still not what I have here. I also think my questions of how to effectively adapt a library to changes in interfaces called for by a new application is not limited to medium, but a much wider range of code.
Just read Tillers paper "Patterns and anti-patterns in Modelica" from 2008 and in section 2.3 "Medium Model Pattern" here is a discussion that relate to my question and think of the last few lines on pg 649.
I just realised that my model structure breaks the Modelica definition, because you are not allowed to extend PumpType, FeedtankType etc from the partial model EquipmentMedium since I need the EquipmentMedium to be replaceable. See Modelica def 3.2 rev 2 section 6.2.1 “Transitively non-Replaceable”.
I would appreciate some comments on the subject and perhaps reading advice. Alternative solutions to my toy-problem is also very wellcome!
Thanks, Jan Peter
I do not know how to append a code file but below I show the application code described above. The library DATA_v04 is straight forward. But note that I need to define models PumpType, FeedtankType etc using extend from a partial model EquipmentMedium...and not allowed.
encapsulated package d4_app7
// ------------------------------------------------------------------------
// Interfaces
// ------------------------------------------------------------------------
import Modelica.Blocks.Interfaces.RealInput;
import Modelica.Blocks.Interfaces.RealOutput;
package Medium7
constant String name = "Seven components" "Medium name";
constant Integer nc = 7 "Number of substances;
type Concentration
= Real[nc] (each min=0, each unit="kg/m3") "Substance conc";
end Medium7;
// ------------------------------------------------------------------------
// Adaptation of library DEMO to Medium7
// ------------------------------------------------------------------------
connector LiquidCon7
import DEMO_v4.LiquidCon;
extends LiquidCon(redeclare package medium=Medium7);
end LiquidCon7;
partial model EquipmentMedium7
connector LiquidConType=LiquidCon7;
end EquipmentMedium7;
package Equipment7
import DEMO_v4.Equipment;
extends Equipment
(redeclare partial model EquipmentMedium=EquipmentMedium7);
end Equipment7;
import DEMO_v4.Control;
// ------------------------------------------------------------------------
// Examples of systems
// ------------------------------------------------------------------------
model Test
LiquidCon7.medium medium;
Equipment7.PumpType pump;
Equipment7.FeedtankType feedtank;
Equipment7.HarvesttankType harvesttank;
Control.FixValueType Fsp(val=0.2);
equation
connect(feedtank.outlet, pump.inlet);
connect(pump.outlet, harvesttank.inlet);
connect(Fsp.out, pump.Fsp);
end Test;
end d4_app7;
I have got some input to this problem to simplify the adaptation of the library code to application both from JModelica and OpenModelica support and I share it here.
The code in the original questions do work in JModelica and OpenModelica but I found it “clumsy” and also actually has a central flaw that people in the OpenModelica community has pointed out to me. I use the partial model EquipmentMedium as parameter for the package Equipment and in the package extend from it. To extend from a replaceable model gives too much flexibility here and gives in OpenModelica 2.0-beta error (but not in earlier versions of OM).
In the updated code below of both library DEMO_v11.mo and application code d11_app7.mo I have simplified the the adaptation of the library and also avoid to extend from a replaceable model. After all it is only the connector LiquidCon that need to be adapted to the application, and to be precise, it is only the medium part of the connector that needs adaptation. So I use Medium as formal parameter of the package Equipment and define the connector inside the package based on the actual Medium. Then the different Equipment use this connector. This way of “extending” the models in the package from a parametrised connector is thus regarded as acceptable while extending from a parametrised model is not.
In the updated code I have also brought in more information in the Medium package and show how to extend that for a new Medium - result from another of my posts here.
For more information about the degree of flexibility there is in parametrised packages in the way, see Modelica def 6.2.1 and 7.3.1 as pointed out by Hans Olsson. There is also section 4.15 in Peter Fritzsons book (2nd ed 2015) that discuss this.
Library code DEMO_v11.mo:
// ---------------------------------------------------------------------------------------------
// Interfaces
// ---------------------------------------------------------------------------------------------
import Modelica.Blocks.Interfaces.RealInput;
import Modelica.Blocks.Interfaces.RealOutput;
package Medium2
replaceable constant String name = "Two components" "Medium name";
replaceable constant Integer nc = 2 "Number of substances";
replaceable type Concentration = Real[nc] "Substance conc";
replaceable constant Real[nc] mw = {10, 20} "Substance weight";
constant Integer A = 1 "Substance index";
constant Integer B = 2 "Substance index";
end Medium2;
package Medium3
import M2 = DEMO_v11.Medium2;
extends M2
(name="Three components" "Medium name",
nc=3 "Number of substances",
mw = cat(1,M2.mw,{30}) "Substance weight",
redeclare type Concentration = Real[nc] "Substance conc");
constant Integer C = 3 "Substance index";
end Medium3;
// ---------------------------------------------------------------------------------------------
// Equipment dependent on the medium
// ---------------------------------------------------------------------------------------------
package Equipment
replaceable package Medium
end Medium;
connector LiquidCon
Medium.Concentration c "Substance conc";
flow Real F (unit="m3/s") "Flow rate";
end LiquidCon;
model PumpType
LiquidCon inlet, outlet;
input RealInput Fsp;
equation
inlet.F = Fsp;
connect(outlet, inlet);
end PumpType;
model FeedtankType
LiquidCon outlet;
constant Integer medium_nc = size(outlet.c,1);
parameter Real[medium_nc] c_in (each unit="kg/m3")
= {1.0*k for k in 1:medium_nc} "Feed inlet conc";
parameter Real V_0 (unit="m3") = 100 "Initial feed volume";
Real V(start=V_0, fixed=true, unit="m3") "Feed volume";
equation
for i in 1:medium_nc loop
outlet.c[i] = c_in[i];
end for;
der(V) = outlet.F;
end FeedtankType;
model HarvesttankType
LiquidCon inlet;
constant Integer medium_nc = size(inlet.c,1);
parameter Real V_0 (unit="m3") = 1.0 "Initial harvest liquid volume";
parameter Real[medium_nc] m_0
(each unit="kg/m3") = zeros(medium_nc) "Initial substance mass";
Real[medium_nc] c "Substance conc";
Real[medium_nc] m
(start=m_0, each fixed=true) "Substance mass";
Real V(start=V_0, fixed=true, unit="m3") "Harvest liquid volume";
equation
for i in 1:medium_nc loop
der(m[i]) = inlet.c[i]*inlet.F;
c[i] = m[i]/V;
end for;
der(V) = inlet.F;
end HarvesttankType;
end Equipment;
// ---------------------------------------------------------------------------------------------
// Control
// ---------------------------------------------------------------------------------------------
package Control
block FixValueType
output RealOutput out;
parameter Real val=0;
equation
out = val;
end FixValueType;
end Control;
// ---------------------------------------------------------------------------------------------
// Examples of systems
// ---------------------------------------------------------------------------------------------
// package Equipment3 = Equipment(redeclare package Medium=Medium3); // Just shorter version
package Equipment3
import DEMO_v11.Equipment;
extends Equipment(redeclare package Medium=Medium3);
end Equipment3;
model Test
Equipment3.Medium medium;
Equipment3.FeedtankType feedtank;
Equipment3.HarvesttankType harvesttank;
Equipment3.PumpType pump;
Control.FixValueType Fsp(val=0.2);
equation
connect(feedtank.outlet, pump.inlet);
connect(pump.outlet, harvesttank.inlet);
connect(Fsp.out, pump.Fsp);
end Test;
end DEMO_v11;
And application code d11_app7.mo:
// ---------------------------------------------------------------------------------------------
// Interfaces
// ---------------------------------------------------------------------------------------------
import Modelica.Blocks.Interfaces.RealInput;
import Modelica.Blocks.Interfaces.RealOutput;
package Medium7
import M2 = DEMO_v11.Medium2;
extends M2
(name = "Seven components" "Medium name",
nc = 7 "Number of substances",
mw = cat(1,M2.mw,{30,40,50,60,70}) "Substance weight",
redeclare type Concentration = Real[nc] "Substance conc");
constant Integer C = 3 "Substance index";
constant Integer D = 4 "Substance index";
constant Integer E = 5 "Substance index";
constant Integer F = 6 "Substance index";
constant Integer G = 7 "Substance index";
end Medium7;
// ---------------------------------------------------------------------------------------------
// Adaptation of library DEMO_v11 to Medium7
// ---------------------------------------------------------------------------------------------
package Equipment7
import DEMO_v11.Equipment;
extends Equipment(redeclare package Medium=Medium7);
end Equipment7;
// ---------------------------------------------------------------------------------------------
// Examples of systems
// ---------------------------------------------------------------------------------------------
import DEMO_v11.Control;
model Test
Equipment7.Medium medium; // Instance not necessary but helpful for user interface
Equipment7.PumpType pump;
Equipment7.FeedtankType feedtank;
Equipment7.HarvesttankType harvesttank;
Control.FixValueType Fsp(val=0.2);
equation
connect(feedtank.outlet, pump.inlet);
connect(pump.outlet, harvesttank.inlet);
connect(Fsp.out, pump.Fsp);
end Test;
end d11_app7;
The following three questions are tied together so please forgive the length of the post.
Using Dymola 2016.
Using a replaceable function call within a model provides the opportunity for the user to have the drop down options. Example below:
model Test1
parameter Real x = 1;
Real y;
replaceable function a=b constrainedby d annotation(choicesAllMatching=true);
equation
y = a(x);
end Test1;
Doing the same replaceable function call within a function seems to not permit the same drop down functionality with the the function is called (i.e. right click call function in package browser. I assume this is intentional as a function is typically called within other functions/models. Example below:
function Test2
input Real x;
output Real y;
replaceable function a=b constrainedby d annotation(choicesAllMatching=true);
algorithm
y :=a(x);
end Test2;
Question #1. Is it possible to use a replaceable function call within a function in the same way you do a model? If so, what is the appropriate syntax? Alternative approach?
Alternatively, a different option would be to perform the replaceable function call in the model and then pass the result to another function that then makes the appropriate call. Example shown below:
model Test3mod
parameter Real x = 1;
Real y;
replaceable function a=b constrainedby d annotation(choicesAllMatching=true);
equation
y = Test3func(x,a);
end Test3mod;
Which passes parameter x and function handle a to:
function Test3func
input Real x;
input ???? a;
output Real y;
algorithm
y :=a(x);
end Test3func;
Question #2. Is this allowable in Modelica and if so, how? Alternative approach?
Question #3. Is it possible to define a string and turn that into a the name of a function. Example below:
model Test4
parameter String 'functionname';
parameter Real x = 1;
Real y;
equation
y = functionname(x);
end Test4;
Thank you in advance! I appreciate your feedback as I continue to explore the use of Modelica.
This should work fine:
model Test3mod
parameter Real x = 1;
Real y;
replaceable function a=b constrainedby d annotation(choicesAllMatching=true);
equation
y = Test3Func(x,a);
end blah;
function Test3func
input Real x;
input d f;
output Real y;
algorithm
y := f(x);
end Test3func;
Does Modelica have something equivalent to C++ templates? I would like to build a class that would process an input of type T, but T would be known only when instantiating the class. I tried to pass the type as a parameter but it gives errors.
You can use replaceable types/classes.
model M
replaceable class C; // = some partial class if you need an interface constrainedby ...
C c;
end M;
model Test
M m1(redeclare class C = C1);
M m2(redeclare class C = C2);
end Test;
See more in the Modelica Specification 4.5 Class declarations https://modelica.org/documents/ModelicaSpec32Revision2.pdf.
Aims: all the derived classes should inherit some default equations from their base class. When the default equation is not valid for a derived class then it should redeclare it.
Here is a somewhat silly minimalistic example.
package Pkg
class Equations
Real x;
end Equations;
class DefaultEquations
extends Equations;
equation
x = 0.0;
end DefaultEquations;
class Base
replaceable DefaultEquations equations extends Equations;
end Base;
end Pkg;
model DuplicateEquations
import Pkg.*;
class CustomizedClass
extends Base;
redeclare Equations equations;
equation
equations.x = 3;
end CustomizedClass;
CustomizedClass customized;
end DuplicateEquations;
For some mysterious reason, the default equation is not overriden:
omc Test.mo Package.mo
class DuplicateEquations
Real customized.equations.x;
equation
customized.equations.x = 0.0;
customized.equations.x = 3.0;
end DuplicateEquations;
Why is this happening? Why are both x=0 and x=3 generated?
If I comment out the package declaration I get only the expected x=3 equation.
The type has to be redeclared, not the component, as pointed out by Adrian Pop.