Songio
Songio

Reputation: 347

Modelica: how to use replaceable components?

I am trying to build a model that can use two different components (taken from an existing library) for the same scope: in particular a system with an heat exchanger; this heat exchanger can be based on different technologies, e.g. pipes or plates.

I want then define a model with a default replaceable exchanger and say which other technologies may be used.

This is a vey simple exemple of what I tried:

package Test

  // Component 1 original definition in the library
  model COMP1
    parameter Real p1=1 "";
    Real v "";
  equation 
    v=p1*time;
  end COMP1;

  // Component 2 original definition in the library
  model COMP2
    parameter Real p2=1 "";
    Real v "";
  equation 
    v=p2*time;
  end COMP2;

  // Main module (system)
  model MAIN
    parameter Real pm=100 "";
    Real vm "";

    // Redefinition of the component modifing the default values
    model call1 = COMP1(p1=10);
    model call2 = COMP2(p2=20);

    replaceable call1 OBJ
    annotation(choices(
            choice(redeclare call1 OBJ "Default"),
            choice(redeclare call2 OBJ "Variant")));

  equation 
    vm = OBJ.v+pm;
  end MAIN;

  // Application model, using the main model
  model APP
    MAIN mAIN;
  end APP;

end Test;

The model APP run successfully. However, if I open the parameters of APP.mAIN and change OBJ (either selecting "Default" or "Variant"), which results in modifing the APP model as follows:

  model APP
    MAIN mAIN(redeclare call1 OBJ "Default");
  end APP;

I get the following error:

Component type specifier call1 not found

I do not understand what I did wrong, please help.

Upvotes: 1

Views: 1660

Answers (1)

marco
marco

Reputation: 6645

The problem related to the error occurs, because you did not use the correct class path in the choices annotations.

If you select "Default" in APP, you get the following code:

model APP
  MAIN mAIN(redeclare call1 OBJ "Default");
end APP;

Here we see that the class path call1 is not valid. APP can access call1 only by using the relative class path MAIN.call1 or the absolute class path Test.MAIN.call1. So you can fix this problem by using the following annotation:

  replaceable call1 OBJ
  annotation(choices(
          choice(redeclare MAIN.call1 OBJ "Default"),
          choice(redeclare MAIN.call2 OBJ "Variant")));

However, in Dymola the model still does not check, apparently due to the local class definitions in MAIN, under Redefinition of the component modifying the default values. Here you create the new classes call1 and call2. This could be a Dymola bug, as it works in OpenModelica - but the new classes are not necessary. Instead, you can use the original classes and set the parameters with modifier equations in the redeclare statement as follows:

model MAIN
  parameter Real pm=100 "";
  Real vm "";

  replaceable COMP1 OBJ
  annotation(choices(
          choice(redeclare Test.COMP1 OBJ(p1=10) "Default"),
          choice(redeclare Test.COMP2 OBJ(p2=10) "Variant")));
equation 
  vm = OBJ.v+pm;
end MAIN;

Now the model works for no selection and "Default", but when "Variant" is selected, Dymola complains that the redeclared class does not contain the same variables as the original one. This is one of the restrictions you have when you work with replaceable classes (again, OpenModelica has no problem with it, but Dymola warns you that this is not conform to the Modelica language specification)

Alternative approach: MSL-Style with interfaces

I suggest to create an interface model like the Modelica library usually does (e.g. with Modelica.Electrical.Analog.Interfaces.OnePort):

  • You create a partial base model, which contains everything that is common for all variants, called the interface
  • The variants extend the interface and one of the models of the the existing library
  • The class using the variants instantiates one of the variants and constrains the redeclaration to the interface using constrainedby
  • Instead of creating the choices annotations manually you can now create it automatically with the annotation choicesAllMatching

This is how your example could look like. The third-party-components COMP1 and COMP2 are moved to the package ReadOnlyLibrary.

package Test
  // Original definition of Component 1 and 2 in the external library
  package ReadOnlyLibrary
    model COMP1
      parameter Real p1=1 "";
      Real v "";
    equation 
      v=p1*time;
    end COMP1;

    model COMP2
      parameter Real p2=1 "";
      Real v "";
    equation 
      v=p2*time;
    end COMP2;
  end ReadOnlyLibrary;

  // Interface and variants with modified default values
  partial model Call_Interface
    Real v "";
  end Call_Interface;

  model Call1 "Default"
    extends Call_Interface;
    extends ReadOnlyLibrary.COMP1(p1=10);
  end Call1;

  model Call2 "Variant"
    extends Call_Interface;
    extends ReadOnlyLibrary.COMP2(p2=20);
  end Call2;

  // Main module (system)
  model Main
    parameter Real pm=100 "";
    Real vm "";

    replaceable Test.Call1 OBJ constrainedby Test.Call_Interface annotation (choicesAllMatching);

  equation 
    vm = OBJ.v+pm;
  end Main;

  // Application model, using the main model
  model App
    Main main annotation (Placement(transformation(extent={{-12,10},{8,30}})));
  end App;
end Test;

Upvotes: 3

Related Questions