modelicaopenmodelicajmodelica

Modelica - how to best adapt import from library to application code


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:

  1. Define a connector LiquidCon7 as an extension of import of standard connector LiquidCon from the library and redeclare the medium to Medium7
  2. Define a partial model EquipmentMedium7 as an extension of import of standard partial model EquipmentMedium and where the connector is redeclared LiquidCon to LiquidCon7
  3. 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;

Solution

  • 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;