Myrtillus
Myrtillus

Reputation: 51

OpenModelica Building Libary, using PlugFlowPipe component

This question is a continuation to the question: OpenModelica: How to model plug flow for multi substance fluid medium?

The modelica does not include by default a medium model supporting multiple substances and trace substances. Therefore, I was putting together a medium model that works fine with basic Modelica fluid components (sources, open tanks, static pipe). However, Buildings library's component Buildings.Fluid.FixedResistances.PlugFlowPipe seems to be problematic and I could not figure out why? I have a very limited experience on OpenModelica and would appreciate if someone could help and point out the reasoning.

I have ended up to the Building library since I haven't found any other component in other libraries that would model the plug flow and transfer delays in pipe lines.

Below are the medium model and Simulation model that has been used for testing.

Medium model used:

// file: SimpleMachineStockMedium_400_v1.mo 
package SimpleMachineStockMedium_400_v1

  import Modelica = Modelica;


  // EXTENDING FROM A CLASS
  // **************************


  extends Modelica.Media.Interfaces.PartialMedium(
      final ThermoStates = Modelica.Media.Interfaces.Choices.IndependentVariables.pTX, 
      final singleState = true, 
      final reducedX = true, 
      final fixedX = false, 
      reference_X=fill(1/nX,nX),
      mediumName="SimpleMachineStockMedium_400_v1",
      substanceNames={"water","fiber","filler"},
      extraPropertiesNames=fill("", 0)
      
      //extraPropertiesNames={"reta"}
      );

  // SPECIFY CONSTANTS
  // *********************************

  constant SpecificHeatCapacity cp_const=4184  "Constant specific heat capacity at constant pressure";
  constant SpecificHeatCapacity cv_const=4184  "Constant specific heat capacity at constant volume";
  constant Density d_const=995.586 "Constant density";
  constant DynamicViscosity eta_const=1.e-3 "Constant dynamic viscosity";
  constant ThermalConductivity lambda_const=0.598 "Constant thermal conductivity";
  constant VelocityOfSound a_const=1484 "Constant velocity of sound";
  constant Temperature T_min=273 "Minimum temperature valid for medium model";
  constant Temperature T_max=373 "Maximum temperature valid for medium model";
  constant Temperature T0=273.15 "Zero enthalpy temperature";




  // defining fluid constants for substances
  import Modelica.Media.Water.ConstantPropertyLiquidWater.simpleWaterConstants;

  constant Modelica.Media.Interfaces.Types.Basic.FluidConstants[3]
    simpleWaterConstants(
    each chemicalFormula="H2O",
    each structureFormula="H2O",
    each casRegistryNumber="7732-18-5",
    each iupacName="oxidane",
    each molarMass=0.018015268);



  //constant MolarMass MM_const "Molar mass";
  // Molarmasses are defined for substances, just giving same values for all
  constant Real MM_const_fiber = 0.018015268;
  constant Real MM_const_filler = 0.018015268;
  constant Real MM_const_water = 0.018015268;

  constant MolarMass[nX] MMX ={MM_const_fiber, MM_const_filler, MM_const_water} "Molar mass";


  // THERMODYNAMIC STATE
  // **********************


  redeclare record extends ThermodynamicState "Thermodynamic state"
    AbsolutePressure p "Absolute pressure of medium";
    Temperature T "Temperature of medium";

    // bring in the substances
    MassFraction[nX] X(start=reference_X) "Mass fractions (= (component mass)/total mass  m_i/m)";  

  end ThermodynamicState;



  // MODEL BaseProperties
  // ********************

  redeclare replaceable model extends BaseProperties(
    T(stateSelect=if preferredMediumStates then StateSelect.prefer else StateSelect.default),
    p(stateSelect=if preferredMediumStates then StateSelect.prefer else StateSelect.default), 
    Xi(each stateSelect = if preferredMediumStates then StateSelect.prefer else StateSelect.default),
    final standardOrderComponents = true) "Base properties"

  equation
    assert(T >= T_min and T <= T_max, "
      Temperature T (= " + String(T) + " K) is not
      in the allowed range (" + String(T_min) + " K <= T <= " + String(T_max) + " K)
      required from medium model \"" + mediumName + "\".
      ");

    // h = cp_const*(T-T0);
    h = specificEnthalpy_pTX(
            p,
            T,
            X);
    u = cv_const*(T - T0);
    d = d_const;
    R_s = 0;
    //MM = MM_const;
    MM = molarMass(state);
    state.T = T;
    state.p = p;
    state.X = if fixedX then reference_X else X;

    annotation (Documentation(info="<html>
    <p>
    This is the most simple incompressible medium model, where
    specific enthalpy h and specific internal energy u are only
    a function of temperature T and all other provided medium
    quantities are assumed to be constant.
    Note that the (small) influence of the pressure term p/d is neglected.
    </p>
    </html>"));
    end BaseProperties;

  // DECLARE FUNCTIONS
  // *******************


  //-------------------
  redeclare function setState_pTX
    "Return thermodynamic state from p, T, and X or Xi"
    extends Modelica.Icons.Function;
    input AbsolutePressure p "Pressure";
    input Temperature T "Temperature";
    input MassFraction X[:]=reference_X "Mass fractions";
    output ThermodynamicState state "Thermodynamic state record";
  algorithm
    //state := ThermodynamicState(p=p, T=T);

    // take into account substances
    state := if size(X,1) == 0 then ThermodynamicState(p=p,T=T,X=reference_X) 
        else if size(X,1) == nX then ThermodynamicState(p=p,T=T, X=X) 
        else ThermodynamicState(p=p,T=T, X=cat(1,X,{1-sum(X)}));   // when reduceX = true

  end setState_pTX;

  //-------------------
  redeclare function setState_phX
    "Return thermodynamic state from p, h, and X or Xi"
    extends Modelica.Icons.Function;
    input AbsolutePressure p "Pressure";
    input SpecificEnthalpy h "Specific enthalpy";
    input MassFraction X[:]=reference_X "Mass fractions";
    output ThermodynamicState state "Thermodynamic state record";
  
  algorithm

    state := if size(X,1) == 0 then ThermodynamicState(p = p, T = T0 + h / cp_const, X=X)
         else if size(X,1) == nX then ThermodynamicState(p = p, T = T0 + h / cp_const, X=X)
         else ThermodynamicState(p = p, T = T0 + h / cp_const, X=cat(1,X,{1-sum(X)}));

  end setState_phX;

  //-------------------
  redeclare replaceable function setState_psX
    "Return thermodynamic state from p, s, and X or Xi"
    extends Modelica.Icons.Function;
    input AbsolutePressure p "Pressure";
    input SpecificEntropy s "Specific entropy";
    input MassFraction X[:]=reference_X "Mass fractions";
    output ThermodynamicState state "Thermodynamic state record";
  algorithm
    //state := ThermodynamicState(p=p, T=Modelica.Math.exp(s/cp_const +
    //  Modelica.Math.log(reference_T)))
    //  "Here the incompressible limit is used, with cp as heat capacity";

    // take into account substances
    state := if size(X,1) == 0 then ThermodynamicState(p = p, T = Modelica.Math.exp(s / cp_const + Modelica.Math.log(reference_T)), X=X)
             else if size(X,1) == nX then ThermodynamicState(p = p, T = Modelica.Math.exp(s / cp_const + Modelica.Math.log(reference_T)), X=X)
             else ThermodynamicState(p = p, T = Modelica.Math.exp(s / cp_const + Modelica.Math.log(reference_T)), X=cat(1,X,{1-sum(X)}));
  end setState_psX;

  //-------------------
  redeclare function setState_dTX
    "Return thermodynamic state from d, T, and X or Xi"
    extends Modelica.Icons.Function;
    input Density d "Density";
    input Temperature T "Temperature";
    input MassFraction X[:]=reference_X "Mass fractions";
    output ThermodynamicState state "Thermodynamic state record";
  algorithm
    assert(false,
      "Pressure can not be computed from temperature and density for an incompressible fluid!");
  end setState_dTX;

  //-------------------
  redeclare function extends setSmoothState
    "Return thermodynamic state so that it smoothly approximates: if x > 0 then state_a else state_b"
  algorithm
    state := ThermodynamicState(p=Media.Common.smoothStep(
            x,
            state_a.p,
            state_b.p,
            x_small), T=Media.Common.smoothStep(
            x,
            state_a.T,
            state_b.T,
            x_small));
  end setSmoothState;

  //-------------------
  redeclare function extends dynamicViscosity "Return dynamic viscosity"

  algorithm
    eta := eta_const;
  end dynamicViscosity;

  //-------------------
  redeclare function extends thermalConductivity
    "Return thermal conductivity"

  algorithm
    lambda := lambda_const;
  end thermalConductivity;

  //-------------------
  redeclare function extends pressure "Return pressure"

  algorithm
    p := state.p;
  end pressure;

  //-------------------
  redeclare function extends temperature "Return temperature"

  algorithm
    T := state.T;
  end temperature;

  //-------------------
  redeclare function extends density "Return density"

  algorithm
    d := d_const;
  end density;

  //-------------------
  redeclare function extends specificEnthalpy "Return specific enthalpy"

  algorithm
    h := cp_const*(state.T - T0);
  end specificEnthalpy;

  //-------------------
  redeclare function extends specificHeatCapacityCp
    "Return specific heat capacity at constant pressure"

  algorithm
    cp := cp_const;
  end specificHeatCapacityCp;

  //-------------------
  redeclare function extends specificHeatCapacityCv
    "Return specific heat capacity at constant volume"

  algorithm
    cv := cv_const;
  end specificHeatCapacityCv;

  //-------------------
  redeclare function extends isentropicExponent "Return isentropic exponent"

  algorithm
    gamma := cp_const/cv_const;
  end isentropicExponent;

  //-------------------
  redeclare function extends velocityOfSound "Return velocity of sound"

  algorithm
    a := a_const;
  end velocityOfSound;

  //-------------------
  redeclare function specificEnthalpy_pTX
    "Return specific enthalpy from p, T, and X or Xi"
    extends Modelica.Icons.Function;
    input AbsolutePressure p "Pressure";
    input Temperature T "Temperature";
    input MassFraction X[nX] "Mass fractions";
    output SpecificEnthalpy h "Specific enthalpy";
  algorithm
    h := cp_const*(T - T0);
    annotation (Documentation(info="<html>
    <p>
    This function computes the specific enthalpy of the fluid, but neglects the (small) influence of the pressure term p/d.
    </p>
    </html>"));
  end specificEnthalpy_pTX;

  //-------------------
  redeclare function temperature_phX
    "Return temperature from p, h, and X or Xi"
    extends Modelica.Icons.Function;
    input AbsolutePressure p "Pressure";
    input SpecificEnthalpy h "Specific enthalpy";
    input MassFraction X[nX] "Mass fractions";
    output Temperature T "Temperature";
  algorithm
    T := T0 + h/cp_const;
  end temperature_phX;

  //-------------------
  redeclare function density_phX "Return density from p, h, and X or Xi"
    extends Modelica.Icons.Function;
    input AbsolutePressure p "Pressure";
    input SpecificEnthalpy h "Specific enthalpy";
    input MassFraction X[nX] "Mass fractions";
    output Density d "Density";
  algorithm
    d := density(setState_phX(
            p,
            h,
            X));
  end density_phX;

  //-------------------
  redeclare function extends specificInternalEnergy
    "Return specific internal energy"
    extends Modelica.Icons.Function;
  algorithm
    //  u := cv_const*(state.T - T0) - reference_p/d_const;
    u := cv_const*(state.T - T0);
    annotation (Documentation(info="<html>
    <p>
    This function computes the specific internal energy of the fluid, but neglects the (small) influence of the pressure term p/d.
    </p>
    </html>"));
  end specificInternalEnergy;

  //-------------------
  redeclare function extends specificEntropy "Return specific entropy"
    extends Modelica.Icons.Function;
  algorithm
    s := cv_const*Modelica.Math.log(state.T/T0);
  end specificEntropy;

  //-------------------
  redeclare function extends specificGibbsEnergy
    "Return specific Gibbs energy"
    extends Modelica.Icons.Function;
  algorithm
    g := specificEnthalpy(state) - state.T*specificEntropy(state);
  end specificGibbsEnergy;

  //-------------------
  redeclare function extends specificHelmholtzEnergy
    "Return specific Helmholtz energy"
    extends Modelica.Icons.Function;
  algorithm
    f := specificInternalEnergy(state) - state.T*specificEntropy(state);
  end specificHelmholtzEnergy;

  //-------------------
  redeclare function extends isentropicEnthalpy "Return isentropic enthalpy"
  algorithm
    h_is := cp_const*(temperature(refState) - T0);
  end isentropicEnthalpy;

  //-------------------
  redeclare function extends isobaricExpansionCoefficient
    "Returns overall the isobaric expansion coefficient beta"
  algorithm
    beta := 0.0;
  end isobaricExpansionCoefficient;

  //-------------------
  redeclare function extends isothermalCompressibility
    "Returns overall the isothermal compressibility factor"
  algorithm
    kappa := 0;
  end isothermalCompressibility;

  //-------------------
  redeclare function extends density_derp_T
    "Returns the partial derivative of density with respect to pressure at constant temperature"
  algorithm
    ddpT := 0;
  end density_derp_T;

  //-------------------
  redeclare function extends density_derT_p
    "Returns the partial derivative of density with respect to temperature at constant pressure"
  algorithm
    ddTp := 0;
  end density_derT_p;

  //-------------------
  redeclare function extends density_derX
    "Returns the partial derivative of density with respect to mass fractions at constant pressure and temperature"
  algorithm
    dddX := fill(0, nX);
  end density_derX;

  //-------------------
  redeclare function extends molarMass "Return the molar mass of the medium"
  algorithm
    //MM := MM_const;
    MM := 1/sum(state.X[j]/MMX[j] for j in 1:size(state.X,1));
  end molarMass;



  // functions that have been adopted from class PARTIALMIXTUREMEDIUM
 
  // -----------------
  replaceable function gasConstant
    "Return the gas constant of the mixture (also for liquids)"
    extends Modelica.Icons.Function;
    input ThermodynamicState state "Thermodynamic state";
    output SI.SpecificHeatCapacity R_s "Mixture gas constant";
    
  algorithm
    R_s := 0;
  end gasConstant;

  // -----------------
  function moleToMassFractions "Return mass fractions X from mole fractions"
    extends Modelica.Icons.Function;
    input SI.MoleFraction moleFractions[:] "Mole fractions of mixture";
    input MolarMass[:] MMX "Molar masses of components";
    output SI.MassFraction X[size(moleFractions, 1)]
      "Mass fractions of gas mixture";
  protected
    MolarMass Mmix=moleFractions*MMX "Molar mass of mixture";
  algorithm
    for i in 1:size(moleFractions, 1) loop
      X[i] := moleFractions[i]*MMX[i]/Mmix;
    end for;
    annotation (smoothOrder=5);
  end moleToMassFractions;

  // -----------------
  function massToMoleFractions "Return mole fractions from mass fractions X"
    extends Modelica.Icons.Function;
    input SI.MassFraction X[:] "Mass fractions of mixture";
    input SI.MolarMass[:] MMX "Molar masses of components";
    output SI.MoleFraction moleFractions[size(X, 1)]
      "Mole fractions of gas mixture";
  protected
    Real invMMX[size(X, 1)] "Inverses of molar weights";
    SI.MolarMass Mmix "Molar mass of mixture";
  algorithm
    for i in 1:size(X, 1) loop
      invMMX[i] := 1/MMX[i];
    end for;
    Mmix := 1/(X*invMMX);
    for i in 1:size(X, 1) loop
      moleFractions[i] := Mmix*X[i]/MMX[i];
    end for;
    annotation (smoothOrder=5);
  end massToMoleFractions;


end SimpleMachineStockMedium_400_v1;


PlugFlowPipe model derived from the example :

model testing_PlugFlowPipe_example_ver01 "Simple example of plug flow pipe"

  // Modifications to the example
  import Buildings = Buildings;
  import Sources = Buildings.Fluid.Sources;
  import Sensors = Buildings.Fluid.Sensors;
  //replaceable package Medium = Buildings.Media.Water;
  replaceable package Medium = SimpleMachineStockMedium_400_v1;
  
  // End of modifications
  
  
  final parameter Modelica.Units.SI.MassFlowRate m_flow_nominal = 3 "Mass flow rate";
  Modelica.Blocks.Sources.Ramp Tin(height = 20, duration = 0, offset = 273.15 + 50, startTime = 100) "Ramp pressure signal" annotation(
    Placement(transformation(extent = {{-100, -10}, {-80, 10}})));
  Sources.Boundary_pT sin(redeclare package Medium = Medium, T = 273.15 + 10, nPorts = 2, p(displayUnit = "Pa") = 101325) "Pressure boundary condition" annotation(
    Placement(transformation(extent = {{100, -10}, {80, 10}})));
  Buildings.Fluid.FixedResistances.PlugFlowPipe pip(redeclare package Medium = Medium, dh = 0.1, length = 100, dIns = 0.05, kIns = 0.028, m_flow_nominal = m_flow_nominal, cPip = 500, thickness = 0.0032, initDelay = true, m_flow_start = m_flow_nominal, rhoPip = 8000, T_start_in = 323.15, T_start_out = 323.15) "Pipe" annotation(
    Placement(transformation(extent = {{0, 10}, {20, 30}})));
  Buildings.HeatTransfer.Sources.FixedTemperature bou[2](each T = 283.15) "Boundary temperature" annotation(
    Placement(transformation(extent = {{-40, 60}, {-20, 80}})));
  Buildings.Fluid.Sources.MassFlowSource_T sou(redeclare package Medium = Medium, use_T_in = true, m_flow = m_flow_nominal, nPorts = 1) "Flow source" annotation(
    Placement(transformation(extent = {{-60, 10}, {-40, 30}})));
  Buildings.Fluid.Sensors.TemperatureTwoPort senTemOut(redeclare package Medium = Medium, m_flow_nominal = m_flow_nominal, tau = 0, T_start = 323.15) "Temperature sensor" annotation(
    Placement(transformation(extent = {{40, 10}, {60, 30}})));
  Buildings.Fluid.Sensors.TemperatureTwoPort senTemIn(redeclare package Medium = Medium, m_flow_nominal = m_flow_nominal, tau = 0, T_start = 323.15) "Temperature sensor" annotation(
    Placement(transformation(extent = {{-30, 10}, {-10, 30}})));
  Sensors.TemperatureTwoPort senTemInNoMix(redeclare package Medium = Medium, m_flow_nominal = m_flow_nominal, tau = 0, T_start = 323.15) "Temperature sensor" annotation(
    Placement(transformation(extent = {{-30, -30}, {-10, -10}})));
  Buildings.Fluid.FixedResistances.PlugFlowPipe pipNoMix(have_pipCap = false, redeclare package Medium = Medium, dh = 0.1, length = 100, dIns = 0.05, kIns = 0.028, m_flow_nominal = m_flow_nominal, cPip = 500, thickness = 0.0032, initDelay = true, m_flow_start = m_flow_nominal, rhoPip = 8000, T_start_in = 323.15, T_start_out = 323.15) "Pipe" annotation(
    Placement(transformation(extent = {{0, -30}, {20, -10}})));
  Sensors.TemperatureTwoPort senTemOutNoMix(redeclare package Medium = Medium, m_flow_nominal = m_flow_nominal, tau = 0, T_start = 323.15) "Temperature sensor" annotation(
    Placement(transformation(extent = {{40, -30}, {60, -10}})));
  Sources.MassFlowSource_T souNoMix(redeclare package Medium = Medium, use_T_in = true, m_flow = m_flow_nominal, nPorts = 1) "Flow source" annotation(
    Placement(transformation(extent = {{-60, -30}, {-40, -10}})));
equation
  connect(Tin.y, sou.T_in) annotation(
    Line(points = {{-79, 0}, {-68, 0}, {-68, 24}, {-62, 24}}, color = {0, 0, 127}));
  connect(pip.port_b, senTemOut.port_a) annotation(
    Line(points = {{20, 20}, {40, 20}}, color = {0, 127, 255}));
  connect(senTemOut.port_b, sin.ports[1]) annotation(
    Line(points = {{60, 20}, {76, 20}, {76, -1}, {80, -1}}, color = {0, 127, 255}));
  connect(senTemIn.port_b, pip.port_a) annotation(
    Line(points = {{-10, 20}, {0, 20}}, color = {0, 127, 255}));
  connect(senTemInNoMix.port_b, pipNoMix.port_a) annotation(
    Line(points = {{-10, -20}, {0, -20}}, color = {0, 127, 255}));
  connect(pipNoMix.port_b, senTemOutNoMix.port_a) annotation(
    Line(points = {{20, -20}, {40, -20}}, color = {0, 127, 255}));
  connect(senTemOutNoMix.port_b, sin.ports[2]) annotation(
    Line(points = {{60, -20}, {76, -20}, {76, 1}, {80, 1}}, color = {0, 127, 255}));
  connect(bou[1].port, pip.heatPort) annotation(
    Line(points = {{-20, 70}, {-4, 70}, {-4, 40}, {10, 40}, {10, 30}}, color = {191, 0, 0}));
  connect(bou[2].port, pipNoMix.heatPort) annotation(
    Line(points = {{-20, 70}, {-4, 70}, {-4, 0}, {10, 0}, {10, -10}}, color = {191, 0, 0}));
  connect(sou.ports[1], senTemIn.port_a) annotation(
    Line(points = {{-40, 20}, {-30, 20}}, color = {0, 127, 255}));
  connect(souNoMix.ports[1], senTemInNoMix.port_a) annotation(
    Line(points = {{-40, -20}, {-30, -20}}, color = {0, 127, 255}));
  connect(Tin.y, souNoMix.T_in) annotation(
    Line(points = {{-79, 0}, {-68, 0}, {-68, -16}, {-62, -16}}, color = {0, 0, 127}));
  annotation(
    __Dymola_Commands(file = "modelica://Buildings/Resources/Scripts/Dymola/Fluid/FixedResistances/Examples/PlugFlowPipe.mos" "Simulate and Plot"),
    experiment(StopTime = 1000, Tolerance = 1e-006),
    Documentation(info = "<html>
<p>Basic test of model
<a href=\"modelica://Buildings.Fluid.FixedResistances.PlugFlowPipe\">
Buildings.Fluid.FixedResistances.PlugFlowPipe</a> with and without outlet mixing volume.
This test includes an inlet temperature step under a constant mass flow rate.
</p>
</html>", revisions = "<html>
<ul>
<li>July 27, 2021 by Baptiste Ravache<br/>Add case without mixing volume</li>
</ul>
<ul>
<li>September 8, 2017 by Bram van der Heijde<br/>First implementation</li>
</ul>
</html>"),
    Diagram(coordinateSystem(extent = {{-120, -100}, {120, 100}})),
    Icon(coordinateSystem(extent = {{-100, -100}, {100, 100}})));
end testing_PlugFlowPipe_example_ver01;

EDIT: Version information: MSL 4.0.0

Buildings library:

Information obtained from the Buildings library through OMEdit enter image description here

Error Message (Intergrator method dassl): enter image description here

Latest Debug message from Transformational debugger enter image description here

Error Message when using rungekutta integrator: enter image description here

Upvotes: 2

Views: 243

Answers (1)

sjoelund.se
sjoelund.se

Reputation: 3523

You need the latest development version of Buildings (the master branch; not any released version) because have_pipCap was only introduced in https://github.com/lbl-srg/modelica-buildings/commit/89bf74035bdea926701edef2c3dbf35f48b32680

Without this Buildings version, you will get the message:

Error: Modified element have_pipCap not found in class PlugFlowPipe.

It is recommended to let the Modelica tool put the annotation uses on the code so the same version can be used for testing. I am assuming this is the problem because the questions did not list the version of Buildings used or the error-message you got.

Upvotes: 1

Related Questions