如何在 Modelica 中构建液体平衡连接器?

How to construct a balanced connector for liquids in Modelica?

post 的状态:

200313 得到了代码 DEMO_v42 的答案,我接受赏金!

200310 我评论了昨天推荐的两篇关键论文。还是不明白怎么更新DEMO_v41.

200309 我想强调一下,关键问题是如何在代码中引入流的概念DEMO_v41(如果可能的话),并通过这种方式使连接器平衡。作为浓度的变量 c 应该声明为 stream,但是方程式应该如何用 inStream 或 actualStream 更新——我很乐意看到!

200226 添加了 post 示例 DEMO_v41,它是一个简化的代码,我希望代码比第一个 DEMO_v40.

更具可读性

200225 我对给出的答案进行了一些评论,并试图让读者关注实际问题,但收效甚微。

200224 我得到了一些关于 post 的一般和详细信息。详细的评论价值较低,部分原因是误解了问题。 Rene 的更笼统的回答很好,但过于笼统。在我考虑使用 Modelica.Media 等之前,我真的很想通过小例子了解如何使用流的概念。这是一个学习过程。


我想知道如何正确定义液体的连接器,该液体在溶液中具有多种不同浓度的成分,然后该溶液具有流速。液体中的压力可以忽略不计。 我长期使用的标准连接器是:

connector LiquidCon
   nc=5;
   Real c[nc]        “Component concentrations”;
   flow Real F       “Flow rate”;
end LiquidCon;

连接器在 JModelica 和 OpenModelica 中运行良好,但我在 OpenModelica 中收到连接器不平衡的警告。在 Modelica 语言规范的第 9.3.1 节中,我看到我的构造实际上是不合法的,请参见 https://www.modelica.org/documents/ModelicaSpec34.pdf。如何制作满足需求的连接器?

我花了一些时间阅读了 Fritzons 书第 2n 版中关于“流”概念的第 5.10 章,但我需要更详细地研究它。

我的简单连接器发出警告的原因是,当您声明流变量时,编译器假定另一个变量是该流变量的潜在变量,即至少流和潜在变量的数量必须是在连接器中相同。那么当然在我的例子中,组件浓度不是潜在变量,而是编译器无法检测到的。

在第 5.10 章的介绍部分,“流”概念的范围似乎是“......具有相关属性的双向物质流的应用......”。在我的应用领域,我怀疑我需要考虑双向流。这意味着使用流是一种“矫枉过正”。但这似乎也暗示我也不应该使用“流”的概念,这有点可惜。我们真的应该停止使用吗这里是“流”的概念?

无论如何,我已经尝试整理一个比 Fritzson 书中关于这个主题的更基本的示例,以了解如何使用“流”的概念以及计算时间等方面的开销你得到。在下面的示例中,我模拟了从进料罐到收获罐的液体流动。流量现在由压力差控制。代码 DEMO_v41 起作用并发出连接器不平衡的警告。如果我现在声明底物浓度 c 为“流”,我现在应该如何使用 inStream 和 actualStream 更新代码以使其以相同的方式工作,但现在使用此平衡连接器?

package DEMO_v41

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

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

//  ---------------------------------------------------------------------------------------------
//     Equipment
//  ---------------------------------------------------------------------------------------------

    package EquipmentLib

        connector LiquidCon
            Real P                                             "Pressure"; 
            flow Real F                                        "Flow rate";
            Real c                                             "Substance conc";
        end LiquidCon;

        model PipeType
            LiquidCon inlet, outlet;
            parameter Real area = 1;
        equation
            inlet.F = -outlet.F;
            outlet.F = -area^2*(inlet.P - outlet.P);           // Linearized Bernoulli equation
            outlet.c = inlet.c;
        end PipeType;

        model FeedtankType
            LiquidCon outlet;                                  
            parameter Real P = 0.1                             "Pressure"; 
            parameter Real V_0 = 100                           "Initial feed volume";         
            parameter Real c_in = 1.0                          "Feedtank conc"; 
            Real V(start=V_0, fixed=true)                      "Feed volume";
        equation    
            outlet.c = c_in;
            outlet.P = P;
            der(V) = outlet.F;               
        end FeedtankType;

        model HarvesttankType
            LiquidCon inlet;
            parameter Real P = 0.0                             "Pressure";                      
            parameter Real V_0 = 1.0                           "Initial harvest liquid volume";
            parameter Real m_0 = 0.0                           "Initial substance mass";
            Real V(start=V_0, fixed=true)                      "Harvest liquid volume";
            Real m(start=m_0, fixed=true)                      "Substance mass";
            Real c                                             "Substance conc"; 
        equation
            inlet.P = P;
            der(V) = inlet.F;
            der(m) = inlet.c*inlet.F;
            c = m/V;            
        end HarvesttankType;
    end EquipmentLib;

//  ---------------------------------------------------------------------------------------------
//     Example of system 
//  ---------------------------------------------------------------------------------------------

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

end DEMO_v41;

下面的旧示例 DEMO_v40 更笼统且更难阅读,但自从围绕此示例的一个早期回答以来保留以供参考。

我得到的编译 (JModelica 2.14) 错误消息是:“扁平模型中的错误:系统在结构上是单一的。以下变量无法与方程匹配:harvesttank.inlet.c[1]、pipe.outlet.c[1]。 OpenModelica (1.16) 给出了大致相同的信息。这里有什么问题?

package DEMO_v40

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

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

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

    package Medium1 
        extends MediumBase
            (name="One component medium",
             nc=1);
        constant Real[nc] mw = {10}                            "Substance weight";  
        constant Integer A = 1                                 "Substance index";
    end Medium1;

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

//  ---------------------------------------------------------------------------------------------
//     Equipment dependent on the medium  
//  ---------------------------------------------------------------------------------------------

    package EquipmentLib
        replaceable package Medium = MediumBase                // formal parameter - 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*(inlet.P - outlet.P);            // Linearized Bernoulli equation
            for i in 1:Medium.nc loop
                outlet.c[i] = inlet.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;
            der(V) = inlet.F;
            for i in 1:Medium.nc loop
                der(m[i]) = inStream(inlet.c[i])*inlet.F;
                c[i] = m[i]/V;
            end for;               
        end HarvesttankType;
    end EquipmentLib;

//  ---------------------------------------------------------------------------------------------
//     Adaptation of package Equipment to Medium1 
//  ---------------------------------------------------------------------------------------------

    package Equipment
        import DEMO_v40.EquipmentLib;
        extends EquipmentLib(redeclare package Medium=Medium1);
    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_v40;

正确的方法是

connector LiquidCon
   nc=5;
   Real c[nc]        “Component concentrations”;
   flow Real F[nc]       “Flow rate”;
end LiquidCon;

connector LiquidCon
   Real c        “Component concentrations”;
   flow Real F       “Flow rate”;
end LiquidCon;

取决于您要建模的内容。经验法则是:number of potentials = number of flows。由于您只使用一种流量和多种浓度,这意味着您有多个类似水箱的组件,每个组件都有一定的浓度,并通过允许流量的类似管道的组件连接。

对于这些我会推荐我发布的第二个版本!

一些背景信息: 连接器永远不会平衡,假定与其未知数相比提供一半的方程式。每当您向组件添加连接器时,该组件都必须平衡它。原因很简单:例如具有一种电位和一种流量的连接器。信息流动的方向不清楚,但可以肯定的是,要么 flow 变量被认为是已知的,要么 potential 被认为是已知的,另一个将由组件的方程计算。对于水箱,浓度由其自身的方程计算,流量通过连接器(管道反之亦然)。

只要连接两个或多个连接器,所有电位都设置为相等,所有流量总和为零(Modelica Language Specification 第 9.2 节)。

我更改了您的示例,以便我可以实际单独测试组件。注意我给nc添加了一个默认值,否则无法检查单个组件的一致性

package DEMO_v40

//  ---------------------------------------------------------------------------------------------

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

    package Medium1 
        extends MediumBase
            (name="One component medium",
             nc=1);
        constant Real[nc] mw = {10}                            "Substance weight";  
        constant Integer A = 1                                 "Substance index";
    end Medium1;

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

  //  ---------------------------------------------------------------------------------------------
  //     Equipment dependent on the medium
  //  ---------------------------------------------------------------------------------------------

    package EquipmentLib

        replaceable package Medium = MediumBase                "formal parameter EquipmentLib";


        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*(inlet.P - outlet.P);            // Linearized Bernoulli equation
            for i in 1:Medium.nc loop
                outlet.c[i] = inlet.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;
            der(V) = inlet.F;
            for i in 1:Medium.nc loop
                der(m[i]) = inStream(inlet.c[i])*inlet.F;
                c[i] = m[i]/V;
            end for;               
        end HarvesttankType;

    end EquipmentLib;

  //  ---------------------------------------------------------------------------------------------
  //     Adaptation of package Equipment to Medium1
  //  ---------------------------------------------------------------------------------------------

    package Equipment
        import DEMO_v40.EquipmentLib;
        extends EquipmentLib(redeclare package Medium=Medium1);
    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_v40;

有了这个,我转到 OMEdit 并使用 CheckModel 按钮(OMEdit 顶部中间的绿色圆圈上的单个复选标记)分别检查每个组件。 我意识到你的连接器有 3 个未知数和 1 个方程式,这是非法的(正如我所说,它应该是 2:1 比率)。这也会导致您的所有其他组件都是非法的。

由于调试您所有的东西会很麻烦,所以我只能提供我前段时间为学生项目制作的东西,但它应该展示必须完成的工作。您不需要同时传递 pressureconcentration,因为无论如何它们应该是代数连接的。我改用 height

请参阅以下模型答案,它不适合这个(我无法在此处添加文件)。

编辑:我刚刚创建了一个 git 存储库。实际上更容易:

HTTPS:https://github.com/kabdelhak/TankSystem

SSH: git@github.com:kabdelhak/TankSystem.git

就我个人而言,我会 "go all the way" 并使用流连接器,原因如下:

  1. 在过去的 15-20 年中,人们进行了许多尝试,以在 Modelica 中创建良好的热液压连接器。这一努力在 2008 年产生了 stream 个连接器,这是目前 Modelica 中最先进的。它允许您使用一个 flow 变量传输特定的焓和物质分数(或物质浓度),并启用流动反转。使用 stream 连接器 而不是 矫枉过正。
  2. 遵守例如Modelica.Fluid.Interfaces.FluidPort,您的作品将与许多现有库和模型兼容,您无需自己制作泵、管道、阀门、水箱模型等。

不过,您将面临一些挑战:

  1. 您需要学习 stream 连接器的语法和工作原理。您可以在 https://github.com/justnielsen/ModelicaTutorials
  2. 中找到灵感
  3. 您必须为将在流连接器中传输的流体实施基于 Modelica.Media 的介质模型。例如,如果您可以假设恒定密度 and/or 比热容,则介质模型不必非常复杂。如果介质模型很简单,当您指定边界条件(source/sinks)时,在 volume/mass 流量之间切换在计算上很容易。

经过一番思考,我相信以下是您转换为直接使用流变量的示例(尽管我同意让它与 MSL 兼容会很好,正如@ReneJustNielsen 所建议的)。

package DEMO_v42

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

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

//  ---------------------------------------------------------------------------------------------
//     Equipment
//  ---------------------------------------------------------------------------------------------

    package EquipmentLib

        connector LiquidCon
            Real P                                             "Pressure";
            flow Real F                                        "Flow rate";
      stream Real c_outflow "Substance conc";
        end LiquidCon;

        model PipeType
            LiquidCon inlet, outlet;
            parameter Real area = 1;
        equation 
            inlet.F = -outlet.F;
            outlet.F = -area^2*(inlet.P - outlet.P);           // Linearized Bernoulli equation
            outlet.c_outflow = inStream(inlet.c_outflow);
            inlet.c_outflow=inStream(outlet.c_outflow);
        end PipeType;

        model FeedtankType
            LiquidCon outlet;
            parameter Real P = 0.1                             "Pressure";
            parameter Real V_0 = 100                           "Initial feed volume";
            parameter Real c_in = 1.0                          "Feedtank conc";
            Real V(start=V_0, fixed=true)                      "Feed volume";
        equation 
            outlet.c_outflow = c_in;
            outlet.P = P;
            der(V) = outlet.F;
        end FeedtankType;

        model HarvesttankType
            LiquidCon inlet;
            parameter Real P = 0.0                             "Pressure";
            parameter Real V_0 = 1.0                           "Initial harvest liquid volume";
            parameter Real m_0 = 0.0                           "Initial substance mass";
            Real V(start=V_0, fixed=true)                      "Harvest liquid volume";
            Real m(start=m_0, fixed=true)                      "Substance mass";
            Real c                                             "Substance conc";
            Real inletC=actualStream(inlet.c_outflow);
        equation 
            inlet.P = P;
            inlet.c_outflow=c;
            der(V) = inlet.F;
            der(m) = actualStream(inlet.c_outflow)*inlet.F;
            c = m/V;
        end HarvesttankType;
    end EquipmentLib;

//  ---------------------------------------------------------------------------------------------
//     Example of system 
//  ---------------------------------------------------------------------------------------------

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

end DEMO_v42;

我添加了 inletC 以便能够将浓度与以前的模型进行比较。

主要变化是对于流变量 c_outflow,实际上有 two/three 个不同的变量可供使用:

  • 如果您想要介质流出时的浓度,请使用c_outflow
  • 如果你想要介质在使用中流动时的浓度inStream(c_outflow)
  • 如果您只想要流量中的实际浓度,请使用 actualStream(c_outflow)

所以对于你写的管道,从一个端口流出的浓度等于流入另一个端口的浓度,反之亦然。 对于水箱,您只需编写 c_outflow 的方程式,但使用 actualStream 获得流量中的实际浓度。