4

I am working on a (Open)Modelica library for simulating chemical processes. With commercial tools it is a common approach that the mixture configuration (e.g. how many and which chemical components are involved like water, ethanol, ...) is defined once for the feed-stream and then this information is propagated via the streams to all the subsequent unit operations.

Unfortunately, so far I did not manage to use the powerful modelica language to achieve this functionality. The ideal situation would be to model a "Process" like this:

package StreamTest 
  connector Stream_Port
    parameter String componentlist[:];
    Real T, p;
  end Stream_Port;

  model Feed_model
    parameter String componentlist[:];
    Stream_Port Out(componentlist = componentlist);
    Real T, p;
  equation
    Out.T = T;
    Out.p = p;
  end Feed_model;

  model Tank_model
    parameter String componentlist[:];
    Real T,p;
    Stream_Port In(componentlist = componentlist);
  equation
    In.T = T;
    In.p = p;
  end Tank_model;

  model Process
    Feed_model Feed (componentlist = {"A", "B"});
    Tank_model Tank;
  equation
    connect(Feed.Out, Tank.In);
    Feed.Out.T = 200;
    Feed.Out.p = 1;
  end Process;
end StreamTest;

Of course this example is stripped down to the essential since nothing is done with "componentlist". In the next step "componentlist" would be used for calculating physical properties in each model, to define array sizes of molefractions etc.

The idea of propagation is as follows:

  1. The "componentlist" is defined in the instance of the Feed_model only once.
  2. The "componentlist" is set equal to the "componentlist" of the connector Stream_Port.
  3. Feed and Tank are connected and the "componentlist" is transferred to the Port "In" of Tank.
  4. Now the crucial point: Tank takes the "componentlist" from the port "In" and propagates it to the componentlist of Tank. componentlist than is used inside the tank for physical properties and array sizes and may also be propagated via some outlet-port of tank to the next unit.

Unfortunately, this does not work with Openmodelica although there is no syntactical error and I cannot see any point in the language definition this approach is not complying with. The error message is:

[StreamTest: 17:5-17:38]: Failed to deduce dimension 1 of componentlist due to missing binding equation.

Of course, I can define the "componentlist" on the level of the model "process" for each unit:

model Process
  Feed_model Feed (componentlist = {"A", "B"});
  Tank_model Tank (componentlist = {"A", "B"});
equation
  connect(Feed.Out, Tank.In);
  Feed.Out.T = 200;
  Feed.Out.p = 1;
end Process;

But then I have to do this redundantly for each and every instance of a model in "Process" -- not as elegant as I would like to implement larger process models...

Reasons for this behaviour: Parameter propagation only works "downwards" in the declaration part of a model where all the models are instantiated. When we connect models, parameter propagation can only be used for array sizes inside the port-definition. And there it is stuck: The information is not transported to the next connected unit.

Any idea, how to solve this problem? Did I miss something? Although I am quite experienced with some some similar commercial tools I am still on the learning curve of modelica...

BZKN
  • 1,499
  • 2
  • 10
  • 25
JeBa
  • 53
  • 6
  • Interesting question! The closest to what I think you want is probably the `Modelica.Fluid.Interfaces.FluidPort` with its associated number of species `nXi`, but that uses the `replaceable package Medium` approach, which is not quite as elegant as you envision. – Christoph Mar 14 '22 at 15:59
  • Modelon Impact Help center has some related content, here: https://help.modelon.com/latest/guides/propagate_class/ – matth Mar 17 '22 at 14:17

4 Answers4

3

AFAIK, this is not possible with (Open)Modelica alone, but some tools have been amended with an automation capability:

Automatic propagation of fluid models through the ports is not directly possible with the Modelica 3.4 specification, but might be supported by the Modelica tool. For example, in Dymola the option Advanced.MediaPropagation=1 can be set to apply automatic propagation of media models in a circuit.

(https://build.openmodelica.org/Documentation/Modelica.Fluid.UsersGuide.ComponentDefinition.FluidConnectors.html)

Dymola video demonstrating this: https://www.modelon.com/propagating-replaceable-medium-automatically/

The Modelica Specification issue in case you would like to see this included in the language/reopen discussion: https://github.com/modelica/ModelicaSpecification/issues/240

Christoph
  • 5,480
  • 6
  • 36
  • 61
  • Thank you for the additional information! Obviously, I am not alone with the conclusion that there is some missing functionality. The Dymola approach helps but it is only a workaround. What is needed IMHO is clearly a specification of the Modelica language to deal properly with equalities of parameters: - Although the connect is interpreted as an equality between the port-variables it is only checked for equality when it comes to parameters. Why is that the case? - Also if we instantiate a submodel with a given parameter value in the submodel, the parameter is not transported upwards. – JeBa Mar 15 '22 at 12:33
  • > Although the connect is interpreted as an equality between the port-variables it is only checked for equality when it comes to parameters It's a little less simple than that ([link](https://specification.modelica.org/master/connectors-and-connections.html#generation-of-connection-equations)) – Christoph Mar 17 '22 at 06:56
2

In Modelica, media models are not propagated via connections (which I agree would be quite logical). Instead, they are propagated via replaceable packages from the top down. As I said, I understand that propagation of parameters via connectors is more intuitive. But the language semantics of connections only deal with values, not with types and the ability to substitute different abstract media models is pretty important because in most cases different media models are not simply parametric (value changes) but rather topology changes (type changes). This is why the replaceable package (types) mechanism is employed.

Michael Tiller
  • 9,291
  • 3
  • 26
  • 41
  • Thanks for the clarification! Of course the mechanism of replaceable media packages is a consistent and elegant approach and fits in many situations quite well. However, for (chemical) processes, where Modelica is not yet widely used, I would clearly prefer the proposed information flow via connections. Thus, the "parameter part" of ports would have to be interpreted simultaneously as parameter-equations with the parameter/constant declarations of all involved models to build the final structure of the overall system. At least that would be something on my personal wish list... – JeBa Mar 18 '22 at 11:07
  • If this is an important feature for you, there's always the possibility to rekindle the discussion in the [specification issue](https://github.com/modelica/ModelicaSpecification/issues/240) linked above, or a new one. User interest and use cases afaik are welcome, and if you are in a position to assist/support the [development process](https://github.com/modelica/ModelicaSpecification/blob/master/RationaleMCP/DevelopmentProcess.md) (e.g. towards a Modelica Change Proposal), even better. :-) – Christoph Mar 19 '22 at 12:03
1

I think the problem is that the array size is not defined everywhere at compile time. Assuming what you want is to avoid specifying the number of components many times, you could also use the inner/outer mechanism:

package StreamTest
  connector Stream_Port
    parameter String componentlist[:];
    Real T, p;
  end Stream_Port;

  model Feed_model
    outer parameter Integer n_components;
    parameter String componentlist[n_components];
    Stream_Port Out(componentlist = componentlist);
    Real T, p;
  equation
    Out.T = T;
    Out.p = p;
  end Feed_model;

  model Tank_model
    outer parameter Integer n_components;
    parameter String componentlist[n_components];
    Real T,p;
    Stream_Port In(componentlist = componentlist);
  equation
    In.T = T;
    In.p = p;
  end Tank_model;

  model Process
    inner parameter Integer n_components = 3; // only define it once 
    Feed_model Feed (componentlist = {"A", "B", "C"});
    Tank_model Tank;
  equation
    connect(Feed.Out, Tank.In);
    Feed.Out.T = 200;
    Feed.Out.p = 1;
  end Process;
end StreamTest;
Christoph
  • 5,480
  • 6
  • 36
  • 61
  • Thanks for the hint! I know about that mechanism, and it is extremely useful in many situations. But I think it is not specific and flexible enough when it comes to different streams structures: I would have to define everywhere different parameter names for n_components beforehand if the situation could arise that n_components differs -- even if it is the same number. – JeBa Mar 18 '22 at 11:18
  • Yes, that is indeed a limitation. – Christoph Mar 19 '22 at 11:56
1

I read your post and discussion here with great interest and I have struggled with similar questions myself. Important to distinguish between what can be expressed in Modelica from what is common usage and style of Modelica Standard Library.

To exemplify what Michael Tiller wrote about how media models are propagated via replaceable package I enclose the code below that shows an example somewhat similar to yours. Here, a flow F of liquid with concentration vector c from FeedTank to HarvestTank through a pipe with a pressure difference that drives the flow. The concepts of flow and stream are also used. You can read more about a similar example at How to construct a balanced connector for liquids in Modelica?. (Note DEMO_v46 is the same as DEMO_v40 updated with the solution DEMO_v42)

I prefer not to use the term "propagate" and rather say that the EquipmentLib is "adapted" using a replaceable media package. Propagate implies a path and perhaps a sequence of change, while here the whole EquipmentLib is adapted in one statement.

The EquipmentLib contains components like tanks, pipes, sensors, pumps etc where the actual package "Medium" (including number of substances) is called a formal parameter of the EquipmentLib. By changing the "medium-parameter" of the library you get all equipment adapted to the media of your choice. This resembles the way you work in practice in my eyes. For example if you need to build a process that should handle very acid solutions, then you choose tanks, pipes, pumps etc that are specified to handle that, up front from start.

 package DEMO_v46

 //  ---------------------------------------------------------------------------------------------
 //     Interfaces  
 //  ---------------------------------------------------------------------------------------------

     import Modelica.Blocks.Interfaces.RealInput;
     import Modelica.Blocks.Interfaces.RealOutput;

     partial package MediumBase
         constant String name                                   "Medium name";
         constant Integer nc = 1                                "Number of substances";
         replaceable type Concentration = Real[nc]              "Substance conc";       
     end MediumBase;

 //  ---------------------------------------------------------------------------------------------
 //     Medium3 specified  
 //  ---------------------------------------------------------------------------------------------

     package Medium3 
         extends MediumBase
             (name="Three component medium",
              nc=3);
         constant Real[nc] mw = {10, 20, 30}                    "Substance weight"; 
         constant Integer A = 1                                 "Substance index";
         constant Integer B = 2                                 "Substance index";
         constant Integer C = 3                                 "Substance index";
     end Medium3;

     record Medium_data
         constant String name = Medium3.name;
         constant Integer nc = Medium3.nc;
         constant Real[nc] mw = Medium3.mw;
         constant Integer A = Medium3.A;
         constant Integer B = Medium3.B;
         constant Integer C = Medium3.C;
     end Medium_data;

 //  ---------------------------------------------------------------------------------------------
 //     Equipment dependent on the medium but written in a general way 
 //  ---------------------------------------------------------------------------------------------

     package EquipmentLib
         replaceable package Medium = MediumBase         // formal parameter Medium for EquipmentLib
             constrainedby MediumBase;

         connector LiquidCon
             Real p                                              "Pressure"; 
             flow Real F (unit="m3/s")                           "Flow rate";
             stream Medium.Concentration c                       "Substance conc";
         end LiquidCon;

         model PipeType
             LiquidCon inlet, outlet; 
             parameter Real area = 1;
         equation
             inlet.F = -outlet.F;
             outlet.F = area^2*(outlet.p - inlet.p);            // Linearized Bernoulli equation
             for i in 1:Medium.nc loop
                 outlet.c[i] = inStream(inlet.c[i]);
                 inlet.c[i] = inStream(outlet.c[i]);
             end for;
         end PipeType;

        model FeedtankType
           LiquidCon outlet;                                  
             parameter Real p = 0.1                              "Pressure"; 
           parameter Real V_0 (unit="m3") = 100                "Initial feed volume";         
             parameter Real[Medium.nc] c_in (each unit="kg/m3") 
                            = {1.0*k for k in 1:Medium.nc}          "Feed inlet conc";                        
           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;
             outlet.p = p;
           der(V) = outlet.F;               
        end FeedtankType;

       model HarvesttankType
          LiquidCon inlet;
             parameter Real p = 0.0                             "Pressure";                     
           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] m 
                  (start=m_0, each fixed=true)                 "Substance mass";
             Real[Medium.nc] c                                  "Substance conc"; 
           Real V(start=V_0, fixed=true, unit="m3")           "Harvest liquid volume";
        equation
             inlet.p = p;
             inlet.c = c;
           der(V) = inlet.F;
             for i in 1:Medium.nc loop
                 der(m[i]) = actualStream(inlet.c[i])*inlet.F;
                 c[i] = m[i]/V;
             end for;               
        end HarvesttankType;
     end EquipmentLib;
  
 //  ---------------------------------------------------------------------------------------------
 //     Adaptation of package Equipment to Medium3 
 //  ---------------------------------------------------------------------------------------------

     package Equipment
         import DEMO_v46.EquipmentLib;
         extends EquipmentLib(redeclare package Medium=Medium3);
     end Equipment;

 //  ---------------------------------------------------------------------------------------------
 //     Examples of systems 
 //  ---------------------------------------------------------------------------------------------

     model Test
         Medium_data medium;
         Equipment.FeedtankType feedtank;
         Equipment.HarvesttankType harvesttank;
         Equipment.PipeType pipe;
     equation
         connect(feedtank.outlet, pipe.inlet);
         connect(pipe.outlet, harvesttank.inlet);
     end Test;

 end DEMO_v46;
janpeter
  • 681
  • 8
  • 22
  • 1
    Thanks for your reply, that was very helpful, especially the link to the other discussion. My conclusion is that I most probably will switch to the "medium-approach". Although my personal opinion is that this is a much steeper learning curve for my students compared to just providing a string array with the names of the chemical components (how I have implemented it right now with some success). My wish still would be to propagate constant/parameter information via streams from one model to the other... – JeBa Apr 05 '22 at 09:08
  • Good! A key for you is to early get used to the Modelica-concepts: flow and stream. I sense that you use "stream" in some more general way, not sure, but confuses. Sure, you can model this basic "chemical engineering examples" in different ways. My experience is that the Modelica text-books lacks these basic chemical engineering examples and start-of at somewhat more complicated examples which can be harder to learn from. You may find some further inspiration here: https://www.openmodelica.org/images/M_images/OpenModelicaWorkshop_2021/Design%20aspects%20of%20BPL%20v4b.pdf – janpeter Apr 05 '22 at 15:30
  • 1
    Thanks for the link to your very good presentation! It is funny too read in the paper of Franke "The used medium has CURRENTLY to be defined for every component. It would be nicer if he medium was defined at one source and the medium definition would then be propagated through the connection structure". Unfortunately, CURRENTLY from 2009 is still the status quo... Don't get me wrong: Modelica/Openmodelica is great, but as you wrote: It is not directly focused on chemical engineering and thus the needs of our domain are not completely covered. But of course almost everything can be modeled. – JeBa Apr 07 '22 at 07:54
  • Very good! I think Modelica is "under-used" in the chemical engineer community and have tried to understand why. From start of conception of Modelica language there have been some chemical engineers around. MSL Media contain a lot of "knowledge". The concept of "stream" that came around 2008 is an advanced concept that help to handle flow in T-valves and to handle back-flow right etc, see https://doc.modelica.org/Modelica%203.2.3/Resources/Documentation/Fluid/Stream-Connectors-Overview-Rationale.pdf You have my e-mail address in the presentation above for further contact – janpeter Apr 07 '22 at 12:56