Library of media property models

within Modelica;
package Media "Library of media property models"
  extends Modelica.Icons.Package;
  import Modelica.Units.SI;
  import Cv = Modelica.Units.Conversions;

package UsersGuide "User's Guide of Media Library"
  extends Modelica.Icons.Information;

  package MediumUsage "Medium usage"
    extends Modelica.Icons.Information;

    class BasicUsage "Basic usage"
      extends Modelica.Icons.Information;

 Functions to compute additional properties (such as saturation
     properties, viscosity, thermal conductivity, etc.).

There are - as stated above - two different basic ways of using the Media library which
will be described in more details in the following section. One way is to use the model BaseProperties.
Every instance of BaseProperties for any medium model provides 3+nXi
equations for the following 5+nXi variables that are declared in
the medium model (nXi is the number of independent mass fractions, see
explanation below):

Variable
      Unit
      Description

T
      K
      temperature

p
      Pa
      absolute pressure

d
      kg/m3
      density

u
      J/kg
      specific internal energy

h
      J/kg
      specific enthalpy (h = u + p/d)

Xi[nXi]
      kg/kg
      independent mass fractions m_i/m

X[nX]
      kg/kg
      All mass fractions m_i/m. X is defined in BaseProperties by:

          X = if reducedX then vector([Xi; 1-sum(Xi)])
          else Xi

Two variables out of p, d, h, or u, as well as the
mass fractions Xi are the independent variables and the
medium model basically provides equations to compute
the remaining variables, including the full mass fraction vector X
(more details to Xi and X are given further below).

In a component, the most basic usage of a medium model is as follows

model Pump
  replaceable package Medium = Modelica.Media.Interfaces.PartialMedium
                       \"Medium model\" annotation (choicesAllMatching = true);
  Medium.BaseProperties medium_a \"Medium properties at location a (e.g., port_a)\";
  // Use medium variables (medium_a.p, medium_a.T, medium_a.h, ...)
   ...
end Pump;

The second way is to use the setState_XXX functions to compute the thermodynamic state
record from which all other thermodynamic state variables can be computed (see

Basic definition of medium for further details on ThermodynamicState). The setState_XXX functions
accept either X or Xi (see explanation below) and will decide internally which of these two compositions
is provided by the user. The four fundamental setState_XXX functions are provided in PartialMedium

Function
      Description
      Short-form for
single component medium

setState_dTX
      computes ThermodynamicState from density, temperature, and composition X or Xi
      setState_dT

setState_phX
      computes ThermodynamicState from pressure, specific enthalpy, and composition X or Xi
      setState_ph

setState_psX
      computes ThermodynamicState from pressure, specific entropy, and composition X or Xi
      setState_ps

setState_pTX
      computes ThermodynamicState from pressure, temperature, and composition X or Xi
      setState_pT

The simple example that explained the basic usage of BaseProperties would then become

model Pump
  replaceable package Medium = Modelica.Media.Interfaces.PartialMedium
                       \"Medium model\" annotation (choicesAllMatching = true);
  Medium.ThermodynamicState state_a \"Thermodynamic state record at location a (e.g., port_a)\";
  // Compute medium variables from thermodynamic state record (pressure(state_a), temperature(state_a),
  // specificEnthalpy(state_a), ...)
  ...
end Pump;

All media models are directly or indirectly a subpackage of package
Modelica.Media.Interfaces.PartialMedium. Therefore,
a medium model in a component should inherit from this
partial package. Via the annotation \"choicesAllMatching = true\" it
is defined that the tool should display a selection box with
all loaded packages that inherit from PartialMedium. An example
is given in the next figure:

A selected medium model leads, e.g., to the following equation:

Pump pump(redeclare package Medium = Modelica.Media.Water.SimpleLiquidWater);

Usually, a medium model is associated with the variables of a
fluid connector. Therefore, equations have to be defined in a model
that relate the variables in the connector with the variables
in the medium model:

model Pump
  replaceable package Medium = Modelica.Media.Interfaces.PartialMedium
                       \"Medium model\" annotation (choicesAllMatching = true);
  Medium.BaseProperties medium_a \"Medium properties of port_a\";
  // definition of the fluid port port_a
   ...
equation
  medium.p = port_a.p;
  medium.h = port_a.h;
  medium.Xi = port_a.Xi;
   ...
end Pump;

in the case of using BaseProperties or

model Pump
  replaceable package Medium = Modelica.Media.Interfaces.PartialMedium
                       \"Medium model\" annotation (choicesAllMatching = true);
  Medium.ThermodynamicState state_a \"Thermodynamic state record of medium at port_a\";
  // definition of the fluid port port_a
   ...
equation
  state_a = Medium.setState_phX(port_a.p, port_a.h, port_a.Xi) // if port_a contains the variables
                                                               // p, h, and Xi
   ...
end Pump;

in the case of using ThermodynamicState.

If a component model shall treat both single and multiple
substance fluids, equations for the mass fractions have to be
present (above: medium.Xi = port_a.Xi) in the model. According
to the Modelica semantics, the equations of the mass fractions
are ignored, if the dimension of Xi is zero, i.e., for a single-component
medium. Note, by specific techniques sketched in section
\"Medium definition\", the independent variables in the medium model
need not to be the same as the variables in the connector and still
get the same efficiency, as if the same variables would be used.

If a fluid consists of a single
substance, nXi = 0 and the vector of mass fractions Xi is not
present. If a fluid consists of nS substances,
the medium model may define the number of independent
mass fractions nXi to be nS, nS-1, or zero.
In all cases, balance equations for nXi substances have to be
given in the corresponding component (see discussion below).
Note, that if nXi = nS, the constraint \"sum(Xi)=1\" between the mass
fractions is not present in the model; in that case, it is necessary to
provide consistent start values for Xi such that sum(Xi) = 1.

The reason for this definition of Xi is that a fluid component library
can be implemented by using only the independent mass fractions Xi and
then via the medium it is defined how Xi is interpreted:

 If Xi = nS, then the constraint equation sum(X) = 1 is neglected
     during simulation. By making sure that the initial conditions of X
     fulfill this constraint, it can usually be guaranteed that small
     errors in sum(X) = 1 remain small although this constraint equation is
     not explicitly used during the simulation. This approach is usually useful
     if components of the mixture can become very small. If such a small
     quantity is computed via the equation 1 - sum(X[1:nX-1]), there might
     be large numerical errors and it is better to compute it via
     the corresponding balance equation.

 If Xi = nS-1, then the true independent mass fractions are used
     in the fluid component and the last component of X is computed via
     X[nX] = 1 - sum(Xi). This is useful for, e.g., MoistAir, where the
     number of states should be as small as possible without introducing
     numerical problems.

 If Xi = 0, then the reference value of composition reference_X is
     assumed. This case is useful to avoid composition states in all
     the cases when the composition will always be constant, e.g., with
     circuits having fixed composition sources.

The full vector of mass fractions X[nX] is computed in
PartialMedium.BaseProperties based on Xi, reference_X, and the information whether Xi = nS or nS-1. For single-substance media, nX = 0, so there's also no X vector. For multiple-substance media, nX = nS, and X always contains the full vector of mass fractions. In order to reduce confusion for the user of a fluid component library, \"Xi\" has the annotation \"HideResult=true\", meaning, that this variable is not shown in the plot window. Only X is shown in the plot window and this vector always contains all mass fractions.

"));
    end BasicUsage;

    class BalanceVolume "Balance volume"
      extends Modelica.Icons.Information;

  FluidPort_a port(redeclare package Medium = Medium);
  Medium.BaseProperties medium(preferredMediumStates = true);

  SI.Energy U              \"Internal energy of junction volume\";
  SI.Mass   M              \"Mass of junction volume\";
  SI.Mass   MX[Medium.nXi] \"Independent substance masses of junction volume\";
equation
  medium.p   = port.p;
  medium.h   = port.h;
  medium.Xi = port.Xi;

  M  = V*medium.d;                  // mass of JunctionVolume
  MX = M*medium.Xi;                 // mass fractions in JunctionVolume
  U  = M*medium.u;                  // internal energy in JunctionVolume

  der(M)  = port.m_flow;    // mass balance
  der(MX) = port.mX_flow;   // substance mass balance
  der(U)  = port.H_flow;    // energy balance
end JunctionVolume;

Assume the Modelica.Media.Air.SimpleAir medium model is used with
the JunctionVolume model above. This medium model uses pressure p
and temperature T as independent variables. If the flag
\"preferredMediumStates\" is set to true in the declaration
of \"medium\", then the independent variables of this medium model
get the attribute \"stateSelect = StateSelect.prefer\", i.e., the
Modelica translator should use these variables as states, if this
is possible. Basically, this means that
constraints between the
potential states p,T and the potential states U,M are present.
A Modelica tool will therefore automatically
differentiate medium equations and will use the following
equations for code generation (note the equations related to X are
removed, because SimpleAir consists of a single substance only):

M  = V*medium.d;
U  = M*medium.u;

// balance equations
der(M)  = port.m_flow;
der(U)  = port.H_flow;

// abbreviations introduced to get simpler terms
p = medium.p;
T = medium.T;
d = medium.d;
u = medium.u;
h = medium.h;

// medium equations
d = fd(p,T);
h = fh(p,T);
u = h - p/d;

// equations derived automatically by a Modelica tool due to index reduction
der(U) = der(M)*u + M*der(u);
der(M) = V*der(d);
der(u) = der(h) - der(p)/d - p/der(d);
der(d) = der(fd,p)*der(p) + der(fd,T)*der(T);
der(h) = der(fh,p)*der(p) + der(fd,T)*der(T);

Note, that \"der(y,x)\" is an operator that characterizes
in the example above the partial derivative of y with respect to x
(this operator will be included in one of the next Modelica language
releases).
All media models in this library are written in such a way that
at least the partial derivatives of the medium variables with
respect to the independent variables are provided, either because
the equations are directly given (= symbolic differentiation is possible)
or because the derivative of the corresponding function (such as fd above)
is provided. A Modelica tool will transform the equations above
in differential equations with p and T as states, i.e., will
generate equations to compute der(p) and der(T) as function of p and T.

Note, when preferredMediumStates = false, no differentiation
will take place and the Modelica translator will use the variables
appearing differentiated as states, i.e., M and U. This has the
disadvantage that for many media non-linear systems of equations are
present to compute the intrinsic properties p, d, T, u, h from
M and U.

"));
    end BalanceVolume;

    class ShortPipe "Short pipe"
      extends Modelica.Icons.Information;

  Utilities.FluidPort_a port_a (redeclare package Medium = Medium);
  Utilities.FluidPort_b port_b (redeclare package Medium = Medium);

  SI.Pressure dp = port_a.p - port_b.p \"Pressure drop\";
  Medium.BaseProperties medium_a \"Medium properties in port_a\";
  Medium.BaseProperties medium_b \"Medium properties in port_b\";
equation
  // define media models of the ports
  medium_a.p   = port_a.p;
  medium_a.h   = port_a.h;
  medium_a.Xi = port_a.Xi;

  medium_b.p   = port_b.p;
  medium_b.h   = port_b.h;
  medium_b.Xi = port_b.Xi;

  // Handle reverse and zero flow (semiLinear is a built-in Modelica operator)
  port_a.H_flow   = semiLinear(port_a.m_flow, port_a.h, port_b.h);
  port_a.mXi_flow = semiLinear(port_a.m_flow, port_a.Xi, port_b.Xi);

  // Energy, mass and substance mass balance
  port_a.H_flow + port_b.H_flow = 0;
  port_a.m_flow + port_b.m_flow = 0;
  port_a.mXi_flow + port_b.mXi_flow = zeros(Medium.nXi);

  // Provide equation: port_a.m_flow = f(dp)
end ShortPipe;

The semiLinear(..) operator is basically defined as:

semiLinear(m_flow, ha, hb) = if m_flow ≥ 0 then m_flow*ha else m_flow*hb;

that is, it computes the enthalpy flow rate either from the port_a or
from the port_b properties, depending on flow direction. The exact
details of this operator are given in

ModelicaReference.Operators.'semiLinear()'. Especially, rules
are defined in the Modelica specification that m_flow = 0 can be treated
in a \"meaningful way\". Especially, if n fluid components (such as pipes)
are connected together and the fluid connector from above is used,
a linear system of equations appear between
medium1.h, medium2.h, medium3.h, ..., port1.h, port2.h, port3.h, ...,
port1.H_flow, port2.H_flow, port3.H_flow, .... The rules for the
semiLinear(..) operator allow the following solution of this
linear system of equations:

 n = 2 (two components are connected):

     The linear system of equations can be analytically solved
     with the result

medium1.h = medium2.h = port1.h = port2.h
0 = port1.H_flow + port2.H_flow

     Therefore, no problems with zero mass flow rate are present.

 n > 2 (more than two components are connected together):

     The linear system of equations is solved numerically during simulation.
     For m_flow = 0, the linear system becomes singular and has an
     infinite number of solutions. The simulator could use the solution t
     that is closest to the solution in the previous time step
     (\"least squares solution\"). Physically, the solution is determined
     by diffusion which is usually neglected. If diffusion is included,
     the linear system is regular.

"));
    end ShortPipe;

    class OptionalProperties "Optional properties"
      extends Modelica.Icons.Information;
  // use eta in the pressure drop equation: port_a.m_flow = f(dp, eta)
end ShortPipe;

Note, \"Medium.DynamicViscosity\" is a type defined in Modelica.Interfaces.PartialMedium
as

import Modelica.Units.SI;
type DynamicViscosity = SI.DynamicViscosity (
                                   min=0,
                                   max=1.e8,
                                   nominal=1.e-3,
                                   start=1.e-3);

Every medium model may modify the attributes, to provide, e.g.,
min, max, nominal, and start values adapted to the medium.
Also, other types, such as AbsolutePressure, Density, MassFlowRate,
etc. are defined in PartialMedium. Whenever possible, these medium
specific types should be used in a model in order that medium information,
e.g., about nominal or start values, are automatically utilized.

"));
    end OptionalProperties;

    class Constants "Constants"
      extends Modelica.Icons.Information;
    end Constants;

    class TwoPhase "Two-phase media"
      extends Modelica.Icons.Information;
  phase = state1.phase;

  // Force the computation of the state with one-phase
  // equations of state, irrespective of the (p, h) values
  state1 = Medium.setState_ph(p, h, 1);

  // Force the computation of the state with 2-phase
  // equations of state, irrespective of the (p, h) values
  state2 = Medium.setState_ph(p, h, 2);

This feature can be used for the following purposes:

 saving computational time, if one knows in advance the phase of the medium;

 unambiguously determine the phase, when the two inputs correspond to a point on the saturation boundary (the derivative functions have substantially different values on either side);

 get the properties of metastable states, like superheated water or subcooled vapour.

Many additional optional functions are defined to compute properties of
saturated media, either liquid (bubble point) or vapour (dew point).
The argument to such functions is a SaturationProperties record, which can be
set starting from either the saturation pressure or the saturation temperature,
as shown in the following example.

  replaceable package Medium = Modelica.Media.Interfaces.PartialTwoPhaseMedium;
  Medium.SaturationProperties sat_p;
  Medium.SaturationProperties sat_T;
equation
  // Set sat_p to saturation properties at pressure p
  sat_p = Medium.setSat_p(p);

  // Compute saturation properties at pressure p
  saturationTemperature_p = Medium.saturationTemperature_sat(sat_p);
  bubble_density_p =        Medium.bubbleDensity(sat_p);
  dew_enthalpy_p   =        Medium.dewEnthalpy(sat_p);

  // Set sat_T to saturation properties at temperature T
  sat_T = Medium.setSat_T(T);

  // Compute saturation properties at temperature T
  saturationTemperature_T = Medium.saturationPressure_sat(sat_T);
  bubble_density_T =        Medium.bubbleDensity(sat_T);
  dew_enthalpy_T =          Medium.dewEnthalpy(sat_T);

With reference to a model defining a pressure p, a temperature T, and a
SaturationProperties record sat, the following functions are provided:

Function call
      Unit
      Description

Medium.saturationPressure(T)
      Pa
      Saturation pressure at temperature T

Medium.saturationTemperature(p)
      K
      Saturation temperature at pressure p

Medium.saturationTemperature_derp(p)
      K/Pa
      Derivative of saturation temperature with respect to pressure

Medium.saturationTemperature_sat(sat)
      K
      Saturation temperature

Medium.saturationPressure_sat(sat)
      Pa
      Saturation pressure

Medium.bubbleEnthalpy(sat)
      J/kg
      Specific enthalpy at bubble point

Medium.dewEnthalpy(sat)
      J/kg
      Specific enthalpy at dew point

Medium.bubbleEntropy(sat)
      J/(kg.K)
      Specific entropy at bubble point

Medium.dewEntropy(sat)
      J/(kg.K)
      Specific entropy at dew point

Medium.bubbleDensity(sat)
      kg/m3
      Density at bubble point

Medium.dewDensity(sat)
      kg/m3
      Density at dew point

Medium.saturationTemperature_derp_sat(sat)
      K/Pa
      Derivative of saturation temperature with respect to pressure

Medium.dBubbleDensity_dPressure(sat)
      kg/(m3.Pa)
      Derivative of density at bubble point with respect to pressure

Medium.dDewDensity_dPressure(sat)
      kg/(m3.Pa)
      Derivative of density at dew point with respect to pressure

Medium.dBubbleEnthalpy_dPressure(sat)
      J/(kg.Pa)
      Derivative of specific enthalpy at bubble point with respect to pressure

Medium.dDewEnthalpy_dPressure(sat)
      J/(kg.Pa)
      Derivative of specific enthalpy at dew point with respect to pressure

Medium.surfaceTension(sat)
      N/m
      Surface tension between liquid and vapour phase

Sometimes it can be necessary to compute fluid properties in the thermodynamic
plane, just inside or outside the saturation dome. In this case, it is possible
to obtain an instance of a ThermodynamicState state vector, and then use it
to call the additional functions already defined for one-phase media.

Function call
      Description

Medium.setBubbleState(sat, phase)
      Obtain the thermodynamic state vector
          corresponding to the bubble point. If phase==1 (default), the state is
      on the one-phase side; if phase==2, the state is on the two-phase
      side

Medium.setDewState(sat, phase)
      Obtain the thermodynamic state vector
          corresponding to the dew point. If phase==1 (default), the state is
      on the one-phase side; if phase==2, the state is on the two-phase
      side

Here are some examples:

  replaceable package Medium = Modelica.Media.Interfaces.PartialTwoPhaseMedium;
  Medium.SaturationProperties sat;
  Medium.ThermodynamicState   dew_1;    // dew point, one-phase side
  Medium.ThermodynamicState   bubble_2; // bubble point, two phase side
equation
  // Set sat to saturation properties at pressure p
  sat = setSat_p(p);

  // Compute dew point properties, (default) one-phase side
  dew_1 = setDewState(sat);
  cpDew = Medium.specificHeatCapacityCp(dew_1);
  drho_dp_h_1 = Medium.density_derp_h(dew_1);

  // Compute bubble point properties, two-phase side
  bubble_2    = setBubbleState(sat, 2);
  drho_dp_h_2 = Medium.density_derp_h(bubble_2);

"));
    end TwoPhase;

    class Initialization "Initialization"
      extends Modelica.Icons.Information;
   Medium.BaseProperties medium2;
initial equation
   if not Medium.singleState then
      medium2.p = 1e5;
   end if;
   medium2.T = 300;
equation

If initial conditions are not provided for the independent
medium variables, non-linear systems of equations may
occur to compute the initial values of the independent
medium variables from the provided initial conditions.

Guess values

If non-linear systems of equations occur during initialization,
e.g., in case of steady state initialization, guess values
for the iteration variables of the non-linear system of equations
have to be provided via the \"start\" attribute (and fixed=false).
Unfortunately, it is usually not known in
advance which variables are selected as iteration variables of
a non-linear system of equations. One of the following possibilities
exist:

 Do not supply start values and hope that the medium specific
     types have meaningful start values, such as in \"Medium.AbsolutePressure\"

 Supply start values on all variables of the BaseProperties model,
     i.e., on p, T, d, u, h, Xi.

 Determine the iteration variables of the non-linear systems of
     equations and provide start values for these variables.
     In the Modelica simulation environment Dymola, the iteration
     variables can be determined by setting the command
     Advanced.OutputModelicaCode = true
     and by inspection of the file \"dsmodel.mof\" that is generated
     when this option is set (search for \"nonlinear\").

"));
    end Initialization;
  end MediumUsage;

  package MediumDefinition "Medium definition"
    extends Modelica.Icons.Information;

    class BasicStructure "Basic structure"
      extends Modelica.Icons.Information;
  constant Boolean          singleState = false;
  constant Boolean          reducedX = true;
  constant Boolean          fixedX = false;
  constant AbsolutePressure reference_p = 101325;
  constant MassFraction     reference_X[nX]=fill(1/nX,nX);
  constant AbsolutePressure p_default = 101325;
  constant Temperature      T_default = Modelica.Units.Conversions.from_degC(20);
  constant SpecificEnthalpy h_default =
                            specificEnthalpy_pTX(p_default, T_default, X_default);
  constant MassFraction     X_default[nX]=reference_X;
  final constant Integer    nS  = size(substanceNames,1);
  final constant Integer    nX  = nS;
  final constant Integer    nXi = if fixedX then 0
                                  else if reducedX or nS == 1
                                  then nS-1 else nS;
  final constant Integer    nC  = size(extraPropertiesNames,1);
  constant FluidConstants[nS] fluidConstants;

  replaceable record BasePropertiesRecord
    AbsolutePressure p;
    Density d;
    Temperature T;
    SpecificEnthalpy h;
    SpecificInternalEnergy u;
    MassFraction[nX] X;
    MassFraction[nXi] Xi;
    SpecificHeatCapacity R_s;
    MolarMass MM;
  end BasePropertiesRecord;

  replaceable partial model BaseProperties
    extends BasePropertiesRecord;
    ThermodynamicState state;
    parameter Boolean preferredMediumStates=false;
    Modelica.Units.NonSI.Temperature_degC T_degC =
       Modelica.Units.Conversions.to_degC(T)
    Modelica.Units.NonSI.Pressure_bar p_bar =
       Modelica.Units.Conversions.to_bar(p)
  equation
    Xi = X[1:nXi];
    if nX > 1 then
       if fixedX then
          X = reference_X;
       elseif reducedX then
          X[nX] = 1 - sum(Xi);
       end if;
    end if;
    // equations such as
    //    d = d(p,T);
    //    u = u(p,T);
    //    h = u + p/d;
    //    state.p = p;
    //    state.T = T;
    // will go here in actual media implementations, but are not present
    // in the base class since the ThermodynamicState record is still empty
   end BaseProperties

  replaceable record ThermodynamicState
     // there are no \"standard\" thermodynamic variables in the base class
     // but they will be defined here in actual media extending PartialMedium
     // Example:
     //    AbsolutePressure p \"Absolute pressure of medium\";
     //    Temperature      T \"Temperature of medium\";
  end ThermodynamicState;

  // optional medium properties
  replaceable partial function dynamicViscosity
    input  ThermodynamicState state;
    output DynamicViscosity eta;
  end dynamicViscosity;

  // other optional functions

  // medium specific types
  type AbsolutePressure = SI.AbsolutePressure (
                               min     = 0,
                               max     = 1.e8,
                               nominal = 1.e5,
                               start   = 1.e5);
  type DynamicViscosity = ...;
  // other type definitions
end PartialMedium;

We will discuss all parts of this package in the
following paragraphs. An actual medium model should extend from PartialMedium
and has to provide implementations of the various parts.

Some of the constants at the beginning of the package do not have a value yet
(this is valid in Modelica), but a value has to be provided when extending from
package PartialMedium. A given value can be modified until the model is
translated or the final prefix is set.
The reason to use constants instead of parameters in the model BaseProperties
is that some of these constants are used in a context where parameters
are not allowed. For example, in connector definitions the
number of independent mass fractions nXi is used as dimension
of a vector Xi. When defining the
connector, only constants in packages can be accessed, but not
parameters in a model, because a connector cannot contain an instance
of BaseProperties.

The record BasePropertiesRecord contains the variables
primarily used in balance equations. Three equations for these variables have
to be provided by every medium in model BaseProperties, plus two equations
for the gas constant and the molar mass.

Optional medium properties are defined by functions, such as the function
dynamicViscosity (see code Section above) to compute the dynamic viscosity.
The argument of those functions is the ThermodynamicState record, defined
in BaseProperties, which contains the minimum number of thermodynamic variables
needed as an input to compute all the optional properties.
This construction simplifies the usage
considerably as demonstrated in the following code fragment:

replaceable package Medium = Modelica.Media.Interfaces.PartialMedium;
Medium.BaseProperties   medium;
Medium.DynamicViscosity eta;
...
U   = m*medium.u; //Internal energy
eta = Medium.dynamicViscosity(medium.state);

Medium is the medium package that satisfies the
requirements of a PartialMedium (when using the model above, a value for
Medium has to be provided by a redeclaration). The medium component is an
instance of the model Medium.BaseProperties and contains the core medium
equations. Variables in this model can be accessed just by dot-notation, such
as medium.u or medium.T. If an optional medium variable has to be computed, the
corresponding function from the actual Medium package is called, such as
Medium.dynamicViscosity. The medium.state vector can be given as input argument
to this function, and its fields are kept consistent to those of BaseProperties
by suitable equations, contained in BaseProperties itself (see above).

If a medium model does not provide implementations of all
optional functions and one of these functions is called in a model, an error
occurs during translation since the optional functions which have not been
redeclared have the partial attribute. For example, if function
dynamicViscosity is not provided in the medium model when it is used, only
simple pressure drop loss models without a reference to the viscosity can be
used and not the sophisticated ones.

At the bottom of the PartialMedium package type declarations
are present, that are used in all other parts of the PartialMedium package and
that should be used in all models and connectors where a medium model is
accessed. The reason is that minimum, maximum, nominal, and start
values are defined and these values can be adapted to the particular medium at
hand. For example, the nominal value of AbsolutePressure is 105
Pa. If a simple model of water steam is used that is only valid above 100 °C,
then the minimum value in the Temperature type should be set to this value. The
minimum and maximum values are also important for parameters in order to get an
early message if data outside of the validity region is given. The nominal
attribute is important as a scaling value if the variable is used as a state in
a differential equation or as an iteration variable in a non-linear system of
equations. The start attribute can be very useful to provide a meaningful
default start or guess value if the variable is used, e.g., as iteration
variable in a non-linear system of equations. Note, that all these attributes
can be set specifically for a medium in the following way:

package MyMedium
  extends Modelica.Media.Interfaces.PartialMedium(
     ...
     Temperature(min=373));
end MyMedium;

The type PartialMedium.MassFlowRate is defined as

type MassFlowRate = SI.MassFlowRate
     (quantity = \"MassFlowRate.\" + mediumName);

Note that the constant mediumName, that has to be
defined in every medium model, is used in the quantity attribute. For example,
if mediumName = SimpleLiquidWater, then the quantity attribute has the value
MassFlowRate.SimpleLiquidWater. This type should be used in a connector
definition of a fluid library:

connector FluidPort
  replaceable package Medium = Modelica.Media.Interfaces.PartialMedium;
  flow Medium.MassFlowRate m_flow;
  ...
end FluidPort;

In the model where this connector is used, the actual
Medium has to be defined. Connectors can only be connected together, if the
corresponding attributes are either not defined or have identical values. Since
mediumName is part of the quantity attribute of MassFlowRate, it is not
possible to connect connectors with different media models together.

"));
    end BasicStructure;

    class BasicDefinition "Basic definition"
      extends Modelica.Icons.Information; fast states
    resulting from the low compressibility effects are automatically avoided.

reducedX = true for single-substance media, which do not need mass
    fractions at all.

It is also possible to change the default min, max, nominal, and start
attributes of Medium-defined types (see TemplateMedium).

All other package constants, such as nX, nXi, nS, are automatically set
by the declarations of the base package Interfaces.PartialMedium.

The second step is to provide an implementation to the BaseProperties model,
partially defined in the base class Interfaces.PartialMedium. In the case of
single-substance media, two independent state variables must be selected among
p, T, d, u, h, and three equations must be written to provide the values of
the remaining variables. Two equations must then be added to compute the molar
mass MM and the gas constant R_s.

The third step is to consider the optional functions that are going to be
implemented, among the partial functions defined by the base class PartialMedium.
A minimal set of state variables that could be provided as an input to
all those functions must be selected, and included in the redeclaration
of the ThermodynamicState record. Subsequently, equations must be added to
BaseProperties in order that the instance of that record inside BaseProperties
(named \"state\") is kept updated. For example, assume that all additional
properties can be computed as a function of p and T. Then, ThermodynamicState
should be redeclared as follows:

redeclare replaceable record ThermodynamicState
  AbsolutePressure p \"Absolute pressure of medium\";
  Temperature T \"Temperature of medium\";
end ThermodynamicState;

and the following equations should be added to BaseProperties:

state.p = p;
state.T = T;

The additional functions can now be implemented by redeclaring the functions
defined in the base class and adding their algorithms, e.g.:

redeclare function extends dynamicViscosity \"Return dynamic viscosity\"
algorithm
  eta := 10 - state.T*0.3 + state.p*0.2;
end dynamicViscosity;

"));
    end BasicDefinition;

    class MultipleSubstances "Multiple Substances"
      extends Modelica.Icons.Information;
    end MultipleSubstances;

    class SpecificEnthalpyAsFunction "Specific enthalpy as function"
      extends Modelica.Icons.Information;

The reason for this rule requires a longer explanation.
In short, if h is not a computed by a Modelica function and
this function is non-linear in the independent medium variables,
then non-linear systems of equations will occur at
every connection point, if the FluidPort connectors from the
Modelica.Fluid library are used (these are the same as in
Modelica.Media.Examples.Utilities.FluidPort).
Only, if the above rule is fulfilled, a tool is able to
remove these non-linear system of equations in most cases.

The basic idea of the FluidPort connector is that 2 or more
components can be connected together at a point and that
automatically the mass and energy balance is fulfilled
in the connection point, i.e., the ideal mixing equations
are generated. Note, the momentum balance is only correct for
straight line connections. If \"ideal mixing\" is not sufficient,
a special component to define the mixing equations must be introduced.

The mass and momentum balance equations in a component are
derived from the partial differential equations along the
flow direction of a pipe:

Note, FF is the fanning friction factor.
The energy balance can be given in different forms.
Usually, it is given as:

This form describes the change of the internal energy, kinetic
energy and potential energy of a volume as function of the
in and out flowing fluid. Multiplying the momentum balance
with the flow velocity v and subtracting it from the energy
balance above, results in the following alternative form
of the energy balance:

This form has the advantage that the kinetic and potential
energy is no longer part of the energy balance and therefore
the energy balance is substantially simpler (e.g., additional non-linear
systems of equations occur in the first form since the velocity
is present in the energy balance; in the second form this is not
the case and it is still valid also for high speeds).

Assume now that the second form of the energy balance above
is used in all components and that the following FluidPort connector
is used in all components:

connector FluidPort
  replaceable package Medium = Modelica.Media.Interfaces.PartialMedium;
  Medium.AbsolutePressure      p;
  flow Medium.MassFlowRate     m_flow;

  Medium.SpecificEnthalpy      h;
  flow Medium.EnthalpyFlowRate H_flow;

  Medium.MassFraction          Xi    [Medium.nXi];
  flow Medium.MassFlowRate     mX_flow[Medium.nXi];
end FluidPort;

As an example, assume that 3 components
are connected together and that the medium is a single substance
fluid. This will result in the following
connection equations:

p1=p2=p3;
h1=h2=h3;
0 = m_flow1 + m_flow2 + m_flow3;
0 = H_flow1 + H_flow2 + H_flow3;

These are the mass balance and the
energy balance (form 2) of an infinitesimal volume
in the connection point under the assumption that no
mass or energy is stored in this volume. In other words,
the connection equations are the equations that describe
ideal mixing. Under the assumption that the velocity
vectors of the 3 flows are identical (especially, they are
parallel), also the momentum balance is fulfilled:

0 = m_flow1*v1 + m_flow2*v2 + m_flow3*v3;
  = v*(m_flow1 + m_flow2 + m_flow3);
  = 0;

With the above connector it is therefore possible to
connect components together in a nearly arbitrary fashion,
because every connection fulfills automatically the
balance equations. This approach has, however, one drawback:
If two components are connected together, then the medium
variables on both sides of the connector are identical.
However, due to the connector, only the two equations

p1 = p2;
h1 = h2;

are present. Assume, that p,T are the independent medium variables
and that the medium properties are computed at one side of the
connections. This means, the following equations are basically
present:

h1 = h(p1,T1);
h2 = h(p2,T2);
p1 = p2;
h1 = h2;

These equations can be solved in the following way:

h1 := h(p1,T1)
p2 := p1;
h2 := h1;
0  := h2 - h(p2,T2);   // non-linear system of equations for T2

This means that T2 is computed by solving a non-linear system
of equations. If h1 and h2 are provided as Modelica functions,
a Modelica translator can replace
this non-linear system of equations by the equation:

T2 := T1;

because after alias substitution there are two function calls

h1 := h(p1,T1);
h1 := h(p1,T2);

Since the left hand side of the function call and the first
argument are the same, the second arguments T1 and T2 must also be
identical and therefore T2 := T1. This type of analysis seems
to be only possible, if the specific enthalpy is defined as a function
of the independent medium variables.

"));
    end SpecificEnthalpyAsFunction;

    class StaticStateSelection "Static State Selection"
      extends Modelica.Icons.Information;
     u = h - R_s*T;
     p = d*R_s*T;
    ...
end BaseProperties;

If p, T are preferred states, these equations are not
written in the recommended form, because d is not a
function of p and T. If p,T would be states, it would be
necessary to solve for the density:

   d = p/(R_s*T)

If T or R_s are zero, this results in a division by zero.
A tool does not know that R_s or T cannot become zero.
Therefore, a tool must assume that p, T cannot always be
selected as states and has to either use another static
state selection or use dynamic state selection. The only
other choice for static state selection is d,T, because
h,u,p are given as functions of d,T.
However, as potential states only variables appearing differentiated and variables
declared with StateSelect.prefer or StateSelect.always
are used. Since \"d\" does not appear differentiated and has
StateSelect.default, it cannot be selected as a state.
As a result, the tool has to select states dynamically
during simulation. Since the equations above are non-linear
and they are utilized in the dynamic state
selection, a non-linear system of equations is present
in every balance volume.

To summarize, for single substance ideal gas media there
are the following two possibilities to get static
state selection and linear systems of equations:

 Use p,T as preferred states and write the equation
     for d in the form: d = p/(T*R_s)

 Use d,T as preferred states and write the equation
     for p in the form: p = d*T*R_s

All other settings (other/no preferred states etc.) lead
to dynamic state selection and non-linear systems of
equations for a balance volume.

Multiple Substance Media

A medium consisting of multiple substance
has to define two of \"p,T,d,u,h\" as well
as the mass fractions Xi with
stateSelect=StateSelect.prefer (if BaseProperties.preferredMediumStates = true)
and has to provide
the other three variables as functions of these
states. Only then, static selection is possible
for a tool.

Example for a multiple substance medium:

p, T and Xi are defined as preferred states and
the equations are written in the form:

d = fp(p,T,Xi);
u = fu(p,T,Xi);
h = fh(p,T,Xi);

Since the balance equations are written in the form:

  M = V*medium.d;
MXi = M*medium.Xi;

The variables M and MXi appearing differentiated in the
balance equations are provided as functions of d and Xi
and since d is given as a function of p, T and Xi, it
is possible to compute M and MXi directly from the desired
states. This means that static state selection is possible.

"));
    end StaticStateSelection;

    class TestOfMedium "Test of medium"
      extends Modelica.Icons.Information;
end TestOfMyMedium;

It might be necessary to adapt or change initial values
depending on the validity range of the medium.
The model above should translate and simulate.
If the medium model is written according to the
suggestions given in the previous sections (and the Modelica
translator has appropriate algorithms implemented),
there should be only static state selection everywhere
and no non-linear system of equations, provided h is an independent
medium variable or is only a function of T. If h is a function
of, say h=h(p,T), one non-linear system of equations occurs that
cannot be avoided.

The test model above can be used to test the most basic
properties. Of course, more tests should be performed.

"));
    end TestOfMedium;
  end MediumDefinition;

  class ReleaseNotes "Release notes"
    extends Modelica.Icons.ReleaseNotes;
  end ReleaseNotes;

  class Contact "Contact"
    extends Modelica.Icons.Contact;
  end Contact;
end UsersGuide;

package Examples
  "Demonstrate usage of property models"

  extends Modelica.Icons.ExamplesPackage;

  model SimpleLiquidWater "Example for Water.SimpleLiquidWater medium model"
    extends Modelica.Icons.Example;

    constant SI.PressureSlope pressureRate = 1e5/10;
    parameter SI.Volume V=1 "Volume";
    parameter SI.EnthalpyFlowRate H_flow_ext=1.e6
      "Constant enthalpy flow rate into the volume";

    package Medium = Water.ConstantPropertyLiquidWater (SpecificEnthalpy(max=
              1e6)) "Medium model";
    Medium.BaseProperties medium(T(start=300, fixed=true));

    Medium.BaseProperties medium2;
    Medium.ThermodynamicState state;
    Real m_flow_ext2;
    Real der_p;
    Real der_T;

    SI.Mass m(start=1.0);
    SI.InternalEnergy U;

    // Use type declarations from the Medium
    Medium.MassFlowRate m_flow_ext;
    Medium.DynamicViscosity eta=Medium.dynamicViscosity(state);
    Medium.SpecificHeatCapacity cv=Medium.specificHeatCapacityCv(state);
  equation
    medium.p = 1.0e5;
    m = medium.d*V;
    U = m*medium.u;

    // Mass balance
    der(m) = m_flow_ext;

    // Energy balance
    der(U) = H_flow_ext;

    // Smooth state
    medium2.p = pressureRate*time;
    medium2.T = 330;
    m_flow_ext2 = time - 30;
    state = Medium.setSmoothState(
          m_flow_ext2,
          medium.state,
          medium2.state,
          10);
    der_p = der(state.p);
    der_T = der(state.T);
  end SimpleLiquidWater;

  model IdealGasH2O "IdealGas H20 medium model"
    extends Modelica.Icons.Example;
    package Medium = IdealGases.SingleGases.H2O "Medium model";
    Medium.ThermodynamicState state "Thermodynamic state record";
    Medium.ThermodynamicState state2;
    Medium.SpecificHeatCapacity cp=Medium.specificHeatCapacityCp(state);
    Medium.SpecificHeatCapacity cv=Medium.specificHeatCapacityCv(state);
    Medium.IsentropicExponent k=Medium.isentropicExponent(state);
    Medium.SpecificEntropy s=Medium.specificEntropy(state);
    //  Medium.SpecificEntropy s2=Medium.specificEntropy(state2);
    Medium.VelocityOfSound a=Medium.velocityOfSound(state);
    Real beta=Medium.isobaricExpansionCoefficient(state);
    Real gamma=Medium.isothermalCompressibility(state);
    Medium.SpecificEnthalpy h_is=Medium.isentropicEnthalpyApproximation(2.0,
        state);

    Medium.ThermodynamicState smoothState;
    Real m_flow_ext;
    Real der_p;
    Real der_T;
  protected
    parameter SI.AbsolutePressure p01 = 100000.0 "state.p at time 0";
    parameter SI.PressureSlope pRate1 = 0 "state.p rate of change";
    parameter SI.Temperature T01 = 200 "state.T at time 0";
    parameter SI.TemperatureSlope Trate1 = 1000 "state.T rate of change";
    parameter SI.AbsolutePressure p02 = 2.0e5 "state2.p at time 0";
    parameter SI.PressureSlope pRate2 = 0 "state2.p rate of change";
    parameter SI.Temperature T02 = 500 "state2.T at time 0";
    parameter SI.TemperatureSlope Trate2 = 0 "state2.T rate of change";

  equation
    state.p = p01 + pRate1*time;
    state.T = T01 + Trate1*time;
    state2.p = p02 + pRate2*time;
    state2.T = T02 + Trate2*time;
    //  s2 = s;

    // Smooth state
    m_flow_ext = time - 0.5;
    smoothState = Medium.setSmoothState(
          m_flow_ext,
          state,
          state2,
          0.1);
    der_p = der(smoothState.p);
    der_T = der(smoothState.T);
  end IdealGasH2O;

  model WaterIF97 "WaterIF97 medium model"
    extends Modelica.Icons.Example;
    package Medium = Water.StandardWater "Medium model";
    Medium.BaseProperties medium(
      p(start=1.e5,
        fixed=true,
        stateSelect=StateSelect.prefer),
      h(start=1.0e5,
        fixed=true,
        stateSelect=StateSelect.prefer),
      T(start=275.0),
      d(start=999.0));
    SI.Volume V(start=0.1, fixed=true);
    parameter SI.VolumeFlowRate dV=0.0
      "Fixed time derivative of volume";
    parameter Medium.MassFlowRate m_flow_ext=0
      "Fixed mass flow rate into volume";
    parameter Medium.EnthalpyFlowRate H_flow_ext=10000
      "Fixed enthalpy flow rate into volume";
    SI.Mass m "Mass of volume";
    SI.InternalEnergy U "Internal energy of volume";

    Medium.ThermodynamicState state2;
    Medium.ThermodynamicState state;
    Real m_flow_ext2;
    Real der_p;
    Real der_T;

  equation
    der(V) = dV;
    m = medium.d*V;
    U = m*medium.u;

    // Mass balance
    der(m) = m_flow_ext;

    // Energy balance
    der(U) = H_flow_ext;

    // smooth states
    m_flow_ext2 = time - 0.5;
    state2 = Medium.setState_pT(1e5*(1 + time), 300 + 200*time);
    state = Medium.setSmoothState(
          m_flow_ext2,
          medium.state,
          state2,
          0.05);
    der_p = der(state.p);
    der_T = der(state.T);
  end WaterIF97;

  model MixtureGases "Test gas mixtures"
    extends Modelica.Icons.Example;

    parameter SI.Volume V=1 "Fixed size of volume 1 and volume 2";
    parameter SI.MassFlowRate m_flow_ext=0.01
      "Fixed mass flow rate into volume 1 and into volume 2";
    parameter SI.EnthalpyFlowRate H_flow_ext=5000
      "Fixed enthalpy flow rate into volume 1 and into volume 2";

    package Medium1 = Modelica.Media.IdealGases.MixtureGases.CombustionAir
      "Medium model";
    Medium1.BaseProperties medium1(
      p(start=1.e5,
        fixed=true,
        stateSelect=StateSelect.prefer),
      T(start=300,
        fixed=true,
        stateSelect=StateSelect.prefer),
      X(start={0.8,0.2}));
    SI.Mass m1(start=1.0) "Mass of volume 1";
    SI.InternalEnergy U1;
    Medium1.SpecificHeatCapacity cp1=Medium1.specificHeatCapacityCp(medium1.state);
    Medium1.DynamicViscosity eta1=Medium1.dynamicViscosity(medium1.state);
    Medium1.ThermalConductivity lambda1=Medium1.thermalConductivity(medium1.state);

    package Medium2 = Modelica.Media.IdealGases.MixtureGases.SimpleNaturalGas
      "Medium model";
    Medium2.BaseProperties medium2(
      p(start=1.e5,
        fixed=true,
        stateSelect=StateSelect.prefer),
      T(start=300,
        fixed=true,
        stateSelect=StateSelect.prefer),
      X(start={0.1,0.1,0.1,0.2,0.2,0.3}));
    SI.Mass m2(start=1.0) "Mass of volume 2";
    SI.InternalEnergy U2;
    Medium2.SpecificHeatCapacity cp2=Medium2.specificHeatCapacityCp(medium2.state);
    Medium2.DynamicViscosity eta2=Medium2.dynamicViscosity(medium2.state);
    Medium2.ThermalConductivity lambda2=Medium2.thermalConductivity(medium2.state);

    Medium2.ThermodynamicState state2=Medium2.setState_pTX(
            1.005e5,
            302,
            {0.3,0.2,0.2,0.1,0.1,0.1});
    Medium2.ThermodynamicState smoothState;
    Real m_flow_ext2;
    Real der_p;
    Real der_T;

  equation
    medium1.X = {0.8,0.2};
    m1 = medium1.d*V;
    U1 = m1*medium1.u;
    der(m1) = m_flow_ext;
    der(U1) = H_flow_ext;

    medium2.X = {0.1,0.1,0.1,0.2,0.2,0.3};
    m2 = medium2.d*V;
    U2 = m2*medium2.u;
    der(m2) = m_flow_ext;
    der(U2) = H_flow_ext;

    // Smooth state
    m_flow_ext2 = time - 0.5;
    smoothState = Medium2.setSmoothState(
          m_flow_ext2,
          medium2.state,
          state2,
          0.2);
    der_p = der(smoothState.p);
    der_T = der(smoothState.T);
  end MixtureGases;

  model MoistAir "Example for moist air"
    extends Modelica.Icons.Example;
    package Medium = Air.MoistAir "Medium model";
    Medium.BaseProperties medium(
      T(start=274.0, fixed=true),
      X(start={0.95,0.05}),
      p(start=1.0e5, fixed=true));
    //  Medium.SpecificEntropy s=Medium.specificEntropy(medium);
    //  Medium.SpecificEnthalpy h_is = Medium.isentropicEnthalpyApproximation(medium, 2.0e5);
    parameter Medium.MolarMass[2] MMx={Medium.dryair.MM,Medium.steam.MM}
      "Vector of molar masses (consisting of dry air and of steam)";
    Medium.MolarMass MM=1/((1 - medium.X[1])/MMx[1] + medium.X[1]/MMx[2])
      "Molar mass of gas part of mixture";
    //  Real[4] dddX=Medium.density_derX(medium,MM);

    Medium.ThermodynamicState state1;
    Medium.ThermodynamicState state2;
    Medium.ThermodynamicState smoothState;
    Real m_flow_ext;
    Real der_p;
    Real der_T;
  protected
    constant SI.Time unitTime=1;
    parameter SI.AbsolutePressure p01 = 1.e5 "state1.p at time 0";
    parameter SI.PressureSlope pRate1 = 1.e5 "state1.p rate of change";
    parameter SI.Temperature T01 = 300 "state1.T at time 0";
    parameter SI.TemperatureSlope Trate1 = 10 "state1.T rate of change";
    parameter SI.AbsolutePressure p02 = 1.e5 "state2.p at time 0";
    parameter SI.PressureSlope pRate2 = 1.e5/2 "state2.p rate of change";
    parameter SI.Temperature T02 = 340 "state2.T at time 0";
    parameter SI.TemperatureSlope Trate2 = -20 "state2.T rate of change";
  equation
    der(medium.p) = 0.0;
    der(medium.T) = 90;
    medium.X[Medium.Air] = 0.95;
    //    medium.X[Medium.Water] = 0.05;
    // one simple assumption only for quick testing:
    //  medium.X_liquidWater = if medium.X_sat < medium.X[2] then medium.X[2] - medium.X_sat else 0.0;

    // Smooth state
    m_flow_ext = time/unitTime - 0.5;
    state1.p = p01 + pRate1*time;
    state1.T = T01 + Trate1*time;
    state1.X = {time,1 - time}/unitTime;
    state2.p = p02 + pRate2*time;
    state2.T = T02 + Trate2*time;
    state2.X = {0.5*time,1 - 0.5*time}/unitTime;
    smoothState = Medium.setSmoothState(
          m_flow_ext,
          state1,
          state2,
          0.2);
    der_p = der(smoothState.p);
    der_T = der(smoothState.T);
  end MoistAir;

  model PsychrometricData "Produces plot data for psychrometric charts"
    extends Modelica.Icons.Example;
    package Medium = Modelica.Media.Air.MoistAir "Used medium package";
    parameter SI.Pressure p_const=1e5 "Pressure";
    parameter Integer n_T=11 "Number of isotherms";
    parameter SI.Temperature T_min=253.15 "Lowest isotherm";
    parameter SI.Temperature T_step=10
      "Temperature step between two isotherms";
    parameter Integer n_h=16 "Number of lines with constant specific enthalpy";
    parameter SI.SpecificEnthalpy h_min=-20e3
      "Lowest line of constant enthalpy";
    parameter SI.SpecificEnthalpy h_step=1e4
      "Enthalpy step between two lines of constant enthalpy";
    parameter Integer n_phi=10
      "Number of lines with constant relative humidity";
    parameter Real phi_min=0.1 "Lowest line of constant humidity";
    parameter Real phi_step=0.1 "Step between two lines of constant humidity";
    parameter SI.MassFraction x_min=0.00
      "Minimum diagram absolute humidity";
    parameter SI.MassFraction x_max=0.03
      "Maximum diagram absolute humidity";
    parameter SI.Time t=1 "Simulation time";

    final parameter SI.Temperature[n_T] T_const={T_min - T_step + i*
        T_step for i in 1:n_T} "Constant temperatures";
    final parameter SI.SpecificEnthalpy[n_h] h_const={(i - 1)*h_step +
        h_min for i in 1:n_h} "Constant enthalpies";
    final parameter Real[n_phi] phi_const={(i - 1)*phi_step + phi_min for i in
            1:n_phi} "Constant relative humidities";
    final parameter Real diagSlope=Medium.enthalpyOfVaporization(273.15)
      "Rotation of diagram that zero degrees isotherm becomes horizontal outside the fog region";
    final parameter SI.MassFraction x_start=x_min
      "Initial absolute humidity in kg water/kg dry air";

    SI.MassFraction x(start=x_start)
      "Absolute humidity in kg water/kg dry air";
    SI.SpecificEnthalpy[n_T] hx_T "h_1+x for constant T";
    SI.SpecificEnthalpy[n_h] hx_h(start=h_const, each fixed=true)
      "Constanst h_1+x";
    SI.SpecificEnthalpy[n_phi] hx_phi "h_1+x for constanst phi";
    SI.SpecificEnthalpy[n_T] y_T "Chart enthalpy for constant T";
    SI.SpecificEnthalpy[n_h] y_h "Chart enthalpy for constant h";
    SI.SpecificEnthalpy[n_phi] y_phi "Chart enthalpy for constant phi";
    Medium.BaseProperties[n_T] medium_T "Medium properties for constant T";
    Medium.BaseProperties[n_phi] medium_phi "Medium properties for constant phi";

  protected
    SI.Pressure[n_phi] ps_phi "Saturation pressure for constant-phi-lines";
    SI.Temperature[n_phi] T_phi(each start=290);
    Boolean[n_T] fog(start=fill(false, n_T))
      "Triggers events at intersection of isotherms with phi=1";
    SI.Pressure[n_T] pd "Steam partial pressure along isotherms";
  initial equation
    x = x_min;
  equation

    der(x) = (x_max - x_min)/t;

    for i in 1:n_T loop
      medium_T[i].T = T_const[i];
      medium_T[i].p = p_const;
      medium_T[i].Xi = {x/(1 + x)};
      hx_T[i] = medium_T[i].h*(medium_T[i].x_water + 1);
      y_T[i] = hx_T[i] - diagSlope*x;

      //trigger events
      pd[i] = medium_T[i].Xi[1]*medium_T[i].MM/Medium.MMX[1]*p_const;
      fog[i] = pd[i] >= Medium.saturationPressure(T_const[i]);
    end for;
    for i in 1:n_h loop
      der(hx_h[i]) = 0.0;
      y_h[i] = hx_h[i] - diagSlope*x;
    end for;
    for i in 1:n_phi loop
      medium_phi[i].p = p_const;
      ps_phi[i] = p_const*x/phi_const[i]/(Medium.k_mair + x);
      T_phi[i] = if x < 5e-6 then 200 else Medium.saturationTemperature(
        ps_phi[i]);
      medium_phi[i].T = T_phi[i];
      medium_phi[i].Xi = {x/(1 + x)};
      hx_phi[i] = medium_phi[i].h*(medium_phi[i].x_water + 1);
      y_phi[i] = hx_phi[i] - diagSlope*x;
    end for;
  end PsychrometricData;

  package TwoPhaseWater "Extension of the StandardWater package"
    extends Modelica.Media.Water.StandardWater;
    redeclare model extends BaseProperties
      "Make StandardWater.BaseProperties non replaceable in order that inheritance is possible in model ExtendedProperties"
    end BaseProperties;

    model ExtendedProperties "Plenty of two-phase properties"
      extends BaseProperties;
      ThermodynamicState dew "Dew line Properties";
      ThermodynamicState bubble "Bubble line Properties";
      ThermodynamicState bubble2 "Bubble line Properties, on the 2-phase side";
      DynamicViscosity eta "Viscosity (McAdams mixture rules if in 2-phase)";
      DynamicViscosity eta_d "Dew line viscosity";
      DynamicViscosity eta_b "Bubble line viscosity";
      ThermalConductivity lambda_d "Dew line thermal conductivity";
      ThermalConductivity lambda_b "Bubble line thermal conductivity";
      SpecificHeatCapacity cp_d "Dew line Specific heat capacity";
      SpecificHeatCapacity cp_b "Bubble line Specific heat capacity";
      Real ddhp;
      Real ddhp_d;
      Real ddhp_b "Derivatives";
      Real ddph;
      Real ddph_d;
      Real ddph_b "Derivatives";
      Real ddhp_b2;
      Real ddph_b2 "Derivatives";
      // no derivatives yet, ... sat should be temporary
      MassFraction x "Steam mass fraction";
      Real dTp;
      Real dTp2;
      SpecificEntropy s_b;
      SpecificEntropy s_d;
    equation
      eta = if phase == 1 then dynamicViscosity(state) else 1/(x/eta_d + (1 - x)
        /eta_b);
      dew = setDewState(sat);
      bubble = setBubbleState(sat);
      bubble2 = setBubbleState(sat, 2);
      x = (h - bubble.h)/max(dew.h - bubble.h, 1e-6);
      eta_d = dynamicViscosity(dew);
      eta_b = dynamicViscosity(bubble);
      lambda_d = thermalConductivity(dew);
      lambda_b = thermalConductivity(bubble);
      cp_d = specificHeatCapacityCp(dew);
      cp_b = specificHeatCapacityCp(bubble);
      s_d = specificEntropy(dew);
      s_b = specificEntropy(bubble);
      ddph = density_derp_h(state);
      ddph_d = density_derp_h(dew);
      ddph_b = density_derp_h(bubble);
      ddhp = density_derh_p(state);
      ddhp_d = density_derh_p(dew);
      ddhp_b = density_derh_p(bubble);
      ddhp_b2 = density_derh_p(bubble2);
      ddph_b2 = density_derp_h(bubble2);
      dTp = saturationTemperature_derp(p);
      dTp2 = (1/dew.d - 1/bubble.d)/max(s_d - s_b, 1e-6);
    end ExtendedProperties;

    model TestTwoPhaseStates "Test the TwoPhaseWater model"
      extends Modelica.Icons.Example;
      ExtendedProperties medium(p(start=2000.0, fixed=true), h(start=8.0e5,
            fixed=true));
      parameter Real dh(unit="J/(kg.s)", displayUnit="kJ/(kg.s)") = 80000.0 "Derivative of specific enthalpy of medium";
      parameter SI.PressureSlope dp = 1.0e6 "Derivative of pressure of medium";
    equation
      der(medium.p) = dp;
      der(medium.h) = dh;
    end TestTwoPhaseStates;\">DynamicViscosity
eta\".

Compute that variable by calling the function form the package,
e.g., eta =
dynamicViscosity(state). Note that the instance of
ThermodynamicState is used as an input to the function. This instance
\"state\" is declared in PartialMedium and thus available in every medium
model. A user does not have to know what actual variables are required
to compute the dynamic viscosity, because the state instance is
guaranteed to contain what is needed.

Attention: Many
properties are not well defined in the two phase region and the
functions might return undesired values if called there. It is the
user's responsibility  to take care of such situations. The example
uses one of several possible models to compute an averaged viscosity
for two-phase flows.

In two phase models, properties are often needed on the phase boundary
just outside the two phase dome, right on the border.. To compute the
thermodynamic state there, two auxiliary functions are provided: setDewState(sat) and setBubbleState(sat). They take an
instance of SaturationProperties as input. By default they are in
one-phase, but with the optional phase argument set to 2, the output is
forced to be just inside the phase boundary. This is only needed when
derivatives like cv are computed with are different on both sides of
the boundaries. The usual steps to compute properties on the phase
boundary are:

Declare an instance of ThermodynamicState, e.g., \"ThermodynamicState  dew\".

Compute the state, using an instance of SaturationProperties,
e.g., dew = setDewState(sat)

Compute properties on the phase boundary to your full desire,
e.g., \"cp_d = specificHeatCapacityCp(dew)\".

The sample model TestTwoPhaseStates test the extended properties

The same procedure can be used to compute properties at other state
points, e.g., when an isentropic reference state is computed.

"));
  end TwoPhaseWater;

  package ReferenceAir
    "Examples for detailed dry air and moist air medium models"
    extends Modelica.Icons.ExamplesPackage;

    model DryAir1 "Example 1 for dry air"
      extends Modelica.Icons.Example;
      extends Modelica.Media.Examples.Utilities.PartialTestModel(
          redeclare package Medium = Modelica.Media.Air.ReferenceAir.Air_pT);
    end DryAir1;

    model DryAir2 "Example 2 for dry air"
      extends Modelica.Icons.Example;
      extends Modelica.Media.Examples.Utilities.PartialTestModel2(
          redeclare package Medium = Modelica.Media.Air.ReferenceAir.Air_pT);
    end DryAir2;

    model MoistAir "Example for moist air"
      extends Modelica.Icons.Example;
      parameter SI.Temperature T_start=274
        "Initial guess value for temperature";
      parameter SI.Pressure p_start=1e5
        "Initial guess value for pressure";
      package Medium = Modelica.Media.Air.ReferenceMoistAir "Medium model";
      Medium.BaseProperties medium(
        T(start=T_start, fixed=true),
        X(start={0.95,0.05}),
        p(start=p_start, fixed=true));
      parameter Medium.MolarMass[2] MMx={Medium.dryair.MM,Medium.steam.MM}
        "Vector of molar masses (consisting of dry air and of steam)";
      Medium.MolarMass MM=1/((1 - medium.X[1])/MMx[1] + medium.X[1]/MMx[2])
        "Molar mass of gas part of mixture";
      Medium.ThermodynamicState state1;
      Medium.ThermodynamicState state2;
      Medium.ThermodynamicState smoothState(T(start=T_start), p(start=p_start));
      Real m_flow_ext;
      Real der_p;
      Real der_T;
    protected
      constant SI.Time unitTime=1;
      parameter SI.AbsolutePressure p01 = 1.e5 "state1.p at time 0";
      parameter SI.PressureSlope pRate1 = 1.e5 "state1.p rate of change";
      parameter SI.Temperature T01 = 300 "state1.T at time 0";
      parameter SI.TemperatureSlope Trate1 = 10 "state1.T rate of change";
      parameter SI.AbsolutePressure p02 = 1.e5 "state2.p at time 0";
      parameter SI.PressureSlope pRate2 = 1.e5/2 "state2.p rate of change";
      parameter SI.Temperature T02 = 340 "state2.T at time 0";
      parameter SI.TemperatureSlope Trate2 = -20 "state2.T rate of change";
    equation
      der(medium.p) = 0.0;
      der(medium.T) = 90;
      medium.X[Medium.Air] = 0.95;
      m_flow_ext = time - 0.5;
      state1.p = p01 + pRate1*time;
      state1.T = T01 + Trate1*time;
      state1.X = {time,1 - time}/unitTime;
      state2.p = p02 + pRate2*time;
      state2.T = T02 + Trate2*time;
      state2.X = {0.5*time,1 - 0.5*time}/unitTime;
      smoothState = Medium.setSmoothState(
              m_flow_ext,
              state1,
              state2,
              0.2);
      der_p = der(smoothState.p);
      der_T = der(smoothState.T);
    end MoistAir;

    model MoistAir1 "Example 1 for moist air"
      extends Modelica.Icons.Example;
      extends Modelica.Media.Examples.Utilities.PartialTestModel(
          redeclare package Medium = Modelica.Media.Air.ReferenceMoistAir);
    end MoistAir1;

    model MoistAir2 "Example 2 for moist air"
      extends Modelica.Icons.Example;
      extends Modelica.Media.Examples.Utilities.PartialTestModel2(
          redeclare package Medium = Modelica.Media.Air.ReferenceMoistAir);
    end MoistAir2;

    model Inverse_sh_T
      "Solve h = h_pT(p, T), s = s_pT(p, T) for T, if h or s is given"
      extends Modelica.Icons.Example;

      import Medium = Modelica.Media.Air.ReferenceAir.Air_pT "Medium model";

      parameter SI.Temperature T_min=300
        "Vary temperature linearly from T_min (time=0) up to T_max (time=1)";
      parameter SI.Temperature T_max=500
        "Vary temperature linearly from T_min (time=0) up to T_max (time=1)";
      parameter SI.Pressure p=1.0e5 "Fixed pressure in model";
      final parameter SI.SpecificEnthalpy h_min=
          Medium.specificEnthalpy(Medium.setState_pT(p, T_min))
        "Specific enthalpy at T_min";
      final parameter SI.SpecificEnthalpy h_max=
          Medium.specificEnthalpy(Medium.setState_pT(p, T_max))
        "Specific enthalpy at T_max";
      final parameter SI.SpecificEntropy s_min=
          Medium.specificEntropy(Medium.setState_pT(p, T_min))
        "Specific entropy at T_min";
      final parameter SI.SpecificEntropy s_max=
          Medium.specificEntropy(Medium.setState_pT(p, T_max))
        "Specific entropy at T_max";
      SI.SpecificEnthalpy h1 "Pre-defined specific enthalpy";
      SI.SpecificEnthalpy h2
        "Specific enthalpy computed from T (= h1 required)";
      SI.SpecificEntropy s1 "Pre-defined specific entropy";
      SI.SpecificEntropy s2
        "Specific entropy computed from T (= h1 required)";
      SI.Temperature Th "Temperature computed from h1";
      SI.Temperature Ts "Temperature computed from s1";

    protected
      constant SI.Time timeUnit=1.0;

    equation
      // Define specific enthalpy and specific entropy
      h1 = if time < 0 then h_min else if time >= 1 then h_max else h_min + time
        /timeUnit*(h_max - h_min);
      s1 = if time < 0 then s_min else if time >= 1 then s_max else s_min + time
        /timeUnit*(s_max - s_min);

      // Solve for temperature
      Th = Medium.temperature_phX(
              p,
              h1,
              fill(0.0, 0));
      Ts = Medium.temperature_psX(
              p,
              s1,
              fill(0.0, 0));

      // Check (h2 must be identical to h1)
      h2 = Medium.specificEnthalpy_pTX(
              p,
              Th,
              fill(0.0, 0));
      s2 = Medium.specificEntropy_pTX(
              p,
              Ts,
              fill(0.0, 0));
      assert(abs(h1 - h2) < 1e-3, "Inverse for h not correctly computed");
      assert(abs(s1 - s2) < 1e-3, "Inverse for s not correctly computed");
    end Inverse_sh_T;

    model Inverse_sh_TX
      "Solve h = h_pTX(p, T, X), s = s_pTX(p, T, X) for T, if h or s is given"
      extends Modelica.Icons.Example;

      import Medium = Modelica.Media.Air.ReferenceMoistAir "Medium model";

      parameter SI.Temperature T_min=300
        "Vary temperature linearly from T_min (time=0) up to T_max (time=1)";
      parameter SI.Temperature T_max=500
        "Vary temperature linearly from T_min (time=0) up to T_max (time=1)";
      parameter SI.Pressure p=1.0e5 "Fixed pressure in model";
      parameter SI.MassFraction[:] X=Modelica.Media.Air.ReferenceMoistAir.reference_X
        "Mass fraction vector";
      final parameter SI.SpecificEnthalpy h_min=
          Modelica.Media.Air.ReferenceMoistAir.specificEnthalpy(
          Modelica.Media.Air.ReferenceMoistAir.setState_pTX(
                p,
                T_min,
                X)) "Specific enthalpy at T_min";
      final parameter SI.SpecificEnthalpy h_max=
          Modelica.Media.Air.ReferenceMoistAir.specificEnthalpy(
          Modelica.Media.Air.ReferenceMoistAir.setState_pTX(
                p,
                T_max,
                X)) "Specific enthalpy at T_max";
      final parameter SI.SpecificEntropy s_min=
          Modelica.Media.Air.ReferenceMoistAir.specificEntropy(
          Modelica.Media.Air.ReferenceMoistAir.setState_pTX(
                p,
                T_min,
                X)) "Specific entropy at T_min";
      final parameter SI.SpecificEntropy s_max=
          Modelica.Media.Air.ReferenceMoistAir.specificEntropy(
          Modelica.Media.Air.ReferenceMoistAir.setState_pTX(
                p,
                T_max,
                X)) "Specific entropy at T_max";
      SI.SpecificEnthalpy h1 "Pre-defined specific enthalpy";
      SI.SpecificEnthalpy h2
        "Specific enthalpy computed from T (= h1 required)";
      SI.SpecificEntropy s1 "Pre-defined specific entropy";
      SI.SpecificEntropy s2
        "Specific entropy computed from T (= h1 required)";
      SI.Temperature Th "Temperature computed from h1";
      SI.Temperature Ts "Temperature computed from s1";

    protected
      constant SI.Time timeUnit=1.0;

    equation
      // Define specific enthalpy
      h1 = if time < 0 then h_min else if time >= 1 then h_max else h_min + time
        /timeUnit*(h_max - h_min);
      s1 = if time < 0 then s_min else if time >= 1 then s_max else s_min + time
        /timeUnit*(s_max - s_min);

      // Solve for temperature
      Th = Medium.temperature_phX(
              p,
              h1,
              X);
      Ts = Medium.temperature_psX(
              p,
              s1,
              X);

      // Check (h2 must be identical to h1)
      h2 = Medium.specificEnthalpy_pTX(
              p,
              Th,
              X);
      s2 = Medium.specificEntropy_pTX(
              p,
              Ts,
              X);
      assert(abs(h1 - h2) < 1e-3, "Inverse for h not correctly computed");
      assert(abs(s1 - s2) < 1e-3, "Inverse for s not correctly computed");
    end Inverse_sh_TX;
  end ReferenceAir;

  package R134a "Examples for R134a"
    extends Modelica.Icons.ExamplesPackage;

    model R134a1 "Example 1 for R134a"
      extends Modelica.Icons.Example;
      extends Modelica.Media.Examples.Utilities.PartialTestModel(
        redeclare package Medium = Modelica.Media.R134a.R134a_ph,
        h_start=107390,
        fixedMassFlowRate(use_T_ambient=false),
        volume(use_T_start=false),
        ambient(use_T_ambient=false));
    end R134a1;

    model R134a2 "Example 2 for R134a"
      extends Modelica.Icons.Example;
      extends Modelica.Media.Examples.Utilities.PartialTestModel2(
        redeclare package Medium = Modelica.Media.R134a.R134a_ph,
        h_start=107390,
        fixedMassFlowRate(use_T_ambient=false),
        volume(use_T_start=false),
        ambient(use_T_ambient=false));
    end R134a2;
  end R134a;

  package SolveOneNonlinearEquation
    "Demonstrate how to solve one non-linear algebraic equation in one unknown"
    extends Modelica.Icons.ExamplesPackage;

    model Inverse_sine "Solve y = A*sin(w*x) for x, given y"
      import Modelica.Utilities.Streams.print;
      extends Modelica.Icons.Example;

      parameter Real y_zero=0.5 "Desired value of A*sin(w*x)";
      parameter Real x_min=-1.7 "Minimum value of x_zero";
      parameter Real x_max=1.7 "Maximum value of x_zero";
      parameter Real A=1 "Amplitude of sine";
      parameter Real w=1 "Angular frequency of sine";
      Real x_zero "y_zero = A*sin(w*x_zero)";

      function f_nonlinear "Define sine as non-linear equation to be solved"
        extends Modelica.Math.Nonlinear.Interfaces.partialScalarFunction;
        input Real A=1 "Amplitude of sine";
        input Real w=1 "Angular frequency of sine";
        input Real s=0 "Shift of sine";
      algorithm
        y := A*Modelica.Math.sin(w*u) + s;
      end f_nonlinear;

    equation
      x_zero = Modelica.Math.Nonlinear.solveOneNonlinearEquation(
        function f_nonlinear(A=A, w=w, s=-y_zero), x_min, x_max);

      print("x_zero = " + String(x_zero) + ", y_zero = " + String(y_zero) +
        ", A*sin(w*x_zero) = " + String(A*Modelica.Math.sin(w*x_zero))); -> determine x for given y

Translate model \"Inverse_sine\"
and simulate for 0 sec. The result is printed to the
output window.

"));
    end Inverse_sine;

    model Inverse_sh_T
      "Solve h = h_T(T), s = s_T(T) for T, if h or s is given for ideal gas NASA"
      extends Modelica.Icons.Example;

      replaceable package Medium = Modelica.Media.Air.DryAirNasa constrainedby Modelica.Media.IdealGases.Common.SingleGasNasa
        "Medium model"

      parameter SI.Temperature T_min=300
        "Vary temperature linearly from T_min (time=0) up to T_max (time=1)";
      parameter SI.Temperature T_max=500
        "Vary temperature linearly from T_min (time=0) up to T_max (time=1)";
      parameter SI.Pressure p=1.0e5 "Fixed pressure in model";
      final parameter SI.SpecificEnthalpy h_min=Medium.specificEnthalpy(
          Medium.setState_pT(p, T_min)) "Specific enthalpy at T_min";
      final parameter SI.SpecificEnthalpy h_max=Medium.specificEnthalpy(
          Medium.setState_pT(p, T_max)) "Specific enthalpy at T_max";
      final parameter SI.SpecificEntropy s_min=Medium.specificEntropy(
          Medium.setState_pT(p, T_min)) "Specific entropy at T_min";
      final parameter SI.SpecificEntropy s_max=Medium.specificEntropy(
          Medium.setState_pT(p, T_max)) "Specific entropy at T_max";
      SI.SpecificEnthalpy h1(start=h_min, fixed=true)
        "Pre-defined specific enthalpy";
      SI.SpecificEnthalpy h2
        "Specific enthalpy computed from Th (= h1 required)";
      SI.SpecificEntropy s1(start=s_min, fixed=true)
        "Pre-defined specific entropy";
      SI.SpecificEntropy s2 "Specific entropy computed from Ts (= s1 required)";
      SI.Temperature Th "Temperature computed from h1";
      SI.Temperature Ts "Temperature computed from s1";

    protected
      constant SI.Time timeUnit=1.0;

    equation
      // Define specific enthalpy and specific entropy
      der(h1) = if time < 1.0 then 1/timeUnit*(h_max - h_min) else 0.0;
      der(s1) = if time < 1.0 then 1/timeUnit*(s_max - s_min) else 0.0;

      // Solve for temperatures
      Th = Medium.temperature_phX(p, h1, fill(0.0, 0));
      Ts = Medium.temperature_psX(p, s1, fill(0.0, 0));

      // Check (h2 must be identical to h1, s2 must be identical to s1)
      h2 = Medium.specificEnthalpy_pTX(p, Th, fill(0.0, 0));
      s2 = Medium.specificEntropy(Medium.setState_pT(p, Ts));
    end Inverse_sh_T;

    model InverseIncompressible_sh_T
      "Inverse computation for incompressible media"
      extends Modelica.Icons.Example;

      replaceable package Medium =
          Modelica.Media.Incompressible.Examples.Glycol47 "Medium model"

      parameter SI.Temperature T_min=Medium.T_min
        "Vary temperature linearly from T_min (time=0) up to T_max (time=1)";
      parameter SI.Temperature T_max=Medium.T_max
        "Vary temperature linearly from T_min (time=0) up to T_max (time=1)";
      parameter SI.Pressure p=1.0e5 "Fixed pressure in model";
      final parameter SI.SpecificEnthalpy h_min=Medium.h_T(Medium.T_min)
        "Specific enthalpy at T_min";
      final parameter SI.SpecificEnthalpy h_max=Medium.h_T(Medium.T_max)
        "Specific enthalpy at T_max";
      final parameter SI.SpecificEntropy s_min=Medium.specificEntropy(
          Medium.setState_pT(p, T_min)) "Specific entropy at T_min";
      final parameter SI.SpecificEntropy s_max=Medium.specificEntropy(
          Medium.setState_pT(p, T_max)) "Specific entropy at T_max";

      SI.SpecificEnthalpy h1(start=h_min, fixed=true)
        "Pre-defined specific enthalpy";
      SI.SpecificEnthalpy h2
        "Specific enthalpy computed from Th (= h1 required)";
      SI.SpecificEntropy s1(start=s_min, fixed=true)
        "Pre-defined specific entropy";
      SI.SpecificEntropy s2 "Specific entropy computed from Ts (= s1 required)";
      SI.Temperature Th "Temperature computed from h1";
      SI.Temperature Ts "Temperature computed from s1";

    protected
      constant SI.Time timeUnit=1.0;

    equation
      // Define specific enthalpy and specific entropy
      der(h1) = if time < 1.0 then 1/timeUnit*(h_max - h_min) else 0.0;
      der(s1) = if time < 1.0 then 1/timeUnit*(s_max - s_min) else 0.0;

      // Solve for temperatures
      Th = Medium.temperature_phX(p, h1, fill(0.0, 0));
      Ts = Medium.temperature_psX(p, s1, fill(0.0, 0));

      // Check (h2 must be identical to h1, s2 must be identical to s1)
      h2 = Medium.specificEnthalpy_pTX(p, Th, fill(0.0, 0));
      s2 = Medium.specificEntropy(Medium.setState_pT(p, Ts));
    end InverseIncompressible_sh_T;

    model Inverse_sh_TX
      "Solve h = h_TX(TX) for T, if h is given for ideal gas NASA"
      extends Modelica.Icons.Example;

      replaceable package Medium =
          Modelica.Media.IdealGases.MixtureGases.FlueGasLambdaOnePlus
        constrainedby Modelica.Media.IdealGases.Common.MixtureGasNasa
        "Medium model"

      parameter SI.Temperature T_min=300
        "Vary temperature linearly from T_min (time=0) up to T_max (time=1)";
      parameter SI.Temperature T_max=500
        "Vary temperature linearly from T_min (time=0) up to T_max (time=1)";
      parameter SI.Pressure p=1.0e5 "Fixed pressure in model";
      final parameter SI.SpecificEnthalpy h_min=Medium.h_TX(T_min, X)
        "Specific enthalpy at T_min";
      final parameter SI.SpecificEnthalpy h_max=Medium.h_TX(T_max, X)
        "Specific enthalpy at T_max";
      final parameter SI.SpecificEntropy s_min=Medium.specificEntropy(
        Medium.setState_pTX(p, T_min, Medium.reference_X)) "Specific entropy at T_min";
      final parameter SI.SpecificEntropy s_max=Medium.specificEntropy(
        Medium.setState_pTX(p, T_max, Medium.reference_X)) "Specific entropy at T_max";
      SI.SpecificEnthalpy h1(start=h_min, fixed=true)
        "Pre-defined specific enthalpy";
      SI.SpecificEnthalpy h2
        "Specific enthalpy computed from Th (= h1 required)";
      SI.SpecificEntropy s1(start=s_min, fixed=true)
        "Pre-defined specific entropy";
      SI.SpecificEntropy s2 "Specific entropy computed from Ts (= s1 required)";
      SI.Temperature Th "Temperature computed from h1";
      SI.Temperature Ts "Temperature computed from s1";
      parameter SI.MassFraction[4] X=Medium.reference_X "Mass fraction vector";

    protected
      constant SI.Time timeUnit=1.0;

    equation
      // Define specific enthalpy and specific entropy
      der(h1) = if time < 1.0 then 1/timeUnit*(h_max - h_min) else 0.0;
      der(s1) = if time < 1.0 then 1/timeUnit*(s_max - s_min) else 0.0;

      // Solve for temperatures
      Th = Medium.temperature_phX(p, h1, X);
      Ts = Medium.temperature_psX(p, s1, X);

      // Check (h2 must be identical to h1, s2 must be identical to s1)
      h2 = Medium.specificEnthalpy_pTX(p, Th, X);
      s2 = Medium.specificEntropy(Medium.setState_pTX(p, Ts, X));
    end Inverse_sh_TX;
  end SolveOneNonlinearEquation;

    package Utilities
      "Functions, connectors and models needed for the media model tests"

      extends Modelica.Icons.UtilitiesPackage;

      connector FluidPort
        "Interface for quasi one-dimensional fluid flow in a piping network (incompressible or compressible, one or more phases, one or more substances)"
        replaceable package Medium = Modelica.Media.Interfaces.PartialMedium
          "Medium model"

        Medium.AbsolutePressure p "Pressure in the connection point";
        flow Medium.MassFlowRate m_flow
          "Mass flow rate from the connection point into the component";

        Medium.SpecificEnthalpy h
          "Specific mixture enthalpy in the connection point";
        flow Medium.EnthalpyFlowRate H_flow
          "Enthalpy flow rate into the component (if m_flow > 0, H_flow = m_flow*h)";

        Medium.MassFraction Xi[Medium.nXi]
          "Independent mixture mass fractions m_i/m in the connection point";
        flow Medium.MassFlowRate mXi_flow[Medium.nXi]
          "Mass flow rates of the independent substances from the connection point into the component (if m_flow > 0, mX_flow = m_flow*X)";

        Medium.ExtraProperty C[Medium.nC]
          "Properties c_i/m in the connection point";
        flow Medium.ExtraPropertyFlowRate mC_flow[Medium.nC]
          "Flow rates of auxiliary properties from the connection point into the component (if m_flow > 0, mC_flow = m_flow*C)";
      end FluidPort;

      connector FluidPort_a "Fluid connector with filled icon"
        extends FluidPort;
      end FluidPort_a;

      connector FluidPort_b "Fluid connector with outlined icon"
        extends FluidPort;
      end FluidPort_b;

      model PortVolume
        "Fixed volume associated with a port by the finite volume method"
        replaceable package Medium = Modelica.Media.Interfaces.PartialMedium
          "Medium model"

        parameter SI.Volume V=1e-6 "Fixed size of junction volume";

        parameter Boolean use_p_start=true "Select p_start or d_start"
        parameter Medium.AbsolutePressure p_start=101325 "Initial pressure"
        parameter Medium.Density d_start=1 "Initial density"
        parameter Boolean use_T_start=true "Select T_start or h_start"
        parameter Medium.Temperature T_start=
            Modelica.Units.Conversions.from_degC(20) "Initial temperature"
        parameter Medium.SpecificEnthalpy h_start=1.e4
          "Initial specific enthalpy"
        parameter Medium.MassFraction X_start[Medium.nX]
          "Initial mass fractions m_i/m"

        FluidPort_a port(redeclare package Medium = Medium)
        Medium.BaseProperties medium(preferredMediumStates=true);
        SI.Energy U "Internal energy of port volume";
        SI.Mass m "Mass of junction volume";
        SI.Mass mXi[Medium.nXi]
          "Independent substance masses of junction volume";

      initial equation
        if not Medium.singleState then
          if use_p_start then
            medium.p = p_start;
          else
            medium.d = d_start;
          end if;
        end if;

        if use_T_start then
          medium.T = T_start;
        else
          medium.h = h_start;
        end if;

        medium.Xi = X_start[1:Medium.nXi];
      equation
        // Connect port to medium variables
        medium.p = port.p;
        medium.h = port.h;
        medium.Xi = port.Xi;

        // Total quantities
        m = V*medium.d;
        mXi = m*medium.Xi;
        U = m*medium.u;

        // Mass and energy balance
        der(m) = port.m_flow;
        der(mXi) = port.mXi_flow;
        der(U) = port.H_flow;
      end PortVolume;

      model FixedMassFlowRate
        "Ideal pump that produces a constant mass flow rate from a large reservoir at fixed temperature and mass fraction"
        parameter Medium.MassFlowRate m_flow
          "Fixed mass flow rate from an infinite reservoir to the fluid port";

        parameter Boolean use_T_ambient=true "Select T_ambient or h_ambient"
        parameter Medium.Temperature T_ambient=
            Modelica.Units.Conversions.from_degC(20) "Ambient temperature"
        parameter Medium.SpecificEnthalpy h_ambient=1.e4
          "Ambient specific enthalpy"
        parameter Medium.MassFraction X_ambient[Medium.nX]
          "Ambient mass fractions m_i/m of reservoir";

        replaceable package Medium = Modelica.Media.Interfaces.PartialMedium
          "Medium model"

        Medium.BaseProperties medium "Medium in the source";
        FluidPort_b port(redeclare package Medium = Medium)
      equation
        if use_T_ambient then
          medium.T = T_ambient;
        else
          medium.h = h_ambient;
        end if;

        medium.Xi = X_ambient[1:Medium.nXi];
        medium.p = port.p;
        port.m_flow = -m_flow;
        port.mXi_flow = semiLinear(
                  port.m_flow,
                  port.Xi,
                  medium.Xi);
        port.H_flow = semiLinear(
                  port.m_flow,
                  port.h,
                  medium.h);
      end FixedMassFlowRate;

      model FixedAmbient
        "Ambient pressure, temperature and mass fraction source"
        replaceable package Medium = Modelica.Media.Interfaces.PartialMedium
          "Medium model"

        parameter Boolean use_p_ambient=true "Select p_ambient or d_ambient"
        parameter Medium.AbsolutePressure p_ambient=101325 "Ambient pressure"
        parameter Medium.Density d_ambient=1 "Ambient density"
        parameter Boolean use_T_ambient=true "Select T_ambient or h_ambient"
        parameter Medium.Temperature T_ambient=
            Modelica.Units.Conversions.from_degC(20) "Ambient temperature"
        parameter Medium.SpecificEnthalpy h_ambient=1.e4
          "Ambient specific enthalpy"
        parameter Medium.MassFraction X_ambient[Medium.nX]
          "Ambient mass fractions m_i/m"

        Medium.BaseProperties medium "Medium in the source";
        FluidPort_b port(redeclare package Medium = Medium)

      equation
        if use_p_ambient or Medium.singleState then
          medium.p = p_ambient;
        else
          medium.d = d_ambient;
        end if;

        if use_T_ambient then
          medium.T = T_ambient;
        else
          medium.h = h_ambient;
        end if;

        medium.Xi = X_ambient[1:Medium.nXi];

        port.p = medium.p;
        port.H_flow = semiLinear(
                  port.m_flow,
                  port.h,
                  medium.h);
        port.mXi_flow = semiLinear(
                  port.m_flow,
                  port.Xi,
                  medium.Xi);
      end FixedAmbient;

      model ShortPipe "Simple pressure loss in pipe"
        replaceable package Medium = Modelica.Media.Interfaces.PartialMedium
          "Medium model"

        parameter Medium.AbsolutePressure dp_nominal(min=1e-10)
          "Nominal pressure drop";
        parameter Medium.MassFlowRate m_flow_nominal(min=1e-10)
          "Nominal mass flow rate at nominal pressure drop";

        FluidPort_a port_a(redeclare package Medium = Medium)
        FluidPort_b port_b(redeclare package Medium = Medium)
        // Medium.BaseProperties medium_a(p=port_a.p, h=port_a.h, Xi=port_a.Xi)
        //   "Medium properties in port_a";
        // Medium.BaseProperties medium_b(p=port_b.p, h=port_b.h, Xi=port_b.Xi)
        //   "Medium properties in port_b";
        Medium.MassFlowRate m_flow
          "Mass flow rate from port_a to port_b (m_flow > 0 is design flow direction)";
        SI.Pressure dp "Pressure drop from port_a to port_b";
      equation
        /* Handle reverse and zero flow */
        port_a.H_flow = semiLinear(
                  port_a.m_flow,
                  port_a.h,
                  port_b.h);
        port_a.mXi_flow = semiLinear(
                  port_a.m_flow,
                  port_a.Xi,
                  port_b.Xi);

        /* Energy, mass and substance mass balance */
        port_a.H_flow + port_b.H_flow = 0;
        port_a.m_flow + port_b.m_flow = 0;
        port_a.mXi_flow + port_b.mXi_flow = zeros(Medium.nXi);

        // Design direction of mass flow rate
        m_flow = port_a.m_flow;

        // Pressure drop
        dp = port_a.p - port_b.p;
        m_flow = (m_flow_nominal/dp_nominal)*dp;
      end ShortPipe;

      partial model PartialTestModel "Basic test model to test a medium"

        replaceable package Medium = Modelica.Media.Interfaces.PartialMedium
          "Medium model"
        parameter SI.AbsolutePressure p_start=Medium.p_default
          "Initial value of pressure";
        parameter SI.Temperature T_start=Medium.T_default
          "Initial value of temperature";
        parameter SI.SpecificEnthalpy h_start=Medium.h_default
          "Initial value of specific enthalpy";
        parameter Real X_start[Medium.nX]=Medium.X_default
          "Initial value of mass fractions";

        /*
  parameter SI.AbsolutePressure p_start = 1.0e5 "Initial value of pressure";
  parameter SI.Temperature T_start = 300 "Initial value of temperature";
  parameter SI.Density h_start = 1 "Initial value of specific enthalpy";
  parameter Real X_start[Medium.nX] = Medium.reference_X
    "Initial value of mass fractions";
*/
        PortVolume volume(
          redeclare package Medium = Medium,
          p_start=p_start,
          T_start=T_start,
          h_start=h_start,
          X_start=X_start,
          V=0.1)
        FixedMassFlowRate fixedMassFlowRate(
          redeclare package Medium = Medium,
          T_ambient=1.2*T_start,
          h_ambient=1.2*h_start,
          m_flow=1,
          X_ambient=0.5*X_start)
        FixedAmbient ambient(
          redeclare package Medium = Medium,
          T_ambient=T_start,
          h_ambient=h_start,
          X_ambient=X_start,
          p_ambient=p_start)
        ShortPipe shortPipe(
          redeclare package Medium = Medium,
          m_flow_nominal=1,
          dp_nominal=0.1e5)
      equation
        connect(fixedMassFlowRate.port, volume.port)
        connect(volume.port, shortPipe.port_a)
        connect(shortPipe.port_b, ambient.port)
      end PartialTestModel;

      partial model PartialTestModel2
        "Slightly larger test model to test a medium"
        replaceable package Medium = Modelica.Media.Interfaces.PartialMedium
          "Medium model"
        parameter SI.AbsolutePressure p_start=1.0e5 "Initial value of pressure";
        parameter SI.Temperature T_start=300 "Initial value of temperature";
        parameter SI.SpecificEnthalpy h_start=1
          "Initial value of specific enthalpy";
        parameter Real X_start[Medium.nX]=Medium.reference_X
          "Initial value of mass fractions";
        PortVolume volume(
          redeclare package Medium = Medium,
          p_start=p_start,
          T_start=T_start,
          h_start=h_start,
          X_start=X_start,
          V=0.1)
        FixedMassFlowRate fixedMassFlowRate(
          redeclare package Medium = Medium,
          T_ambient=1.2*T_start,
          h_ambient=1.2*h_start,
          m_flow=1,
          X_ambient=0.5*X_start)
        FixedAmbient ambient(
          redeclare package Medium = Medium,
          T_ambient=T_start,
          h_ambient=h_start,
          X_ambient=X_start,
          p_ambient=p_start)
        ShortPipe shortPipe(
          redeclare package Medium = Medium,
          m_flow_nominal=1,
          dp_nominal=0.1e5)
        PortVolume volume1(
          redeclare package Medium = Medium,
          p_start=p_start,
          T_start=T_start,
          h_start=h_start,
          X_start=X_start,
          V=0.1)
        ShortPipe shortPipe1(
          redeclare package Medium = Medium,
          m_flow_nominal=1,
          dp_nominal=0.1e5)
      equation
        connect(fixedMassFlowRate.port, volume.port)
        connect(volume.port, shortPipe.port_a)
        connect(volume1.port, shortPipe1.port_a)
        connect(shortPipe.port_b, volume1.port)
        connect(shortPipe1.port_b, ambient.port)
      end PartialTestModel2;
    end Utilities;
end Examples;

package Interfaces "Interfaces for media models"
  extends Modelica.Icons.InterfacesPackage;

  partial package TemplateMedium "Template for media models"
    /* For a new medium, make a copy of this package and remove
     the "partial" keyword from the package definition above.
     The statement below extends from PartialMedium and sets some
     package constants. Provide values for these constants
     that are appropriate for your medium model. Note that other
     constants (such as nX, nXi) are automatically defined by
     definitions given in the base class Interfaces.PartialMedium"
  */
    extends Modelica.Media.Interfaces.PartialMedium(
      final mediumName="NameOfMedium",
      final substanceNames={mediumName},
      final singleState=false,
      final reducedX=true,
      final fixedX=true,
      Temperature(
        min=273,
        max=450,
        start=323));

    // Provide medium constants here
    constant SpecificHeatCapacity cp_const=123456
      "Constant specific heat capacity at constant pressure";

    /* The vector substanceNames is mandatory, as the number of
         substances is determined based on its size. Here we assume
         a single-component medium.
         singleState is true, if u and d do not depend on pressure, but only
         on a thermal variable (temperature or enthalpy). Otherwise, set it
         to false.
         For a single-substance medium, just set reducedX and fixedX to true, and there's
         no need to bother about medium compositions at all. Otherwise, set
         final reducedX = true, if the medium model has nS-1 independent mass
         fraction, or reducedX = false, if the medium model has nS independent
         mass fractions (nS = number of substances).
         If a mixture has a fixed composition set fixedX=true, otherwise false.
         The modifiers for reducedX and fixedX should normally be final
         since the other equations are based on these values.

         It is also possible to redeclare the min, max, and start attributes of
         Medium types, defined in the base class Interfaces.PartialMedium
         (the example of Temperature is shown here). Min and max attributes
         should be set in accordance to the limits of validity of the medium
         model, while the start attribute should be a reasonable default value
         for the initialization of nonlinear solver iterations */

    /* Provide an implementation of model BaseProperties,
     that is defined in PartialMedium. Select two independent
     variables from p, T, d, u, h. The other independent
     variables are the mass fractions "Xi", if there is more
     than one substance. Provide 3 equations to obtain the remaining
     variables as functions of the independent variables.
     It is also necessary to provide two additional equations to set
     the gas constant R_s and the molar mass MM of the medium.
     Finally, the thermodynamic state vector, defined in the base class
     Interfaces.PartialMedium.BaseProperties, should be set, according to
     its definition (see ThermodynamicState below).
     The computation of vector X[nX] from Xi[nXi] is already included in
     the base class Interfaces.PartialMedium.BaseProperties, so it should not
     be repeated here.
     The code fragment below is for a single-substance medium with
     p,T as independent variables.
  */

    redeclare model extends BaseProperties(final standardOrderComponents=true)
      "Base properties of medium"

    equation
      d = 1;
      h = cp_const*T;
      u = h - p/d;
      MM = 0.024;
      R_s = Modelica.Constants.R/MM;
      state.p = p;
      state.T = T;
    end BaseProperties;

    /* Provide implementations of the following optional properties.
     If not available, delete the corresponding function.
     The record "ThermodynamicState" contains the input arguments
     of all the function and is defined together with the used
     type definitions in PartialMedium. The record most often contains two of the
     variables "p, T, d, h" (e.g., medium.T)
  */
    redeclare replaceable record ThermodynamicState
      "A selection of variables that uniquely defines the thermodynamic state"
      extends Modelica.Icons.Record;
      AbsolutePressure p "Absolute pressure of medium";
      Temperature T "Temperature of medium";
    end ThermodynamicState;

    redeclare function extends dynamicViscosity "Return dynamic viscosity"
    algorithm
      eta := 10 - state.T*0.3 + state.p*0.2;
    end dynamicViscosity;

    redeclare function extends thermalConductivity
      "Return thermal conductivity"
    algorithm
      lambda := 0;
    end thermalConductivity;

    redeclare function extends specificEntropy "Return specific entropy"
    algorithm
      s := 0;
    end specificEntropy;

    redeclare function extends specificHeatCapacityCp
      "Return specific heat capacity at constant pressure"
    algorithm
      cp := 0;
    end specificHeatCapacityCp;

    redeclare function extends specificHeatCapacityCv
      "Return specific heat capacity at constant volume"
    algorithm
      cv := 0;
    end specificHeatCapacityCv;

    redeclare function extends isentropicExponent "Return isentropic exponent"
      extends Modelica.Icons.Function;
    algorithm
      gamma := 1;
    end isentropicExponent;

    redeclare function extends velocityOfSound "Return velocity of sound"
      extends Modelica.Icons.Function;
    algorithm
      a := 0;
    end velocityOfSound;
  end TemplateMedium;

  partial package PartialMedium
    "Partial medium properties (base package of all media packages)"
    extends Modelica.Media.Interfaces.Types;
    extends Modelica.Icons.MaterialPropertiesPackage;

    // Constants to be set in Medium
    constant Modelica.Media.Interfaces.Choices.IndependentVariables
      ThermoStates "Enumeration type for independent variables";
    constant String mediumName="unusablePartialMedium" "Name of the medium";
    constant String substanceNames[:]={mediumName}
      "Names of the mixture substances. Set substanceNames={mediumName} if only one substance.";
    constant String extraPropertiesNames[:]=fill("", 0)
      "Names of the additional (extra) transported properties. Set extraPropertiesNames=fill(\"\",0) if unused";
    constant Boolean singleState
      "= true, if u and d are not a function of pressure";
    constant Boolean reducedX=true
      "= true, if medium contains the equation sum(X) = 1.0; set reducedX=true, if only one substance (see docu for details)";
    constant Boolean fixedX=false
      "= true, if medium contains the equation X = reference_X";
    constant AbsolutePressure reference_p=101325
      "Reference pressure of Medium: default 1 atmosphere";
    constant Temperature reference_T=298.15
      "Reference temperature of Medium: default 25 deg Celsius";
    constant MassFraction reference_X[nX]=fill(1/nX, nX)
      "Default mass fractions of medium";
    constant AbsolutePressure p_default=101325
      "Default value for pressure of medium (for initialization)";
    constant Temperature T_default=Modelica.Units.Conversions.from_degC(20)
      "Default value for temperature of medium (for initialization)";
    constant SpecificEnthalpy h_default=specificEnthalpy_pTX(
            p_default,
            T_default,
            X_default)
      "Default value for specific enthalpy of medium (for initialization)";
    constant MassFraction X_default[nX]=reference_X
      "Default value for mass fractions of medium (for initialization)";
    constant ExtraProperty C_default[nC]=fill(0, nC)
      "Default value for trace substances of medium (for initialization)";

    final constant Integer nS=size(substanceNames, 1) "Number of substances";
    constant Integer nX=nS "Number of mass fractions";
    constant Integer nXi=if fixedX then 0 else if reducedX then nS - 1 else nS
      "Number of structurally independent mass fractions (see docu for details)";

    final constant Integer nC=size(extraPropertiesNames, 1)
      "Number of extra (outside of standard mass-balance) transported properties";
    constant Real C_nominal[nC](min=fill(Modelica.Constants.eps, nC)) = 1.0e-6*
      ones(nC) "Default for the nominal values for the extra properties";
    replaceable record FluidConstants =
        Modelica.Media.Interfaces.Types.Basic.FluidConstants
      "Critical, triple, molecular and other standard data of fluid";

    replaceable record ThermodynamicState
      "Minimal variable set that is available as input argument to every medium function"
      extends Modelica.Icons.Record;
    end ThermodynamicState;

    replaceable partial model BaseProperties
      "Base properties (p, d, T, h, u, R_s, MM and, if applicable, X and Xi) of a medium"
      InputAbsolutePressure p "Absolute pressure of medium";
      InputMassFraction[nXi] Xi(start=reference_X[1:nXi])
        "Structurally independent mass fractions";
      InputSpecificEnthalpy h "Specific enthalpy of medium";
      Density d "Density of medium";
      Temperature T "Temperature of medium";
      MassFraction[nX] X(start=reference_X)
        "Mass fractions (= (component mass)/total mass  m_i/m)";
      SpecificInternalEnergy u "Specific internal energy of medium";
      SpecificHeatCapacity R_s "Gas constant (of mixture if applicable)";
      MolarMass MM "Molar mass (of mixture or single fluid)";
      ThermodynamicState state
        "Thermodynamic state record for optional functions";
      parameter Boolean preferredMediumStates=false
        "= true, if StateSelect.prefer shall be used for the independent property variables of the medium"
      parameter Boolean standardOrderComponents=true
        "If true, and reducedX = true, the last element of X will be computed from the other ones";
      Modelica.Units.NonSI.Temperature_degC T_degC=
          Modelica.Units.Conversions.to_degC(T)
        "Temperature of medium in [degC]";
      Modelica.Units.NonSI.Pressure_bar p_bar=
          Modelica.Units.Conversions.to_bar(p)
        "Absolute pressure of medium in [bar]";

      // Local connector definition, used for equation balancing check
      connector InputAbsolutePressure = input SI.AbsolutePressure
        "Pressure as input signal connector";
      connector InputSpecificEnthalpy = input SI.SpecificEnthalpy
        "Specific enthalpy as input signal connector";
      connector InputMassFraction = input SI.MassFraction
        "Mass fraction as input signal connector";

    equation
      if standardOrderComponents then
        Xi = X[1:nXi];

        if fixedX then
          X = reference_X;
        end if;
        if reducedX and not fixedX then
          X[nX] = 1 - sum(Xi);
        end if;
        for i in 1:nX loop
          assert(X[i] >= -1.e-5 and X[i] <= 1 + 1.e-5, "Mass fraction X[" +
            String(i) + "] = " + String(X[i]) + "of substance " +
            substanceNames[i] + "\nof medium " + mediumName +
            " is not in the range 0..1");
        end for;

      end if;

      assert(p >= 0.0, "Pressure (= " + String(p) + " Pa) of medium \"" +
        mediumName + "\" is negative\n(Temperature = " + String(T) + " K)");
    end BaseProperties;

    replaceable partial function setState_pTX
      "Return thermodynamic state as function of p, T and composition 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";
    end setState_pTX;

    replaceable partial function setState_phX
      "Return thermodynamic state as function of p, h and composition 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";
    end setState_phX;

    replaceable partial function setState_psX
      "Return thermodynamic state as function of p, s and composition 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";
    end setState_psX;

    replaceable partial function setState_dTX
      "Return thermodynamic state as function of d, T and composition 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";
    end setState_dTX;

    replaceable partial function setSmoothState
      "Return thermodynamic state so that it smoothly approximates: if x > 0 then state_a else state_b"
      extends Modelica.Icons.Function;
      input Real x "m_flow or dp";
      input ThermodynamicState state_a "Thermodynamic state if x > 0";
      input ThermodynamicState state_b "Thermodynamic state if x < 0";
      input Real x_small(min=0)
        "Smooth transition in the region -x_small < x < x_small";
      output ThermodynamicState state
        "Smooth thermodynamic state for all x (continuous and differentiable)";

This is performed by applying function Media.Common.smoothStep(..)
on every element of the thermodynamic state record.

If mass fractions X[:] are approximated with this function then this can be performed
for all nX mass fractions, instead of applying it for nX-1 mass fractions and computing
the last one by the mass fraction constraint sum(X)=1. The reason is that the approximating function has the
property that sum(state.X) = 1, provided sum(state_a.X) = sum(state_b.X) = 1.
This can be shown by evaluating the approximating function in the abs(x) < x_small
region (otherwise state.X is either state_a.X or state_b.X):

X[1]  = smoothStep(x, X_a[1] , X_b[1] , x_small);
X[2]  = smoothStep(x, X_a[2] , X_b[2] , x_small);
   ...
X[nX] = smoothStep(x, X_a[nX], X_b[nX], x_small);

or

X[1]  = c*(X_a[1]  - X_b[1])  + (X_a[1]  + X_b[1])/2
X[2]  = c*(X_a[2]  - X_b[2])  + (X_a[2]  + X_b[2])/2;
   ...
X[nX] = c*(X_a[nX] - X_b[nX]) + (X_a[nX] + X_b[nX])/2;
c     = (x/x_small)*((x/x_small)^2 - 3)/4

Summing all mass fractions together results in

sum(X) = c*(sum(X_a) - sum(X_b)) + (sum(X_a) + sum(X_b))/2
       = c*(1 - 1) + (1 + 1)/2
       = 1

"));
    end setSmoothState;

    replaceable partial function dynamicViscosity "Return dynamic viscosity"
      extends Modelica.Icons.Function;
      input ThermodynamicState state "Thermodynamic state record";
      output DynamicViscosity eta "Dynamic viscosity";
    end dynamicViscosity;

    replaceable partial function thermalConductivity
      "Return thermal conductivity"
      extends Modelica.Icons.Function;
      input ThermodynamicState state "Thermodynamic state record";
      output ThermalConductivity lambda "Thermal conductivity";
    end thermalConductivity;

    replaceable function prandtlNumber "Return the Prandtl number"
      extends Modelica.Icons.Function;
      input ThermodynamicState state "Thermodynamic state record";
      output PrandtlNumber Pr "Prandtl number";
    algorithm
      Pr := dynamicViscosity(state)*specificHeatCapacityCp(state)/
        thermalConductivity(state);
    end prandtlNumber;

    replaceable partial function pressure "Return pressure"
      extends Modelica.Icons.Function;
      input ThermodynamicState state "Thermodynamic state record";
      output AbsolutePressure p "Pressure";
    end pressure;

    replaceable partial function temperature "Return temperature"
      extends Modelica.Icons.Function;
      input ThermodynamicState state "Thermodynamic state record";
      output Temperature T "Temperature";
    end temperature;

    replaceable partial function density "Return density"
      extends Modelica.Icons.Function;
      input ThermodynamicState state "Thermodynamic state record";
      output Density d "Density";
    end density;

    replaceable partial function specificEnthalpy "Return specific enthalpy"
      extends Modelica.Icons.Function;
      input ThermodynamicState state "Thermodynamic state record";
      output SpecificEnthalpy h "Specific enthalpy";
    end specificEnthalpy;

    replaceable partial function specificInternalEnergy
      "Return specific internal energy"
      extends Modelica.Icons.Function;
      input ThermodynamicState state "Thermodynamic state record";
      output SpecificEnergy u "Specific internal energy";
    end specificInternalEnergy;

    replaceable partial function specificEntropy "Return specific entropy"
      extends Modelica.Icons.Function;
      input ThermodynamicState state "Thermodynamic state record";
      output SpecificEntropy s "Specific entropy";
    end specificEntropy;

    replaceable partial function specificGibbsEnergy
      "Return specific Gibbs energy"
      extends Modelica.Icons.Function;
      input ThermodynamicState state "Thermodynamic state record";
      output SpecificEnergy g "Specific Gibbs energy";
    end specificGibbsEnergy;

    replaceable partial function specificHelmholtzEnergy
      "Return specific Helmholtz energy"
      extends Modelica.Icons.Function;
      input ThermodynamicState state "Thermodynamic state record";
      output SpecificEnergy f "Specific Helmholtz energy";
    end specificHelmholtzEnergy;

    replaceable partial function specificHeatCapacityCp
      "Return specific heat capacity at constant pressure"
      extends Modelica.Icons.Function;
      input ThermodynamicState state "Thermodynamic state record";
      output SpecificHeatCapacity cp
        "Specific heat capacity at constant pressure";
    end specificHeatCapacityCp;

    function heatCapacity_cp = specificHeatCapacityCp
      "Alias for deprecated name";

    replaceable partial function specificHeatCapacityCv
      "Return specific heat capacity at constant volume"
      extends Modelica.Icons.Function;
      input ThermodynamicState state "Thermodynamic state record";
      output SpecificHeatCapacity cv
        "Specific heat capacity at constant volume";
    end specificHeatCapacityCv;

    function heatCapacity_cv = specificHeatCapacityCv
      "Alias for deprecated name";

    replaceable partial function isentropicExponent
      "Return isentropic exponent"
      extends Modelica.Icons.Function;
      input ThermodynamicState state "Thermodynamic state record";
      output IsentropicExponent gamma "Isentropic exponent";
    end isentropicExponent;

    replaceable partial function isentropicEnthalpy
      "Return isentropic enthalpy"
      extends Modelica.Icons.Function;
      input AbsolutePressure p_downstream "Downstream pressure";
      input ThermodynamicState refState "Reference state for entropy";
      output SpecificEnthalpy h_is "Isentropic enthalpy";
    end isentropicEnthalpy;

    replaceable partial function velocityOfSound "Return velocity of sound"
      extends Modelica.Icons.Function;
      input ThermodynamicState state "Thermodynamic state record";
      output VelocityOfSound a "Velocity of sound";
    end velocityOfSound;

    replaceable partial function isobaricExpansionCoefficient
      "Return overall the isobaric expansion coefficient beta"
      extends Modelica.Icons.Function;
      input ThermodynamicState state "Thermodynamic state record";
      output IsobaricExpansionCoefficient beta "Isobaric expansion coefficient";
    end isobaricExpansionCoefficient;

    function beta = isobaricExpansionCoefficient
      "Alias for isobaricExpansionCoefficient for user convenience";

    replaceable partial function isothermalCompressibility
      "Return overall the isothermal compressibility factor"
      extends Modelica.Icons.Function;
      input ThermodynamicState state "Thermodynamic state record";
      output SI.IsothermalCompressibility kappa "Isothermal compressibility";
    end isothermalCompressibility;

    function kappa = isothermalCompressibility
      "Alias of isothermalCompressibility for user convenience";

    // explicit derivative functions for finite element models
    replaceable partial function density_derp_h
      "Return density derivative w.r.t. pressure at constant specific enthalpy"
      extends Modelica.Icons.Function;
      input ThermodynamicState state "Thermodynamic state record";
      output DerDensityByPressure ddph "Density derivative w.r.t. pressure";
    end density_derp_h;

    replaceable partial function density_derh_p
      "Return density derivative w.r.t. specific enthalpy at constant pressure"
      extends Modelica.Icons.Function;
      input ThermodynamicState state "Thermodynamic state record";
      output DerDensityByEnthalpy ddhp
        "Density derivative w.r.t. specific enthalpy";
    end density_derh_p;

    replaceable partial function density_derp_T
      "Return density derivative w.r.t. pressure at constant temperature"
      extends Modelica.Icons.Function;
      input ThermodynamicState state "Thermodynamic state record";
      output DerDensityByPressure ddpT "Density derivative w.r.t. pressure";
    end density_derp_T;

    replaceable partial function density_derT_p
      "Return density derivative w.r.t. temperature at constant pressure"
      extends Modelica.Icons.Function;
      input ThermodynamicState state "Thermodynamic state record";
      output DerDensityByTemperature ddTp
        "Density derivative w.r.t. temperature";
    end density_derT_p;

    replaceable partial function density_derX
      "Return density derivative w.r.t. mass fraction"
      extends Modelica.Icons.Function;
      input ThermodynamicState state "Thermodynamic state record";
      output Density[nX] dddX "Derivative of density w.r.t. mass fraction";
    end density_derX;

    replaceable partial function molarMass
      "Return the molar mass of the medium"
      extends Modelica.Icons.Function;
      input ThermodynamicState state "Thermodynamic state record";
      output MolarMass MM "Mixture molar mass";
    end molarMass;

    replaceable 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[:]=reference_X "Mass fractions";
      output SpecificEnthalpy h "Specific enthalpy";
    algorithm
      h := specificEnthalpy(setState_pTX(
              p,
              T,
              X));
    end specificEnthalpy_pTX;

    replaceable function specificEntropy_pTX
      "Return specific entropy 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 SpecificEntropy s "Specific entropy";
    algorithm
      s := specificEntropy(setState_pTX(
              p,
              T,
              X));
    end specificEntropy_pTX;

    replaceable function density_pTX "Return density from p, T, and X or Xi"
      extends Modelica.Icons.Function;
      input AbsolutePressure p "Pressure";
      input Temperature T "Temperature";
      input MassFraction X[:] "Mass fractions";
      output Density d "Density";
    algorithm
      d := density(setState_pTX(
              p,
              T,
              X));
    end density_pTX;

    replaceable 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[:]=reference_X "Mass fractions";
      output Temperature T "Temperature";
    algorithm
      T := temperature(setState_phX(
              p,
              h,
              X));
    end temperature_phX;

    replaceable 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[:]=reference_X "Mass fractions";
      output Density d "Density";
    algorithm
      d := density(setState_phX(
              p,
              h,
              X));
    end density_phX;

    replaceable function temperature_psX
      "Return temperature 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 Temperature T "Temperature";
    algorithm
      T := temperature(setState_psX(
              p,
              s,
              X));
    end temperature_psX;

    replaceable function density_psX "Return density 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 Density d "Density";
    algorithm
      d := density(setState_psX(
              p,
              s,
              X));
    end density_psX;

    replaceable function specificEnthalpy_psX
      "Return specific enthalpy 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 SpecificEnthalpy h "Specific enthalpy";
    algorithm
      h := specificEnthalpy(setState_psX(
              p,
              s,
              X));
    end specificEnthalpy_psX;

    replaceable partial function massFraction "Return independent mass fractions (if any)"
      extends Modelica.Icons.Function;
      input ThermodynamicState state "Thermodynamic state record";
      output MassFraction Xi[nXi] "Independent mass fractions";
    end massFraction;

    type MassFlowRate = SI.MassFlowRate (
        quantity="MassFlowRate." + mediumName,
        min=-1.0e5,
        max=1.e5) "Type for mass flow rate with medium specific attributes";
  end PartialMedium;

  partial package PartialPureSubstance
    "Base class for pure substances of one chemical substance"
    extends PartialMedium(final reducedX=true, final fixedX=true);

    replaceable function setState_pT "Return thermodynamic state from p and T"
      extends Modelica.Icons.Function;
      input AbsolutePressure p "Pressure";
      input Temperature T "Temperature";
      output ThermodynamicState state "Thermodynamic state record";
    algorithm
      state := setState_pTX(
              p,
              T,
              fill(0.0, 0));
    end setState_pT;

    replaceable function setState_ph "Return thermodynamic state from p and h"
      extends Modelica.Icons.Function;
      input AbsolutePressure p "Pressure";
      input SpecificEnthalpy h "Specific enthalpy";
      output ThermodynamicState state "Thermodynamic state record";
    algorithm
      state := setState_phX(
              p,
              h,
              fill(0.0, 0));
    end setState_ph;

    replaceable function setState_ps "Return thermodynamic state from p and s"
      extends Modelica.Icons.Function;
      input AbsolutePressure p "Pressure";
      input SpecificEntropy s "Specific entropy";
      output ThermodynamicState state "Thermodynamic state record";
    algorithm
      state := setState_psX(
              p,
              s,
              fill(0.0, 0));
    end setState_ps;

    replaceable function setState_dT "Return thermodynamic state from d and T"
      extends Modelica.Icons.Function;
      input Density d "Density";
      input Temperature T "Temperature";
      output ThermodynamicState state "Thermodynamic state record";
    algorithm
      state := setState_dTX(
              d,
              T,
              fill(0.0, 0));
    end setState_dT;

    replaceable function density_ph "Return density from p and h"
      extends Modelica.Icons.Function;
      input AbsolutePressure p "Pressure";
      input SpecificEnthalpy h "Specific enthalpy";
      output Density d "Density";
    algorithm
      d := density_phX(
              p,
              h,
              fill(0.0, 0));
    end density_ph;

    replaceable function temperature_ph "Return temperature from p and h"
      extends Modelica.Icons.Function;
      input AbsolutePressure p "Pressure";
      input SpecificEnthalpy h "Specific enthalpy";
      output Temperature T "Temperature";
    algorithm
      T := temperature_phX(
              p,
              h,
              fill(0.0, 0));
    end temperature_ph;

    replaceable function pressure_dT "Return pressure from d and T"
      extends Modelica.Icons.Function;
      input Density d "Density";
      input Temperature T "Temperature";
      output AbsolutePressure p "Pressure";
    algorithm
      p := pressure(setState_dTX(
              d,
              T,
              fill(0.0, 0)));
    end pressure_dT;

    replaceable function specificEnthalpy_dT
      "Return specific enthalpy from d and T"
      extends Modelica.Icons.Function;
      input Density d "Density";
      input Temperature T "Temperature";
      output SpecificEnthalpy h "Specific enthalpy";
    algorithm
      h := specificEnthalpy(setState_dTX(
              d,
              T,
              fill(0.0, 0)));
    end specificEnthalpy_dT;

    replaceable function specificEnthalpy_ps
      "Return specific enthalpy from p and s"
      extends Modelica.Icons.Function;
      input AbsolutePressure p "Pressure";
      input SpecificEntropy s "Specific entropy";
      output SpecificEnthalpy h "Specific enthalpy";
    algorithm
      h := specificEnthalpy_psX(
              p,
              s,
              fill(0.0, 0));
    end specificEnthalpy_ps;

    replaceable function temperature_ps "Return temperature from p and s"
      extends Modelica.Icons.Function;
      input AbsolutePressure p "Pressure";
      input SpecificEntropy s "Specific entropy";
      output Temperature T "Temperature";
    algorithm
      T := temperature_psX(
              p,
              s,
              fill(0.0, 0));
    end temperature_ps;

    replaceable function density_ps "Return density from p and s"
      extends Modelica.Icons.Function;
      input AbsolutePressure p "Pressure";
      input SpecificEntropy s "Specific entropy";
      output Density d "Density";
    algorithm
      d := density_psX(
              p,
              s,
              fill(0.0, 0));
    end density_ps;

    replaceable function specificEnthalpy_pT
      "Return specific enthalpy from p and T"
      extends Modelica.Icons.Function;
      input AbsolutePressure p "Pressure";
      input Temperature T "Temperature";
      output SpecificEnthalpy h "Specific enthalpy";
    algorithm
      h := specificEnthalpy_pTX(
              p,
              T,
              fill(0.0, 0));
    end specificEnthalpy_pT;

    replaceable function density_pT "Return density from p and T"
      extends Modelica.Icons.Function;
      input AbsolutePressure p "Pressure";
      input Temperature T "Temperature";
      output Density d "Density";
    algorithm
      d := density(setState_pTX(
              p,
              T,
              fill(0.0, 0)));
    end density_pT;

    redeclare replaceable function massFraction "Return independent mass fractions (if any)"
      extends Modelica.Icons.Function;
      input ThermodynamicState state "Thermodynamic state record";
      output MassFraction Xi[nXi] "Independent mass fractions";
    algorithm
      Xi := fill(0,0);
    end massFraction;

    redeclare replaceable partial model extends BaseProperties(final
        standardOrderComponents=true)
    end BaseProperties;
  end PartialPureSubstance;

  partial package PartialLinearFluid
    "Generic pure liquid model with constant cp, compressibility and thermal expansion coefficients"

    extends Interfaces.PartialPureSubstance(ThermoStates=Modelica.Media.Interfaces.Choices.IndependentVariables.pTX,
        singleState=false);
    constant SpecificHeatCapacity cp_const
      "Specific heat capacity at constant pressure";
    constant IsobaricExpansionCoefficient beta_const
      "Thermal expansion coefficient at constant pressure";
    constant SI.IsothermalCompressibility kappa_const
      "Isothermal compressibility";
    constant MolarMass MM_const "Molar mass";
    constant Density reference_d "Density in reference conditions";
    constant SpecificEnthalpy reference_h
      "Specific enthalpy in reference conditions";
    constant SpecificEntropy reference_s
      "Specific entropy in reference conditions";
    constant Boolean constantJacobian
      "If true, entries in thermodynamic Jacobian are constant, taken at reference conditions";

    redeclare record ThermodynamicState
      "A selection of variables that uniquely defines the thermodynamic state"
      extends Modelica.Icons.Record;
      AbsolutePressure p "Absolute pressure of medium";
      Temperature T "Temperature of medium";
    end ThermodynamicState;

    redeclare model extends BaseProperties(T(stateSelect=if
            preferredMediumStates then StateSelect.prefer else StateSelect.default),
        p(stateSelect=if preferredMediumStates then StateSelect.prefer else
            StateSelect.default)) "Base properties of medium"
    equation
      d = (1 + (p - reference_p)*kappa_const - (T - reference_T)*beta_const)*
        reference_d;
      h = reference_h + (T - reference_T)*cp_const + (p - reference_p)*(1 -
        beta_const*reference_T)/reference_d;
      u = h - p/d;
      p = state.p;
      T = state.T;
      MM = MM_const;
      R_s = Modelica.Constants.R/MM;
    end BaseProperties;

    redeclare function extends setState_pTX
      "Set the thermodynamic state record from p and T (X not needed)"
    algorithm
      state := ThermodynamicState(p=p, T=T);
    end setState_pTX;

    redeclare function extends setState_phX
      "Set the thermodynamic state record from p and h (X not needed)"
    algorithm
      state := ThermodynamicState(p=p, T=(h - reference_h - (p - reference_p)*(
        (1 - beta_const*reference_T)/reference_d))/cp_const + reference_T);
    end setState_phX;

    redeclare function extends setState_psX
      "Set the thermodynamic state record from p and s (X not needed)"
    algorithm
      state := ThermodynamicState(p=p, T=reference_T*cp_const/(cp_const - s +
        reference_s + (p - reference_p)*(-beta_const/reference_d)));
    end setState_psX;

    redeclare function extends setState_dTX
      "Set the thermodynamic state record from d and T (X not needed)"
    algorithm
      state := ThermodynamicState(p=((d - reference_d) + (T - reference_T)
        *beta_const*reference_d)/(reference_d*kappa_const) + reference_p, T=T);
    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 pressure
      "Return the pressure from the thermodynamic state"
    algorithm
      p := state.p;
    end pressure;

    redeclare function extends temperature
      "Return the temperature from the thermodynamic state"
    algorithm
      T := state.T;
    end temperature;

    redeclare function extends density
      "Return the density from the thermodynamic state"
    algorithm
      d := (1 + (state.p - reference_p)*kappa_const - (state.T - reference_T)*
        beta_const)*reference_d;
    end density;

    redeclare function extends specificEnthalpy
      "Return the specific enthalpy from the thermodynamic state"
    algorithm
      h := reference_h + (state.T - reference_T)*cp_const + (state.p -
        reference_p)*(1 - beta_const*reference_T)/reference_d;
    end specificEnthalpy;

    redeclare function extends specificEntropy
      "Return the specific entropy from the thermodynamic state"
    algorithm
      s := reference_s + (state.T - reference_T)*cp_const/state.T + (state.p -
        reference_p)*(-beta_const/reference_d);
    end specificEntropy;

    redeclare function extends specificInternalEnergy
      "Return the specific internal energy from the thermodynamic state"
    algorithm
      u := specificEnthalpy(state) - state.p/reference_d;
    end specificInternalEnergy;

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

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

    redeclare function extends velocityOfSound
      "Return velocity of sound from the thermodynamic state"
      extends Modelica.Icons.Function;
    algorithm
      a := sqrt(max(0, 1/(kappa_const*density(state) - beta_const*beta_const*
        state.T/cp_const)));
    end velocityOfSound;

    redeclare function extends isentropicExponent
      "Return isentropic exponent from the thermodynamic state"
      extends Modelica.Icons.Function;
    algorithm
      gamma := 1/(state.p*kappa_const)*cp_const/specificHeatCapacityCv(state);
    end isentropicExponent;

    redeclare function extends isentropicEnthalpy "Return isentropic enthalpy"

      /* Previous wrong equation:

protected
  SpecificEntropy s_upstream = specificEntropy(refState)
    "Specific entropy at component inlet";
  ThermodynamicState downstreamState "State at downstream location";
algorithm
  downstreamState.p := p_downstream;
  downstreamState.T := reference_T*cp_const/
    (s_upstream -reference_s -(p_downstream-reference_p)*(-beta_const/reference_d) - cp_const);
  h_is := specificEnthalpy(downstreamState);
*/
    algorithm
      /* s := reference_s + (refState.T-reference_T)*cp_const/refState.T +
                        (refState.p-reference_p)*(-beta_const/reference_d)
        = reference_s + (state.T-reference_T)*cp_const/state.T +
                        (p_downstream-reference_p)*(-beta_const/reference_d);

      (state.T-reference_T)*cp_const/state.T
     = (refState.T-reference_T)*cp_const/refState.T + (refState.p-reference_p)*(-beta_const/reference_d)
       - (p_downstream-reference_p)*(-beta_const/reference_d)
     = (refState.T-reference_T)*cp_const/refState.T + (refState.p-p_downstream)*(-beta_const/reference_d)

     (x - reference_T)/x = k
     x - reference_T = k*x
     (1-k)*x = reference_T
     x = reference_T/(1-k);

     state.T = reference_T/(1 - ((refState.T-reference_T)*cp_const/refState.T + (refState.p-p_downstream)*(-beta_const/reference_d))/cp_const)
  */

      h_is := specificEnthalpy(setState_pTX(
              p_downstream,
              reference_T/(1 - ((refState.T - reference_T)/refState.T + (
          refState.p - p_downstream)*(-beta_const/(reference_d*cp_const)))),
              reference_X));
    end isentropicEnthalpy;

    redeclare function extends specificHeatCapacityCp
      "Return specific heat capacity at constant volume"
    algorithm
      cp := cp_const;
    end specificHeatCapacityCp;

    redeclare function extends specificHeatCapacityCv
      "Return specific heat capacity at constant volume from the thermodynamic state"
    algorithm
      cv := if constantJacobian then cp_const - reference_T*beta_const*
        beta_const/(kappa_const*reference_d) else state.T*beta_const*beta_const
        /(kappa_const*reference_d);
    end specificHeatCapacityCv;

    redeclare function extends isothermalCompressibility
      "Return the isothermal compressibility kappa"
    algorithm
      kappa := kappa_const;
    end isothermalCompressibility;

    redeclare function extends isobaricExpansionCoefficient
      "Return the isobaric expansion coefficient"
    algorithm
      beta := beta_const;
    end isobaricExpansionCoefficient;

    redeclare function extends density_derp_h
      "Return density derivative w.r.t. pressure at constant specific enthalpy"
    algorithm
      ddph := if constantJacobian then kappa_const*reference_d + (beta_const*(1
         - reference_T*beta_const))/cp_const else kappa_const*density(state) +
        (beta_const*(1 - temperature(state)*beta_const))/cp_const;
    end density_derp_h;

    redeclare function extends density_derh_p
      "Return density derivative w.r.t. specific enthalpy at constant pressure"
    algorithm
      ddhp := if constantJacobian then -beta_const*reference_d/cp_const else -
        beta_const*density(state)/cp_const;
    end density_derh_p;

    redeclare function extends density_derp_T
      "Return density derivative w.r.t. pressure at constant temperature"
    algorithm
      ddpT := if constantJacobian then kappa_const*reference_d else kappa_const
        *density(state);
    end density_derp_T;

    redeclare function extends density_derT_p
      "Return density derivative w.r.t. temperature at constant pressure"
    algorithm
      ddTp := if constantJacobian then -beta_const*reference_d else -beta_const
        *density(state);
    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 molar mass"
    algorithm
      MM := MM_const;
    end molarMass;

    function T_ph "Return temperature from pressure and specific enthalpy"
      extends Modelica.Icons.Function;
      input SpecificEnthalpy h "Specific enthalpy";
      input AbsolutePressure p "Pressure";
      output Temperature T "Temperature";
    algorithm
      T := (h - reference_h - (p - reference_p)*((1 - beta_const*reference_T)/
        reference_d))/cp_const + reference_T;
    end T_ph;

    function T_ps "Return temperature from pressure and specific entropy"
      extends Modelica.Icons.Function;
      input AbsolutePressure p "Pressure";
      input SpecificEntropy s "Specific entropy";
      output Temperature T "Temperature";
    algorithm
      T := reference_T*cp_const/(s - reference_s - (p - reference_p)*(-
        beta_const/reference_d) - cp_const);
    end T_ps;
  end PartialLinearFluid;

  partial package PartialMixtureMedium
    "Base class for pure substances of several chemical substances"
    extends PartialMedium(redeclare replaceable record FluidConstants =
          Modelica.Media.Interfaces.Types.IdealGas.FluidConstants);

    redeclare replaceable record extends ThermodynamicState
      "Thermodynamic state variables"
      AbsolutePressure p "Absolute pressure of medium";
      Temperature T "Temperature of medium";
      MassFraction[nX] X(start=reference_X)
        "Mass fractions (= (component mass)/total mass  m_i/m)";
    end ThermodynamicState;

    redeclare replaceable function massFraction "Return independent mass fractions (if any)"
      extends Modelica.Icons.Function;
      input ThermodynamicState state "Thermodynamic state record";
      output MassFraction Xi[nXi] "Independent mass fractions";
    algorithm
      Xi := state.X[1:nXi];
    end massFraction;

    constant FluidConstants[nS] fluidConstants "Constant data for the fluid";

    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";
    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;
    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;
    end massToMoleFractions;

  end PartialMixtureMedium;

  partial package PartialCondensingGases
    "Base class for mixtures of condensing and non-condensing gases"
    extends PartialMixtureMedium(ThermoStates=Modelica.Media.Interfaces.Choices.IndependentVariables.pTX);

    replaceable partial function saturationPressure
      "Return saturation pressure of condensing fluid"
      extends Modelica.Icons.Function;
      input Temperature Tsat "Saturation temperature";
      output AbsolutePressure psat "Saturation pressure";
    end saturationPressure;

    replaceable partial function enthalpyOfVaporization
      "Return vaporization enthalpy of condensing fluid"
      extends Modelica.Icons.Function;
      input Temperature T "Temperature";
      output SpecificEnthalpy r0 "Vaporization enthalpy";
    end enthalpyOfVaporization;

    replaceable partial function enthalpyOfLiquid
      "Return liquid enthalpy of condensing fluid"
      extends Modelica.Icons.Function;
      input Temperature T "Temperature";
      output SpecificEnthalpy h "Liquid enthalpy";
    end enthalpyOfLiquid;

    replaceable partial function enthalpyOfGas
      "Return enthalpy of non-condensing gas mixture"
      extends Modelica.Icons.Function;
      input Temperature T "Temperature";
      input MassFraction[:] X "Vector of mass fractions";
      output SpecificEnthalpy h "Specific enthalpy";
    end enthalpyOfGas;

    replaceable partial function enthalpyOfCondensingGas
      "Return enthalpy of condensing gas (most often steam)"
      extends Modelica.Icons.Function;
      input Temperature T "Temperature";
      output SpecificEnthalpy h "Specific enthalpy";
    end enthalpyOfCondensingGas;

    replaceable partial function enthalpyOfNonCondensingGas
      "Return enthalpy of the non-condensing species"
      extends Modelica.Icons.Function;
      input Temperature T "Temperature";
      output SpecificEnthalpy h "Specific enthalpy";
    end enthalpyOfNonCondensingGas;
  end PartialCondensingGases;

  partial package PartialRealCondensingGases
    "Base class for mixtures of real condensing and non-condensing gases"
    extends Modelica.Media.Interfaces.PartialMixtureMedium(
      redeclare replaceable record FluidConstants =
        Modelica.Media.Interfaces.Types.TwoPhase.FluidConstants);

    replaceable partial function saturationPressure
      "Return saturation pressure of condensing fluid"
      extends Modelica.Icons.Function;
      input ThermodynamicState state "Thermodynamic state record";
      output AbsolutePressure psat "Saturation pressure";
    end saturationPressure;

    replaceable partial function saturationTemperature
      "Return saturation temperature of condensing fluid"
      extends Modelica.Icons.Function;
      input ThermodynamicState state "Thermodynamic state record";
      output Temperature Tsat "Saturation temperature";
    end saturationTemperature;

    replaceable partial function massFractionSaturation
      "Return saturation mass fractions"
      extends Modelica.Icons.Function;
      input ThermodynamicState state "Thermodynamic state record";
      output MassFraction[:] Xsat "Saturation mass fractions";
    end massFractionSaturation;

    replaceable partial function massFraction_pTphi
      "Return mass fractions as a function of pressure, temperature and relative humidity"
      extends Modelica.Icons.Function;
      input AbsolutePressure p "Pressure";
      input Temperature T "Temperature";
      input Real phi "Relative humidity";
      output MassFraction[:] X "Mass fractions";
    end massFraction_pTphi;

    replaceable partial function relativeHumidity "Return relative humidity"
      extends Modelica.Icons.Function;
      input ThermodynamicState state "Thermodynamic state record";
      output Real phi "Relative humidity";
    end relativeHumidity;

    replaceable partial function enthalpyOfVaporization
      "Return vaporization enthalpy of condensing fluid"
      extends Modelica.Icons.Function;
      input ThermodynamicState state "Thermodynamic state record";
      output SpecificEnthalpy r0 "Vaporization enthalpy";
    end enthalpyOfVaporization;

    replaceable partial function enthalpyOfLiquid
      "Return liquid enthalpy of condensing fluid"
      extends Modelica.Icons.Function;
      input ThermodynamicState state "Thermodynamic state record";
      output SpecificEnthalpy h "Liquid enthalpy";
    end enthalpyOfLiquid;

    replaceable partial function enthalpyOfGas
      "Return enthalpy of non-condensing gas mixture"
      extends Modelica.Icons.Function;
      input ThermodynamicState state "Thermodynamic state record";
      output SpecificEnthalpy h "Specific enthalpy";
    end enthalpyOfGas;

    replaceable partial function enthalpyOfCondensingGas
      "Return enthalpy of condensing gas (most often steam)"
      extends Modelica.Icons.Function;
      input ThermodynamicState state "Thermodynamic state record";
      output SpecificEnthalpy h "Specific enthalpy";
    end enthalpyOfCondensingGas;

    replaceable partial function enthalpyOfNonCondensingGas
      "Return enthalpy of the non-condensing species"
      extends Modelica.Icons.Function;
      input ThermodynamicState state "Thermodynamic state record";
      output SpecificEnthalpy h "Specific enthalpy";
    end enthalpyOfNonCondensingGas;

    replaceable partial function specificEntropy_phX
      "Return specific entropy as a function of pressure, specific enthalpy and mass fractions"
      extends Modelica.Icons.Function;
      input AbsolutePressure p "Pressure";
      input SpecificEnthalpy h "Specific enthalpy";
      input MassFraction X[:]=reference_X "Mass fractions";
      output SpecificEntropy s "Specific entropy";
    algorithm
      s := specificEntropy(setState_phX(
              p,
              h,
              X));
    end specificEntropy_phX;

  end PartialRealCondensingGases;

  partial package PartialTwoPhaseMedium
    "Base class for two phase medium of one substance"
    extends PartialPureSubstance(redeclare replaceable record FluidConstants =
          Modelica.Media.Interfaces.Types.TwoPhase.FluidConstants);
    constant Boolean smoothModel=false
      "= true, if the (derived) model should not generate state events";
    constant Boolean onePhase=false
      "= true, if the (derived) model should never be called with two-phase inputs";

    constant FluidConstants[nS] fluidConstants "Constant data for the fluid";

    redeclare replaceable record extends ThermodynamicState
      "Thermodynamic state of two phase medium"
      FixedPhase phase(min=0, max=2)
        "Phase of the fluid: 1 for 1-phase, 2 for two-phase, 0 for not known, e.g., interactive use";
    end ThermodynamicState;

    redeclare replaceable partial model extends BaseProperties
      "Base properties (p, d, T, h, u, R_s, MM, sat) of two phase medium"
      SaturationProperties sat "Saturation properties at the medium pressure";
    end BaseProperties;

    replaceable partial function setDewState
      "Return the thermodynamic state on the dew line"
      extends Modelica.Icons.Function;
      input SaturationProperties sat "Saturation point";
      input FixedPhase phase(
        min=1,
        max=2) = 1 "Phase: default is one phase";
      output ThermodynamicState state "Complete thermodynamic state info";
    end setDewState;

    replaceable partial function setBubbleState
      "Return the thermodynamic state on the bubble line"
      extends Modelica.Icons.Function;
      input SaturationProperties sat "Saturation point";
      input FixedPhase phase(
        min=1,
        max=2) = 1 "Phase: default is one phase";
      output ThermodynamicState state "Complete thermodynamic state info";
    end setBubbleState;

    redeclare replaceable partial function extends setState_dTX
      "Return thermodynamic state as function of d, T and composition X or Xi"
      input FixedPhase phase=0
        "2 for two-phase, 1 for one-phase, 0 if not known";
    end setState_dTX;

    redeclare replaceable partial function extends setState_phX
      "Return thermodynamic state as function of p, h and composition X or Xi"
      input FixedPhase phase=0
        "2 for two-phase, 1 for one-phase, 0 if not known";
    end setState_phX;

    redeclare replaceable partial function extends setState_psX
      "Return thermodynamic state as function of p, s and composition X or Xi"
      input FixedPhase phase=0
        "2 for two-phase, 1 for one-phase, 0 if not known";
    end setState_psX;

    redeclare replaceable partial function extends setState_pTX
      "Return thermodynamic state as function of p, T and composition X or Xi"
      input FixedPhase phase=0
        "2 for two-phase, 1 for one-phase, 0 if not known";
    end setState_pTX;

    replaceable function setSat_T
      "Return saturation property record from temperature"
      extends Modelica.Icons.Function;
      input Temperature T "Temperature";
      output SaturationProperties sat "Saturation property record";
    algorithm
      sat.Tsat := T;
      sat.psat := saturationPressure(T);
    end setSat_T;

    replaceable function setSat_p
      "Return saturation property record from pressure"
      extends Modelica.Icons.Function;
      input AbsolutePressure p "Pressure";
      output SaturationProperties sat "Saturation property record";
    algorithm
      sat.psat := p;
      sat.Tsat := saturationTemperature(p);
    end setSat_p;

    replaceable partial function bubbleEnthalpy
      "Return bubble point specific enthalpy"
      extends Modelica.Icons.Function;
      input SaturationProperties sat "Saturation property record";
      output SI.SpecificEnthalpy hl "Boiling curve specific enthalpy";
    end bubbleEnthalpy;

    replaceable partial function dewEnthalpy
      "Return dew point specific enthalpy"
      extends Modelica.Icons.Function;
      input SaturationProperties sat "Saturation property record";
      output SI.SpecificEnthalpy hv "Dew curve specific enthalpy";
    end dewEnthalpy;

    replaceable partial function bubbleEntropy
      "Return bubble point specific entropy"
      extends Modelica.Icons.Function;
      input SaturationProperties sat "Saturation property record";
      output SI.SpecificEntropy sl "Boiling curve specific entropy";
    end bubbleEntropy;

    replaceable partial function dewEntropy "Return dew point specific entropy"
      extends Modelica.Icons.Function;
      input SaturationProperties sat "Saturation property record";
      output SI.SpecificEntropy sv "Dew curve specific entropy";
    end dewEntropy;

    replaceable partial function bubbleDensity "Return bubble point density"
      extends Modelica.Icons.Function;
      input SaturationProperties sat "Saturation property record";
      output Density dl "Boiling curve density";
    end bubbleDensity;

    replaceable partial function dewDensity "Return dew point density"
      extends Modelica.Icons.Function;
      input SaturationProperties sat "Saturation property record";
      output Density dv "Dew curve density";
    end dewDensity;

    replaceable partial function saturationPressure
      "Return saturation pressure"
      extends Modelica.Icons.Function;
      input Temperature T "Temperature";
      output AbsolutePressure p "Saturation pressure";
    end saturationPressure;

    replaceable partial function saturationTemperature
      "Return saturation temperature"
      extends Modelica.Icons.Function;
      input AbsolutePressure p "Pressure";
      output Temperature T "Saturation temperature";
    end saturationTemperature;

    replaceable function saturationPressure_sat "Return saturation pressure"
      extends Modelica.Icons.Function;
      input SaturationProperties sat "Saturation property record";
      output AbsolutePressure p "Saturation pressure";
    algorithm
      p := sat.psat;
    end saturationPressure_sat;

    replaceable function saturationTemperature_sat
      "Return saturation temperature"
      extends Modelica.Icons.Function;
      input SaturationProperties sat "Saturation property record";
      output Temperature T "Saturation temperature";
    algorithm
      T := sat.Tsat;
    end saturationTemperature_sat;

    replaceable partial function saturationTemperature_derp
      "Return derivative of saturation temperature w.r.t. pressure"
      extends Modelica.Icons.Function;
      input AbsolutePressure p "Pressure";
      output DerTemperatureByPressure dTp
        "Derivative of saturation temperature w.r.t. pressure";
    end saturationTemperature_derp;

    replaceable function saturationTemperature_derp_sat
      "Return derivative of saturation temperature w.r.t. pressure"
      extends Modelica.Icons.Function;
      input SaturationProperties sat "Saturation property record";
      output DerTemperatureByPressure dTp
        "Derivative of saturation temperature w.r.t. pressure";
    algorithm
      dTp := saturationTemperature_derp(sat.psat);
    end saturationTemperature_derp_sat;

    replaceable partial function surfaceTension
      "Return surface tension sigma in the two phase region"
      extends Modelica.Icons.Function;
      input SaturationProperties sat "Saturation property record";
      output SurfaceTension sigma
        "Surface tension sigma in the two phase region";
    end surfaceTension;

    redeclare replaceable function extends molarMass
      "Return the molar mass of the medium"
    algorithm
      MM := fluidConstants[1].molarMass;
    end molarMass;

    replaceable partial function dBubbleDensity_dPressure
      "Return bubble point density derivative"
      extends Modelica.Icons.Function;
      input SaturationProperties sat "Saturation property record";
      output DerDensityByPressure ddldp "Boiling curve density derivative";
    end dBubbleDensity_dPressure;

    replaceable partial function dDewDensity_dPressure
      "Return dew point density derivative"
      extends Modelica.Icons.Function;
      input SaturationProperties sat "Saturation property record";
      output DerDensityByPressure ddvdp "Saturated steam density derivative";
    end dDewDensity_dPressure;

    replaceable partial function dBubbleEnthalpy_dPressure
      "Return bubble point specific enthalpy derivative"
      extends Modelica.Icons.Function;
      input SaturationProperties sat "Saturation property record";
      output DerEnthalpyByPressure dhldp
        "Boiling curve specific enthalpy derivative";
    end dBubbleEnthalpy_dPressure;

    replaceable partial function dDewEnthalpy_dPressure
      "Return dew point specific enthalpy derivative"
      extends Modelica.Icons.Function;

      input SaturationProperties sat "Saturation property record";
      output DerEnthalpyByPressure dhvdp
        "Saturated steam specific enthalpy derivative";
    end dDewEnthalpy_dPressure;

    redeclare replaceable function specificEnthalpy_pTX
      "Return specific enthalpy from pressure, temperature and mass fraction"
      extends Modelica.Icons.Function;
      input AbsolutePressure p "Pressure";
      input Temperature T "Temperature";
      input MassFraction X[:] "Mass fractions";
      input FixedPhase phase=0
        "2 for two-phase, 1 for one-phase, 0 if not known";
      output SpecificEnthalpy h "Specific enthalpy at p, T, X";
    algorithm
      h := specificEnthalpy(setState_pTX(
              p,
              T,
              X,
              phase));
    end specificEnthalpy_pTX;

    redeclare replaceable 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[:] "Mass fractions";
      input FixedPhase phase=0
        "2 for two-phase, 1 for one-phase, 0 if not known";
      output Temperature T "Temperature";
    algorithm
      T := temperature(setState_phX(
              p,
              h,
              X,
              phase));
    end temperature_phX;

    redeclare replaceable 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[:] "Mass fractions";
      input FixedPhase phase=0
        "2 for two-phase, 1 for one-phase, 0 if not known";
      output Density d "Density";
    algorithm
      d := density(setState_phX(
              p,
              h,
              X,
              phase));
    end density_phX;

    redeclare replaceable function temperature_psX
      "Return temperature from p, s, and X or Xi"
      extends Modelica.Icons.Function;
      input AbsolutePressure p "Pressure";
      input SpecificEntropy s "Specific entropy";
      input MassFraction X[:] "Mass fractions";
      input FixedPhase phase=0
        "2 for two-phase, 1 for one-phase, 0 if not known";
      output Temperature T "Temperature";
    algorithm
      T := temperature(setState_psX(
              p,
              s,
              X,
              phase));
    end temperature_psX;

    redeclare replaceable function density_psX
      "Return density from p, s, and X or Xi"
      extends Modelica.Icons.Function;
      input AbsolutePressure p "Pressure";
      input SpecificEntropy s "Specific entropy";
      input MassFraction X[:] "Mass fractions";
      input FixedPhase phase=0
        "2 for two-phase, 1 for one-phase, 0 if not known";
      output Density d "Density";
    algorithm
      d := density(setState_psX(
              p,
              s,
              X,
              phase));
    end density_psX;

    redeclare replaceable function specificEnthalpy_psX
      "Return specific enthalpy from p, s, and X or Xi"
      extends Modelica.Icons.Function;
      input AbsolutePressure p "Pressure";
      input SpecificEntropy s "Specific entropy";
      input MassFraction X[:] "Mass fractions";
      input FixedPhase phase=0
        "2 for two-phase, 1 for one-phase, 0 if not known";
      output SpecificEnthalpy h "Specific enthalpy";
    algorithm
      h := specificEnthalpy(setState_psX(
              p,
              s,
              X,
              phase));
    end specificEnthalpy_psX;

    redeclare replaceable function setState_pT
      "Return thermodynamic state from p and T"
      extends Modelica.Icons.Function;
      input AbsolutePressure p "Pressure";
      input Temperature T "Temperature";
      input FixedPhase phase=0
        "2 for two-phase, 1 for one-phase, 0 if not known";
      output ThermodynamicState state "Thermodynamic state record";
    algorithm
      state := setState_pTX(
              p,
              T,
              fill(0.0, 0),
              phase);
    end setState_pT;

    redeclare replaceable function setState_ph
      "Return thermodynamic state from p and h"
      extends Modelica.Icons.Function;
      input AbsolutePressure p "Pressure";
      input SpecificEnthalpy h "Specific enthalpy";
      input FixedPhase phase=0
        "2 for two-phase, 1 for one-phase, 0 if not known";
      output ThermodynamicState state "Thermodynamic state record";
    algorithm
      state := setState_phX(
              p,
              h,
              fill(0.0, 0),
              phase);
    end setState_ph;

    redeclare replaceable function setState_ps
      "Return thermodynamic state from p and s"
      extends Modelica.Icons.Function;
      input AbsolutePressure p "Pressure";
      input SpecificEntropy s "Specific entropy";
      input FixedPhase phase=0
        "2 for two-phase, 1 for one-phase, 0 if not known";
      output ThermodynamicState state "Thermodynamic state record";
    algorithm
      state := setState_psX(
              p,
              s,
              fill(0.0, 0),
              phase);
    end setState_ps;

    redeclare replaceable function setState_dT
      "Return thermodynamic state from d and T"
      extends Modelica.Icons.Function;
      input Density d "Density";
      input Temperature T "Temperature";
      input FixedPhase phase=0
        "2 for two-phase, 1 for one-phase, 0 if not known";
      output ThermodynamicState state "Thermodynamic state record";
    algorithm
      state := setState_dTX(
              d,
              T,
              fill(0.0, 0),
              phase);
    end setState_dT;

    replaceable function setState_px
      "Return thermodynamic state from pressure and vapour quality"
      extends Modelica.Icons.Function;
      input AbsolutePressure p "Pressure";
      input MassFraction x "Vapour quality";
      output ThermodynamicState state "Thermodynamic state record";
    algorithm
      state := setState_ph(
              p,
              (1 - x)*bubbleEnthalpy(setSat_p(p)) + x*dewEnthalpy(setSat_p(p)),
              2);
    end setState_px;

    replaceable function setState_Tx
      "Return thermodynamic state from temperature and vapour quality"
      extends Modelica.Icons.Function;
      input Temperature T "Temperature";
      input MassFraction x "Vapour quality";
      output ThermodynamicState state "Thermodynamic state record";
    algorithm
      state := setState_ph(
              saturationPressure_sat(setSat_T(T)),
              (1 - x)*bubbleEnthalpy(setSat_T(T)) + x*dewEnthalpy(setSat_T(T)),
              2);
    end setState_Tx;

    replaceable function vapourQuality "Return vapour quality"
      extends Modelica.Icons.Function;
      input ThermodynamicState state "Thermodynamic state record";
      output MassFraction x "Vapour quality";
    protected
      constant SpecificEnthalpy eps=1e-8;
    algorithm
      x := min(max((specificEnthalpy(state) - bubbleEnthalpy(setSat_p(pressure(
        state))))/(dewEnthalpy(setSat_p(pressure(state))) - bubbleEnthalpy(
        setSat_p(pressure(state))) + eps), 0), 1);
    end vapourQuality;

    redeclare replaceable function density_ph "Return density from p and h"
      extends Modelica.Icons.Function;
      input AbsolutePressure p "Pressure";
      input SpecificEnthalpy h "Specific enthalpy";
      input FixedPhase phase=0
        "2 for two-phase, 1 for one-phase, 0 if not known";
      output Density d "Density";
    algorithm
      d := density_phX(
              p,
              h,
              fill(0.0, 0),
              phase);
    end density_ph;

    redeclare replaceable function temperature_ph
      "Return temperature from p and h"
      extends Modelica.Icons.Function;
      input AbsolutePressure p "Pressure";
      input SpecificEnthalpy h "Specific enthalpy";
      input FixedPhase phase=0
        "2 for two-phase, 1 for one-phase, 0 if not known";
      output Temperature T "Temperature";
    algorithm
      T := temperature_phX(
              p,
              h,
              fill(0.0, 0),
              phase);
    end temperature_ph;

    redeclare replaceable function pressure_dT "Return pressure from d and T"
      extends Modelica.Icons.Function;
      input Density d "Density";
      input Temperature T "Temperature";
      input FixedPhase phase=0
        "2 for two-phase, 1 for one-phase, 0 if not known";
      output AbsolutePressure p "Pressure";
    algorithm
      p := pressure(setState_dTX(
              d,
              T,
              fill(0.0, 0),
              phase));
    end pressure_dT;

    redeclare replaceable function specificEnthalpy_dT
      "Return specific enthalpy from d and T"
      extends Modelica.Icons.Function;
      input Density d "Density";
      input Temperature T "Temperature";
      input FixedPhase phase=0
        "2 for two-phase, 1 for one-phase, 0 if not known";
      output SpecificEnthalpy h "Specific enthalpy";
    algorithm
      h := specificEnthalpy(setState_dTX(
              d,
              T,
              fill(0.0, 0),
              phase));
    end specificEnthalpy_dT;

    redeclare replaceable function specificEnthalpy_ps
      "Return specific enthalpy from p and s"
      extends Modelica.Icons.Function;
      input AbsolutePressure p "Pressure";
      input SpecificEntropy s "Specific entropy";
      input FixedPhase phase=0
        "2 for two-phase, 1 for one-phase, 0 if not known";
      output SpecificEnthalpy h "Specific enthalpy";
    algorithm
      h := specificEnthalpy_psX(
              p,
              s,
              fill(0.0, 0));
    end specificEnthalpy_ps;

    redeclare replaceable function temperature_ps
      "Return temperature from p and s"
      extends Modelica.Icons.Function;
      input AbsolutePressure p "Pressure";
      input SpecificEntropy s "Specific entropy";
      input FixedPhase phase=0
        "2 for two-phase, 1 for one-phase, 0 if not known";
      output Temperature T "Temperature";
    algorithm
      T := temperature_psX(
              p,
              s,
              fill(0.0, 0),
              phase);
    end temperature_ps;

    redeclare replaceable function density_ps "Return density from p and s"
      extends Modelica.Icons.Function;
      input AbsolutePressure p "Pressure";
      input SpecificEntropy s "Specific entropy";
      input FixedPhase phase=0
        "2 for two-phase, 1 for one-phase, 0 if not known";
      output Density d "Density";
    algorithm
      d := density_psX(
              p,
              s,
              fill(0.0, 0),
              phase);
    end density_ps;

    redeclare replaceable function specificEnthalpy_pT
      "Return specific enthalpy from p and T"
      extends Modelica.Icons.Function;
      input AbsolutePressure p "Pressure";
      input Temperature T "Temperature";
      input FixedPhase phase=0
        "2 for two-phase, 1 for one-phase, 0 if not known";
      output SpecificEnthalpy h "Specific enthalpy";
    algorithm
      h := specificEnthalpy_pTX(
              p,
              T,
              fill(0.0, 0),
              phase);
    end specificEnthalpy_pT;

    redeclare replaceable function density_pT "Return density from p and T"
      extends Modelica.Icons.Function;
      input AbsolutePressure p "Pressure";
      input Temperature T "Temperature";
      input FixedPhase phase=0
        "2 for two-phase, 1 for one-phase, 0 if not known";
      output Density d "Density";
    algorithm
      d := density(setState_pTX(
              p,
              T,
              fill(0.0, 0),
              phase));
    end density_pT;
  end PartialTwoPhaseMedium;

  partial package PartialSimpleMedium
    "Medium model with linear dependency of u, h from temperature. All other quantities, especially density, are constant."

    extends Interfaces.PartialPureSubstance(final ThermoStates=Modelica.Media.Interfaces.Choices.IndependentVariables.pT,
        final singleState=true);

    constant SpecificHeatCapacity cp_const
      "Constant specific heat capacity at constant pressure";
    constant SpecificHeatCapacity cv_const
      "Constant specific heat capacity at constant volume";
    constant Density d_const "Constant density";
    constant DynamicViscosity eta_const "Constant dynamic viscosity";
    constant ThermalConductivity lambda_const "Constant thermal conductivity";
    constant VelocityOfSound a_const "Constant velocity of sound";
    constant Temperature T_min "Minimum temperature valid for medium model";
    constant Temperature T_max "Maximum temperature valid for medium model";
    constant Temperature T0=reference_T "Zero enthalpy temperature";
    constant MolarMass MM_const "Molar mass";

    constant FluidConstants[nS] fluidConstants "Fluid constants";

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

    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)) "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;
      state.T = T;
      state.p = p;
    end BaseProperties;

    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);
    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 := ThermodynamicState(p=p, T=T0 + h/cp_const);
    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";
    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[:] "Mass fractions";
      output SpecificEnthalpy h "Specific enthalpy";
    algorithm
      h := cp_const*(T - T0);
    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[:] "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[:] "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);
    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;
    end molarMass;
  end PartialSimpleMedium;

  partial package PartialSimpleIdealGasMedium
    "Medium model of Ideal gas with constant cp and cv. All other quantities, e.g., transport properties, are constant."

    extends Interfaces.PartialPureSubstance(
      redeclare replaceable record FluidConstants =
          Modelica.Media.Interfaces.Types.Basic.FluidConstants,
      ThermoStates=Modelica.Media.Interfaces.Choices.IndependentVariables.pT,
      final singleState=false);

    constant SpecificHeatCapacity cp_const
      "Constant specific heat capacity at constant pressure";
    constant SpecificHeatCapacity cv_const=cp_const - R_gas
      "Constant specific heat capacity at constant volume";
    constant SpecificHeatCapacity R_gas "Medium specific gas constant";
    constant MolarMass MM_const "Molar mass";
    constant DynamicViscosity eta_const "Constant dynamic viscosity";
    constant ThermalConductivity lambda_const "Constant thermal conductivity";
    constant Temperature T_min "Minimum temperature valid for medium model";
    constant Temperature T_max "Maximum temperature valid for medium model";
    constant Temperature T0=reference_T "Zero enthalpy temperature";
    constant FluidConstants[nS] fluidConstants "Fluid constants";

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

    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)) "Base properties of ideal gas"
    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 = specificEnthalpy_pTX(
              p,
              T,
              X);
      u = h - R_s*T;
      R_s = R_gas;
      d = p/(R_s*T);
      MM = MM_const;
      state.T = T;
      state.p = p;
    end BaseProperties;

    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);
    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 := ThermodynamicState(p=p, T=T0 + h/cp_const);
    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) + R_gas*Modelica.Math.log(p/reference_p)));
    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
      state := ThermodynamicState(p=d*R_gas*T, T=T);
    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 pressure "Return pressure of ideal gas"

    algorithm
      p := state.p;
    end pressure;

    redeclare function extends temperature "Return temperature of ideal gas"

    algorithm
      T := state.T;
    end temperature;

    redeclare function extends density "Return density of ideal gas"
    algorithm
      d := state.p/(R_gas*state.T);
    end density;

    redeclare function extends specificEnthalpy "Return specific enthalpy"
      extends Modelica.Icons.Function;
    algorithm
      h := cp_const*(state.T - T0);
    end specificEnthalpy;

    redeclare function extends specificInternalEnergy
      "Return specific internal energy"
      extends Modelica.Icons.Function;
    algorithm
      u := cp_const*(state.T - T0) - R_gas*state.T;
    end specificInternalEnergy;

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

    redeclare function extends specificGibbsEnergy
      "Return specific Gibbs energy"
      extends Modelica.Icons.Function;
    algorithm
      g := cp_const*(state.T - T0) - 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 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 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 := sqrt(cp_const/cv_const*R_gas*state.T);
    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[:] "Mass fractions";
      output SpecificEnthalpy h "Specific enthalpy at p, T, X";
    algorithm
      h := cp_const*(T - T0);
    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[:] "Mass fractions";
      output Temperature T "Temperature";
    algorithm
      T := h/cp_const + T0;
    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[:] "Mass fractions";
      output Density d "Density";
    algorithm
      d := density(setState_phX(
              p,
              h,
              X));
    end density_phX;

    redeclare function extends isentropicEnthalpy "Return isentropic enthalpy"
    algorithm
      /*  s = cp_const*log(refState.T/T0) - R_gas*log(refState.p/reference_p)
          = cp_const*log(state.T/T0) - R_gas*log(p_downstream/reference_p)

        log(state.T) = log(refState.T) +
                       (R_gas/cp_const)*(log(p_downstream/reference_p) - log(refState.p/reference_p))
                     = log(refState.T) + (R_gas/cp_const)*log(p_downstream/refState.p)
                     = log(refState.T) + log( (p_downstream/refState.p)^(R_gas/cp_const) )
                     = log( refState.T*(p_downstream/refState.p)^(R_gas/cp_const) )
        state.T = refstate.T*(p_downstream/refstate.p)^(R_gas/cp_const)
    */
      h_is := cp_const*(refState.T*(p_downstream/refState.p)^(R_gas/cp_const)
         - T0);
    end isentropicEnthalpy;

    redeclare function extends isobaricExpansionCoefficient
      "Returns overall the isobaric expansion coefficient beta"
    algorithm
      /* beta = 1/v * der(v,T), with v = 1/d, at constant pressure p:
       v = R_s*T/p
       der(v,T) = R_s/p
       beta = p/(R_s*T)*R_s/p
            = 1/T
    */

      beta := 1/state.T;
    end isobaricExpansionCoefficient;

    redeclare function extends isothermalCompressibility
      "Returns overall the isothermal compressibility factor"
    algorithm
      /* kappa = - 1/v * der(v,p), with v = 1/d at constant temperature T.
       v = R_s*T/p
       der(v,T) = -R_s*T/p^2
       kappa = p/(R_s*T)*R_s*T/p^2
             = 1/p
    */
      kappa := 1/state.p;
    end isothermalCompressibility;

    redeclare function extends density_derp_T
      "Returns the partial derivative of density with respect to pressure at constant temperature"
    algorithm
      /*  d = p/(R_s*T)
        ddpT = 1/(R_s*T)
    */
      ddpT := 1/(R_gas*state.T);
    end density_derp_T;

    redeclare function extends density_derT_p
      "Returns the partial derivative of density with respect to temperature at constant pressure"
    algorithm
      /*  d = p/(R_s*T)
        ddpT = -p/(R_s*T^2)
    */
      ddTp := -state.p/(R_gas*state.T*state.T);
    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 "Returns the molar mass of the medium"
    algorithm
      MM := MM_const;
    end molarMass;
  end PartialSimpleIdealGasMedium;

  package Choices "Types, constants to define menu choices"
    extends Modelica.Icons.Package;

    type IndependentVariables = enumeration(
        T "Temperature",
        pT "Pressure, Temperature",
        ph "Pressure, Specific Enthalpy",
        phX "Pressure, Specific Enthalpy, Mass Fraction",
        pTX "Pressure, Temperature, Mass Fractions",
        dTX "Density, Temperature, Mass Fractions")
      "Enumeration defining the independent variables of a medium";

    type Init = enumeration(
        NoInit "NoInit (no initialization)",
        InitialStates "InitialStates (initialize medium states)",
        SteadyState "SteadyState (initialize in steady state)",
        SteadyMass
          "SteadyMass (initialize density or pressure in steady state)")
      "Enumeration defining initialization for fluid flow"

    type ReferenceEnthalpy = enumeration(
        ZeroAt0K
          "The enthalpy is 0 at 0 K (default), if the enthalpy of formation is excluded",
        ZeroAt25C
          "The enthalpy is 0 at 25 degC, if the enthalpy of formation is excluded",
        UserDefined
          "The user-defined reference enthalpy is used at 293.15 K (25 degC)")
      "Enumeration defining the reference enthalpy of a medium"

    type ReferenceEntropy = enumeration(
        ZeroAt0K "The entropy is 0 at 0 K (default)",
        ZeroAt0C "The entropy is 0 at 0 degC",
        UserDefined
          "The user-defined reference entropy is used at 293.15 K (25 degC)")
      "Enumeration defining the reference entropy of a medium"

    type pd = enumeration(
        default "Default (no boundary condition for p or d)",
        p_known "p_known (pressure p is known)",
        d_known "d_known (density d is known)")
      "Enumeration defining whether p or d are known for the boundary condition"

    type Th = enumeration(
        default "Default (no boundary condition for T or h)",
        T_known "T_known (temperature T is known)",
        h_known "h_known (specific enthalpy h is known)")
      "Enumeration defining whether T or h are known as boundary condition"
  end Choices;

  package Types "Types to be used in fluid models"
    extends Modelica.Icons.Package;

    type AbsolutePressure = SI.AbsolutePressure (
        min=0,
        max=1.e8,
        nominal=1.e5,
        start=1.e5)
      "Type for absolute pressure with medium specific attributes";

    type Density = SI.Density (
        min=0,
        max=1.e5,
        nominal=1,
        start=1) "Type for density with medium specific attributes";
    type DynamicViscosity = SI.DynamicViscosity (
        min=0,
        max=1.e8,
        nominal=1.e-3,
        start=1.e-3)
      "Type for dynamic viscosity with medium specific attributes";
    type EnthalpyFlowRate = SI.EnthalpyFlowRate (
        nominal=1000.0,
        min=-1.0e8,
        max=1.e8) "Type for enthalpy flow rate with medium specific attributes";
    type MassFraction = Real (
        quantity="MassFraction",
        final unit="kg/kg",
        min=0,
        max=1,
        nominal=0.1) "Type for mass fraction with medium specific attributes";
    type MoleFraction = Real (
        quantity="MoleFraction",
        final unit="mol/mol",
        min=0,
        max=1,
        nominal=0.1) "Type for mole fraction with medium specific attributes";
    type MolarMass = SI.MolarMass (
        min=0.001,
        max=0.25,
        nominal=0.032) "Type for molar mass with medium specific attributes";
    type MolarVolume = SI.MolarVolume (
        min=1e-6,
        max=1.0e6,
        nominal=1.0) "Type for molar volume with medium specific attributes";
    type IsentropicExponent = SI.RatioOfSpecificHeatCapacities (
        min=1,
        max=500000,
        nominal=1.2,
        start=1.2)
      "Type for isentropic exponent with medium specific attributes";
    type SpecificEnergy = SI.SpecificEnergy (
        min=-1.0e8,
        max=1.e8,
        nominal=1.e6)
      "Type for specific energy with medium specific attributes";
    type SpecificInternalEnergy = SpecificEnergy
      "Type for specific internal energy with medium specific attributes";
    type SpecificEnthalpy = SI.SpecificEnthalpy (
        min=-1.0e10,
        max=1.e10,
        nominal=1.e6)
      "Type for specific enthalpy with medium specific attributes";
    type SpecificEntropy = SI.SpecificEntropy (
        min=-1.e7,
        max=1.e7,
        nominal=1.e3)
      "Type for specific entropy with medium specific attributes";
    type SpecificHeatCapacity = SI.SpecificHeatCapacity (
        min=0,
        max=1.e7,
        nominal=1.e3,
        start=1.e3)
      "Type for specific heat capacity with medium specific attributes";
    type SurfaceTension = SI.SurfaceTension
      "Type for surface tension with medium specific attributes";
    type Temperature = SI.Temperature (
        min=1,
        max=1.e4,
        nominal=300,
        start=288.15) "Type for temperature with medium specific attributes";
    type ThermalConductivity = SI.ThermalConductivity (
        min=0,
        max=500,
        nominal=1,
        start=1)
      "Type for thermal conductivity with medium specific attributes";
    type PrandtlNumber = SI.PrandtlNumber (
        min=1e-3,
        max=1e5,
        nominal=1.0) "Type for Prandtl number with medium specific attributes";
    type VelocityOfSound = SI.Velocity (
        min=0,
        max=1.e5,
        nominal=1000,
        start=1000)
      "Type for velocity of sound with medium specific attributes";
    type ExtraProperty = Real (min=0.0, start=1.0)
      "Type for unspecified, mass-specific property transported by flow";
    type CumulativeExtraProperty = Real (min=0.0, start=1.0)
      "Type for conserved integral of unspecified, mass specific property";
    type ExtraPropertyFlowRate = Real (unit="kg/s")
      "Type for flow rate of unspecified, mass-specific property";
    type IsobaricExpansionCoefficient = Real (
        min=0,
        max=1.0e8,
        unit="1/K")
      "Type for isobaric expansion coefficient with medium specific attributes";
    type DipoleMoment = Real (
        min=0.0,
        max=2.0,
        unit="debye",
        quantity="ElectricDipoleMoment")
      "Type for dipole moment with medium specific attributes";

    type DerDensityByPressure = SI.DerDensityByPressure
      "Type for partial derivative of density with respect to pressure with medium specific attributes";
    type DerDensityByEnthalpy = SI.DerDensityByEnthalpy
      "Type for partial derivative of density with respect to enthalpy with medium specific attributes";
    type DerEnthalpyByPressure = SI.DerEnthalpyByPressure
      "Type for partial derivative of enthalpy with respect to pressure with medium specific attributes";
    type DerDensityByTemperature = SI.DerDensityByTemperature
      "Type for partial derivative of density with respect to temperature with medium specific attributes";
    type DerTemperatureByPressure = Real (final unit="K/Pa")
      "Type for partial derivative of temperature with respect to pressure with medium specific attributes";

    replaceable record SaturationProperties
      "Saturation properties of two phase medium"
      extends Modelica.Icons.Record;
      AbsolutePressure psat "Saturation pressure";
      Temperature Tsat "Saturation temperature";
    end SaturationProperties;

    record FluidLimits "Validity limits for fluid model"
      extends Modelica.Icons.Record;
      Temperature TMIN "Minimum temperature";
      Temperature TMAX "Maximum temperature";
      Density DMIN "Minimum density";
      Density DMAX "Maximum density";
      AbsolutePressure PMIN "Minimum pressure";
      AbsolutePressure PMAX "Maximum pressure";
      SpecificEnthalpy HMIN "Minimum enthalpy";
      SpecificEnthalpy HMAX "Maximum enthalpy";
      SpecificEntropy SMIN "Minimum entropy";
      SpecificEntropy SMAX "Maximum entropy";
    end FluidLimits;

    type FixedPhase = Integer (min=0, max=2)
      "Phase of the fluid: 1 for 1-phase, 2 for two-phase, 0 for not known, e.g., interactive use";
    package Basic
      "The most basic version of a record used in several degrees of detail"
      extends Icons.Package;

      record FluidConstants
        "Critical, triple, molecular and other standard data of fluid"
        extends Modelica.Icons.Record;
        String iupacName
          "Complete IUPAC name (or common name, if non-existent)";
        String casRegistryNumber
          "Chemical abstracts sequencing number (if it exists)";
        String chemicalFormula
          "Chemical formula, (brutto, nomenclature according to Hill";
        String structureFormula "Chemical structure formula";
        MolarMass molarMass "Molar mass";
      end FluidConstants;

    end Basic;

    package IdealGas
      "The ideal gas version of a record used in several degrees of detail"
      extends Icons.Package;

      record FluidConstants "Extended fluid constants"
        extends Modelica.Media.Interfaces.Types.Basic.FluidConstants;
        Temperature criticalTemperature "Critical temperature";
        AbsolutePressure criticalPressure "Critical pressure";
        MolarVolume criticalMolarVolume "Critical molar Volume";
        Real acentricFactor "Pitzer acentric factor";
        //   Temperature triplePointTemperature "Triple point temperature";
        //   AbsolutePressure triplePointPressure "Triple point pressure";
        Temperature meltingPoint "Melting point at 101325 Pa";
        Temperature normalBoilingPoint "Normal boiling point (at 101325 Pa)";
        DipoleMoment dipoleMoment
          "Dipole moment of molecule in Debye (1 debye = 3.33564e10-30 C.m)";
        Boolean hasIdealGasHeatCapacity=false
          "= true, if ideal gas heat capacity is available";
        Boolean hasCriticalData=false "= true, if critical data are known";
        Boolean hasDipoleMoment=false "= true, if a dipole moment known";
        Boolean hasFundamentalEquation=false "= true, if a fundamental equation";
        Boolean hasLiquidHeatCapacity=false
          "= true, if liquid heat capacity is available";
        Boolean hasSolidHeatCapacity=false
          "= true, if solid heat capacity is available";
        Boolean hasAccurateViscosityData=false
          "= true, if accurate data for a viscosity function is available";
        Boolean hasAccurateConductivityData=false
          "= true, if accurate data for thermal conductivity is available";
        Boolean hasVapourPressureCurve=false
          "= true, if vapour pressure data, e.g., Antoine coefficients are known";
        Boolean hasAcentricFactor=false
          "= true, if Pitzer acentric factor is known";
        SpecificEnthalpy HCRIT0=0.0
          "Critical specific enthalpy of the fundamental equation";
        SpecificEntropy SCRIT0=0.0
          "Critical specific entropy of the fundamental equation";
        SpecificEnthalpy deltah=0.0
          "Difference between specific enthalpy model (h_m) and f.eq. (h_f) (h_m - h_f)";
        SpecificEntropy deltas=0.0
          "Difference between specific enthalpy model (s_m) and f.eq. (s_f) (s_m - s_f)";
      end FluidConstants;
    end IdealGas;

    package TwoPhase
      "The two phase fluid version of a record used in several degrees of detail"
      extends Icons.Package;

      record FluidConstants "Extended fluid constants"
        extends Modelica.Media.Interfaces.Types.Basic.FluidConstants;
        Temperature criticalTemperature "Critical temperature";
        AbsolutePressure criticalPressure "Critical pressure";
        MolarVolume criticalMolarVolume "Critical molar Volume";
        Real acentricFactor "Pitzer acentric factor";
        Temperature triplePointTemperature "Triple point temperature";
        AbsolutePressure triplePointPressure "Triple point pressure";
        Temperature meltingPoint "Melting point at 101325 Pa";
        Temperature normalBoilingPoint "Normal boiling point (at 101325 Pa)";
        DipoleMoment dipoleMoment
          "Dipole moment of molecule in Debye (1 debye = 3.33564e10-30 C.m)";
        Boolean hasIdealGasHeatCapacity=false
          "= true, if ideal gas heat capacity is available";
        Boolean hasCriticalData=false "= true, if critical data are known";
        Boolean hasDipoleMoment=false "= true, if a dipole moment known";
        Boolean hasFundamentalEquation=false "= true, if a fundamental equation";
        Boolean hasLiquidHeatCapacity=false
          "= true, if liquid heat capacity is available";
        Boolean hasSolidHeatCapacity=false
          "= true, if solid heat capacity is available";
        Boolean hasAccurateViscosityData=false
          "= true, if accurate data for a viscosity function is available";
        Boolean hasAccurateConductivityData=false
          "= true, if accurate data for thermal conductivity is available";
        Boolean hasVapourPressureCurve=false
          "= true, if vapour pressure data, e.g., Antoine coefficients are known";
        Boolean hasAcentricFactor=false
          "= true, if Pitzer acentric factor is known";
        SpecificEnthalpy HCRIT0=0.0
          "Critical specific enthalpy of the fundamental equation";
        SpecificEntropy SCRIT0=0.0
          "Critical specific entropy of the fundamental equation";
        SpecificEnthalpy deltah=0.0
          "Difference between specific enthalpy model (h_m) and f.eq. (h_f) (h_m - h_f)";
        SpecificEntropy deltas=0.0
          "Difference between specific enthalpy model (s_m) and f.eq. (s_f) (s_m - s_f)";
      end FluidConstants;
    end TwoPhase;

  end Types;
end Interfaces;

package Common "Data structures and fundamental functions for fluid properties"

  extends Modelica.Icons.Package;
  type Rate = Real (final quantity="Rate", final unit="s-1");
  type MolarFlowRate = Real (final quantity="MolarFlowRate", final unit="mol/s");
  type MolarReactionRate = Real (final quantity="MolarReactionRate", final unit=
         "mol/(m3.s)");
  type MolarEnthalpy = Real (final quantity="MolarEnthalpy", final unit="J/mol");
  type DerDensityByEntropy = Real (final quantity="DerDensityByEntropy", final
        unit="kg2.K/(m3.J)");
  type DerEnergyByPressure = Real (final quantity="DerEnergyByPressure", final
        unit="J/Pa");
  type DerEnergyByMoles = Real (final quantity="DerEnergyByMoles", final unit=
          "J/mol");
  type DerEntropyByTemperature = Real (final quantity="DerEntropyByTemperature",
        final unit="J/K2");
  type DerEntropyByPressure = Real (final quantity="DerEntropyByPressure",
        final unit="J/(K.Pa)");
  type DerEntropyByMoles = Real (final quantity="DerEntropyByMoles", final unit=
         "J/(mol.K)");
  type DerPressureByDensity = Real (final quantity="DerPressureByDensity",
        final unit="Pa.m3/kg");
  type DerPressureBySpecificVolume = Real (final quantity=
          "DerPressureBySpecificVolume", final unit="Pa.kg/m3");
  type DerPressureByTemperature = Real (final quantity=
          "DerPressureByTemperature", final unit="Pa/K");
  type DerVolumeByTemperature = Real (final quantity="DerVolumeByTemperature",
        final unit="m3/K");
  type DerVolumeByPressure = Real (final quantity="DerVolumeByPressure", final
        unit="m3/Pa");
  type DerVolumeByMoles = Real (final quantity="DerVolumeByMoles", final unit=
          "m3/mol");
  type IsenthalpicExponent = Real (final quantity="IsenthalpicExponent", unit=
          "1");
  type IsentropicExponent = Real (final quantity="IsentropicExponent", unit="1");
  type IsobaricVolumeExpansionCoefficient = Real (final quantity=
          "IsobaricVolumeExpansionCoefficient", unit="1/K");
  type IsochoricPressureCoefficient = Real (final quantity=
          "IsochoricPressureCoefficient", unit="1/K");
  type IsothermalCompressibility = Real (final quantity=
          "IsothermalCompressibility", unit="1/Pa");
  type JouleThomsonCoefficient = Real (final quantity="JouleThomsonCoefficient",
        unit="K/Pa");
  // introduce min-manx-nominal values
  constant Real MINPOS=1.0e-9
    "Minimal value for physical variables which are always > 0.0";

  constant SI.Area AMIN=MINPOS "Minimal init area";
  constant SI.Area AMAX=1.0e5 "Maximal init area";
  constant SI.Area ANOM=1.0 "Nominal init area";
  constant SI.AmountOfSubstance MOLMIN=-1.0*MINPOS "Minimal Mole Number";
  constant SI.AmountOfSubstance MOLMAX=1.0e8 "Maximal Mole Number";
  constant SI.AmountOfSubstance MOLNOM=1.0 "Nominal Mole Number";
  constant SI.Density DMIN=1e-6 "Minimal init density";
  constant SI.Density DMAX=30.0e3 "Maximal init density";
  constant SI.Density DNOM=1.0 "Nominal init density";
  constant SI.ThermalConductivity LAMMIN=MINPOS "Minimal thermal conductivity";
  constant SI.ThermalConductivity LAMNOM=1.0 "Nominal thermal conductivity";
  constant SI.ThermalConductivity LAMMAX=1000.0 "Maximal thermal conductivity";
  constant SI.DynamicViscosity ETAMIN=MINPOS "Minimal init dynamic viscosity";
  constant SI.DynamicViscosity ETAMAX=1.0e8 "Maximal init dynamic viscosity";
  constant SI.DynamicViscosity ETANOM=100.0 "Nominal init dynamic viscosity";
  constant SI.Energy EMIN=-1.0e10 "Minimal init energy";
  constant SI.Energy EMAX=1.0e10 "Maximal init energy";
  constant SI.Energy ENOM=1.0e3 "Nominal init energy";
  constant SI.Entropy SMIN=-1.0e6 "Minimal init entropy";
  constant SI.Entropy SMAX=1.0e6 "Maximal init entropy";
  constant SI.Entropy SNOM=1.0e3 "Nominal init entropy";
  constant SI.MassFlowRate MDOTMIN=-1.0e5 "Minimal init mass flow rate";
  constant SI.MassFlowRate MDOTMAX=1.0e5 "Maximal init mass flow rate";
  constant SI.MassFlowRate MDOTNOM=1.0 "Nominal init mass flow rate";
  constant SI.MassFraction MASSXMIN=-1.0*MINPOS "Minimal init mass fraction";
  constant SI.MassFraction MASSXMAX=1.0 "Maximal init mass fraction";
  constant SI.MassFraction MASSXNOM=0.1 "Nominal init mass fraction";
  constant SI.Mass MMIN=-1.0*MINPOS "Minimal init mass";
  constant SI.Mass MMAX=1.0e8 "Maximal init mass";
  constant SI.Mass MNOM=1.0 "Nominal init mass";
  constant SI.MolarMass MMMIN=0.001 "Minimal initial molar mass";
  constant SI.MolarMass MMMAX=250.0 "Maximal initial molar mass";
  constant SI.MolarMass MMNOM=0.2 "Nominal initial molar mass";
  constant SI.MoleFraction MOLEYMIN=-1.0*MINPOS "Minimal init mole fraction";
  constant SI.MoleFraction MOLEYMAX=1.0 "Maximal init mole fraction";
  constant SI.MoleFraction MOLEYNOM=0.1 "Nominal init mole fraction";
  constant SI.MomentumFlux GMIN=-1.0e8 "Minimal init momentum flux";
  constant SI.MomentumFlux GMAX=1.0e8 "Maximal init momentum flux";
  constant SI.MomentumFlux GNOM=1.0 "Nominal init momentum flux";
  constant SI.Power POWMIN=-1.0e8 "Minimal init power or heat";
  constant SI.Power POWMAX=1.0e8 "Maximal init power or heat";
  constant SI.Power POWNOM=1.0e3 "Nominal init power or heat";
  constant SI.Pressure PMIN=1.0e4 "Minimal init pressure";
  constant SI.Pressure PMAX=1.0e8 "Maximal init pressure";
  constant SI.Pressure PNOM=1.0e5 "Nominal init pressure";
  constant SI.Pressure COMPPMIN=-1.0*MINPOS "Minimal init pressure";
  constant SI.Pressure COMPPMAX=1.0e8 "Maximal init pressure";
  constant SI.Pressure COMPPNOM=1.0e5 "Nominal init pressure";
  constant SI.RatioOfSpecificHeatCapacities KAPPAMIN=1.0
    "Minimal init isentropic exponent";
  constant SI.RatioOfSpecificHeatCapacities KAPPAMAX=1.7
    "Maximal init isentropic exponent";
  constant SI.RatioOfSpecificHeatCapacities KAPPANOM=1.2
    "Nominal init isentropic exponent";
  constant SI.SpecificEnergy SEMIN=-1.0e8 "Minimal init specific energy";
  constant SI.SpecificEnergy SEMAX=1.0e8 "Maximal init specific energy";
  constant SI.SpecificEnergy SENOM=1.0e6 "Nominal init specific energy";
  constant SI.SpecificEnthalpy SHMIN=-1.0e8 "Minimal init specific enthalpy";
  constant SI.SpecificEnthalpy SHMAX=1.0e8 "Maximal init specific enthalpy";
  constant SI.SpecificEnthalpy SHNOM=1.0e6 "Nominal init specific enthalpy";
  constant SI.SpecificEntropy SSMIN=-1.0e6 "Minimal init specific entropy";
  constant SI.SpecificEntropy SSMAX=1.0e6 "Maximal init specific entropy";
  constant SI.SpecificEntropy SSNOM=1.0e3 "Nominal init specific entropy";
  constant SI.SpecificHeatCapacity CPMIN=MINPOS
    "Minimal init specific heat capacity";
  constant SI.SpecificHeatCapacity CPMAX=1.0e6
    "Maximal init specific heat capacity";
  constant SI.SpecificHeatCapacity CPNOM=1.0e3
    "Nominal init specific heat capacity";
  constant SI.Temperature TMIN=1.0 "Minimal init temperature";
  constant SI.Temperature TMAX=6000.0 "Maximal init temperature";
  constant SI.Temperature TNOM=320.0 "Nominal init temperature";
  constant SI.ThermalConductivity LMIN=MINPOS
    "Minimal init thermal conductivity";
  constant SI.ThermalConductivity LMAX=500.0
    "Maximal init thermal conductivity";
  constant SI.ThermalConductivity LNOM=1.0 "Nominal init thermal conductivity";
  constant SI.Velocity VELMIN=-1.0e5 "Minimal init speed";
  constant SI.Velocity VELMAX=1.0e5 "Maximal init speed";
  constant SI.Velocity VELNOM=1.0 "Nominal init speed";
  constant SI.Volume VMIN=0.0 "Minimal init volume";
  constant SI.Volume VMAX=1.0e5 "Maximal init volume";
  constant SI.Volume VNOM=1.0e-3 "Nominal init volume";

  package ThermoFluidSpecial "Property records used by the ThermoFluid library"
    extends Modelica.Icons.Package;
    record FixedIGProperties "Constant properties for ideal gases"
      extends Modelica.Icons.Record;
      parameter Integer nspecies(min=1) "Number of components";
      SI.MolarMass[nspecies] MM "Molar mass of components";
      Real[nspecies] invMM "Inverse of molar mass of components";
      SI.SpecificHeatCapacity[nspecies] R_s "Gas constant";
      SI.SpecificEnthalpy[nspecies] Hf "Enthalpy of formation at 298.15K";
      SI.SpecificEnthalpy[nspecies] H0 "H0(298.15K) - H0(0K)";
    end FixedIGProperties;

    record ThermoBaseVars
      extends Modelica.Icons.Record;
      parameter Integer n(min=1) "Discretization number";
      parameter Integer nspecies(min=1) "Number of species";
      SI.Pressure[n] p(
        min=PMIN,
        max=PMAX,
        nominal=PNOM,
        start=fill(1.0e5, n)) "Pressure";
      SI.Temperature[n] T(
        min=TMIN,
        max=TMAX,
        nominal=TNOM) "Temperature";
      SI.Density[n] d(
        min=DMIN,
        max=DMAX,
        nominal=DNOM) "Density";
      SI.SpecificEnthalpy[n] h(
        min=SHMIN,
        max=SHMAX,
        nominal=SHNOM) "Specific enthalpy";
      SI.SpecificEntropy[n] s(
        min=SSMIN,
        max=SSMAX,
        nominal=SSNOM) "Specific entropy";
      SI.RatioOfSpecificHeatCapacities[n] kappa "Ratio of cp/cv";
      SI.Mass[n] M(
        min=MMIN,
        max=MMAX,
        nominal=MNOM) "Total mass";
      SI.Energy[n] U(
        min=EMIN,
        max=EMAX,
        nominal=ENOM) "Inner energy";
      SI.MassFlowRate[n] dM(
        min=MDOTMIN,
        max=MDOTMAX,
        nominal=MDOTNOM) "Change in total mass";
      SI.Power[n] dU(
        min=POWMIN,
        max=POWMAX,
        nominal=POWNOM) "Change in inner energy";
      SI.Volume[n] V(
        min=VMIN,
        max=VMAX,
        nominal=VNOM) "Volume";
      SI.MassFraction[n, nspecies] mass_x(
        min=MASSXMIN,
        max=MASSXMAX,
        nominal=MASSXNOM) "Mass fraction";
      SI.MoleFraction[n, nspecies] mole_y(
        min=MOLEYMIN,
        max=MOLEYMAX,
        nominal=MOLEYNOM) "Mole fraction";
      SI.Mass[n, nspecies] M_x(
        min=MMIN,
        max=MMAX,
        nominal=MNOM) "Component mass";
      SI.MassFlowRate[n, nspecies] dM_x(
        min=MDOTMIN,
        max=MDOTMAX,
        nominal=MDOTNOM) "Rate of change in component mass";
      MolarFlowRate[n, nspecies] dZ(
        min=-1.0e6,
        max=1.0e6,
        nominal=0.0) "Rate of change in component moles";
      MolarFlowRate[n, nspecies] rZ(
        min=-1.0e6,
        max=1.0e6,
        nominal=0.0) "Reaction(source) mole rates";
      SI.MolarMass[n] MM(
        min=MMMIN,
        max=MMMAX,
        nominal=MMNOM) "Molar mass of mixture";
      SI.AmountOfSubstance[n] Moles(
        min=MOLMIN,
        max=MOLMAX,
        nominal=MOLNOM) "Total moles";
      SI.AmountOfSubstance[n, nspecies] Moles_z(
        min=MOLMIN,
        max=MOLMAX,
        nominal=MOLNOM) "Mole vector";
    end ThermoBaseVars;

    record ThermoProperties
      "Thermodynamic base property data for all state models"
      extends Modelica.Icons.Record;
      parameter Integer nspecies(min=1) "Number of species";
      SI.Temperature T(
        min=TMIN,
        max=TMAX,
        nominal=TNOM) "Temperature";
      SI.Density d(
        min=DMIN,
        max=DMAX,
        nominal=DNOM) "Density";
      SI.Pressure p(
        min=PMIN,
        max=PMAX,
        nominal=PNOM) "Pressure";
      SI.Volume V(
        min=VMIN,
        max=VMAX,
        nominal=VNOM) "Volume";
      SI.SpecificEnthalpy h(
        min=SHMIN,
        max=SHMAX,
        nominal=SHNOM) "Specific enthalpy";
      SI.SpecificEnergy u(
        min=SEMIN,
        max=SEMAX,
        nominal=SENOM) "Specific inner energy";
      SI.SpecificEntropy s(
        min=SSMIN,
        max=SSMAX,
        nominal=SSNOM) "Specific entropy";
      SI.SpecificGibbsFreeEnergy g(
        min=SHMIN,
        max=SHMAX,
        nominal=SHNOM) "Specific Gibbs free energy";
      SI.SpecificHeatCapacity cp(
        min=CPMIN,
        max=CPMAX,
        nominal=CPNOM) "Heat capacity at constant pressure";
      SI.SpecificHeatCapacity cv(
        min=CPMIN,
        max=CPMAX,
        nominal=CPNOM) "Heat capacity at constant volume";
      SI.SpecificHeatCapacity R_s(
        min=CPMIN,
        max=CPMAX,
        nominal=CPNOM) "Gas constant";
      SI.MolarMass MM(
        min=MMMIN,
        max=MMMAX,
        nominal=MMNOM) "Molar mass of mixture";
      SI.MassFraction[nspecies] mass_x(
        min=MASSXMIN,
        max=MASSXMAX,
        nominal=MASSXNOM) "Mass fraction";
      SI.MoleFraction[nspecies] mole_y(
        min=MOLEYMIN,
        max=MOLEYMAX,
        nominal=MOLEYNOM) "Mole fraction";
      SI.RatioOfSpecificHeatCapacities kappa "Ratio of cp/cv";
      SI.DerDensityByTemperature ddTp
        "Derivative of density by temperature at constant pressure";
      SI.DerDensityByPressure ddpT
        "Derivative of density by pressure at constant temperature";
      Real dupT(unit="m3.kg-1")
        "Derivative of inner energy by pressure at constant T";
      Real dudT(unit="(J.m3)/(kg2)")
        "Derivative of inner energy by density at constant T";
      SI.SpecificHeatCapacity duTp
        "Derivative of inner energy by temperature at constant p";
      SI.SpecificEnergy ddx[nspecies]
        "Derivative vector of density by change in mass composition";
      SI.SpecificEnergy[nspecies] compu(
        min=SEMIN,
        max=SEMAX,
        nominal=SENOM) "Inner energy of the components";
      SI.Pressure[nspecies] compp(
        min=COMPPMIN,
        max=COMPPMAX,
        nominal=COMPPNOM) "Partial pressures of the components";
      SI.Velocity a(
        min=VELMIN,
        max=VELMAX,
        nominal=VELNOM) "Speed of sound";
      SI.HeatCapacity dUTZ
        "Derivative of inner energy by temperature at constant moles";
      SI.MolarInternalEnergy[nspecies] dUZT
        "Derivative of inner energy by moles at constant temperature";
      SI.SpecificEnthalpy[nspecies] dHMxT(
        min=SEMIN,
        max=SEMAX,
        nominal=SENOM)
        "Derivative of total enthalpy w.r.t. component mass at constant T";
      Real dpT "Derivative of pressure w.r.t. temperature";
      Real dpZ[nspecies] "Derivative of pressure w.r.t. moles";
    end ThermoProperties;

    record ThermoProperties_ph
      "Thermodynamic property data for pressure p and specific enthalpy h as dynamic states"

      extends Modelica.Icons.Record;
      SI.Temperature T(
        min=1.0e-9,
        max=10000.0,
        nominal=298.15) "Temperature";
      SI.Density d(
        min=1.0e-9,
        max=10000.0,
        nominal=10.0) "Density";
      SI.SpecificEnergy u(
        min=-1.0e8,
        max=1.0e8,
        nominal=1.0e6) "Specific inner energy";
      SI.SpecificEntropy s(
        min=-1.0e6,
        max=1.0e6,
        nominal=1.0e3) "Specific entropy";
      SI.SpecificHeatCapacity cp(
        min=1.0,
        max=1.0e6,
        nominal=1000.0) "Heat capacity at constant pressure";
      SI.SpecificHeatCapacity cv(
        min=1.0,
        max=1.0e6,
        nominal=1000.0) "Heat capacity at constant volume";
      SI.SpecificHeatCapacity R_s(
        min=1.0,
        max=1.0e6,
        nominal=1000.0) "Gas constant";
      SI.RatioOfSpecificHeatCapacities kappa "Ratio of cp/cv";
      SI.Velocity a(
        min=1.0,
        max=10000.0,
        nominal=300.0) "Speed of sound";
      SI.DerDensityByEnthalpy ddhp
        "Derivative of density by enthalpy at constant pressure";
      SI.DerDensityByPressure ddph
        "Derivative of density by pressure at constant enthalpy";
      Real duph(unit="m3/kg")
        "Derivative of inner energy by pressure at constant enthalpy";
      Real duhp(unit="1")
        "Derivative of inner energy by enthalpy at constant pressure";
    end ThermoProperties_ph;

    record ThermoProperties_pT
      "Thermodynamic property data for pressure p and temperature T as dynamic states"

      extends Modelica.Icons.Record;
      SI.Density d(
        min=1.0e-9,
        max=10000.0,
        nominal=10.0) "Density";
      SI.SpecificEnthalpy h(
        min=-1.0e8,
        max=1.0e8,
        nominal=1.0e6) "Specific enthalpy";
      SI.SpecificEnergy u(
        min=-1.0e8,
        max=1.0e8,
        nominal=1.0e6) "Specific inner energy";
      SI.SpecificEntropy s(
        min=-1.0e6,
        max=1.0e6,
        nominal=1.0e3) "Specific entropy";
      SI.SpecificHeatCapacity cp(
        min=1.0,
        max=1.0e6,
        nominal=1000.0) "Heat capacity at constant pressure";
      SI.SpecificHeatCapacity cv(
        min=1.0,
        max=1.0e6,
        nominal=1000.0) "Heat capacity at constant volume";
      SI.SpecificHeatCapacity R_s(
        min=1.0,
        max=1.0e6,
        nominal=1000.0) "Gas constant";
      SI.RatioOfSpecificHeatCapacities kappa "Ratio of cp/cv";
      SI.Velocity a(
        min=1.0,
        max=10000.0,
        nominal=300.0) "Speed of sound";
      SI.DerDensityByTemperature ddTp
        "Derivative of density by temperature at constant pressure";
      SI.DerDensityByPressure ddpT
        "Derivative of density by pressure at constant temperature";
      Real dupT(unit="m3.kg-1")
        "Derivative of inner energy by pressure at constant T";
      SI.SpecificHeatCapacity duTp
        "Derivative of inner energy by temperature at constant p";
    end ThermoProperties_pT;

    record ThermoProperties_dT
      "Thermodynamic property data for density d and temperature T as dynamic states"

      extends Modelica.Icons.Record;
      SI.Pressure p(
        min=1.0,
        max=1.0e9,
        nominal=1.0e5) "Pressure";
      SI.SpecificEnthalpy h(
        min=-1.0e8,
        max=1.0e8,
        nominal=1.0e6) "Specific enthalpy";
      SI.SpecificEnergy u(
        min=-1.0e8,
        max=1.0e8,
        nominal=1.0e6) "Specific inner energy";
      SI.SpecificEntropy s(
        min=-1.0e6,
        max=1.0e6,
        nominal=1.0e3) "Specific entropy";
      SI.SpecificHeatCapacity cp(
        min=1.0,
        max=1.0e6,
        nominal=1000.0) "Heat capacity at constant pressure";
      SI.SpecificHeatCapacity cv(
        min=1.0,
        max=1.0e6,
        nominal=1000.0) "Heat capacity at constant volume";
      SI.SpecificHeatCapacity R_s(
        min=1.0,
        max=1.0e6,
        nominal=1000.0) "Gas constant";
      SI.RatioOfSpecificHeatCapacities kappa "Ratio of cp/cv";
      SI.Velocity a(
        min=1.0,
        max=10000.0,
        nominal=300.0) "Speed of sound";
      Real dudT(unit="m5/(kg.s2)")
        "Derivative of inner energy by density at constant T";
    end ThermoProperties_dT;

    //   record GibbsDerivs

    //     "Derivatives of dimensionless Gibbs-function w.r.t. dimensionless pressure and temperature"
    //     extends Modelica.Icons.Record;
    //     Real pi "Dimensionless pressure";
    //     Real tau "Dimensionless temperature";
    //     Real g "Dimensionless Gibbs-function";
    //     Real gpi "Derivative of g w.r.t. pi";
    //     Real gpipi "2nd derivative of g w.r.t. pi";
    //     Real gtau "Derivative of g w.r.t. tau";
    //     Real gtautau "2nd derivative of g w.r.t. tau";
    //     Real gtaupi "Mixed derivative of g w.r.t. pi and tau";
    //   end GibbsDerivs;

    //   record HelmholtzDerivs

    //     "Derivatives of dimensionless Helmholtz-function w.r.t. dimensionless pressure, density and temperature"
    //     extends Modelica.Icons.Record;
    //     Real delta "Dimensionless density";
    //     Real tau "Dimensionless temperature";
    //     Real f "Dimensionless Helmholtz-function";
    //     Real fdelta "Derivative of f w.r.t. delta";
    //     Real fdeltadelta "2nd derivative of f w.r.t. delta";
    //     Real ftau "Derivative of f w.r.t. tau";
    //     Real ftautau "2nd derivative of f w.r.t. tau";
    //     Real fdeltatau "Mixed derivative of f w.r.t. delta and tau";
    //   end HelmholtzDerivs;

    record TransportProps "Record with transport properties"
      extends Modelica.Icons.Record;
      SI.DynamicViscosity eta;
      SI.ThermalConductivity lam;
    end TransportProps;

    function gibbsToProps_ph
      "Calculate property record for pressure and specific enthalpy as states from dimensionless Gibbs function"

      extends Modelica.Icons.Function;
      input GibbsDerivs g "Dimensionless derivatives of Gibbs function";
      output ThermoProperties_ph pro
        "Property record for pressure and specific enthalpy as dynamic states";
    protected
      Real vt(unit="m3.kg-1.K-1")
        "Derivative of specific volume w.r.t. temperature";
      Real vp(unit="m4.kg-2.s2")
        "Derivative of specific volume w.r.t. pressure";
    algorithm
      pro.T := g.T;
      pro.R_s := g.R_s;
      pro.d := g.p/(pro.R_s*pro.T*g.pi*g.gpi);
      pro.u := g.T*g.R_s*(g.tau*g.gtau - g.pi*g.gpi);
      pro.s := pro.R_s*(g.tau*g.gtau - g.g);
      pro.cp := -pro.R_s*g.tau*g.tau*g.gtautau;
      pro.cv := pro.R_s*(-g.tau*g.tau*g.gtautau + (g.gpi - g.tau*g.gtaupi)*(g.gpi
         - g.tau*g.gtaupi)/(g.gpipi));
      pro.a := sqrt(abs(g.R_s*g.T*(g.gpi*g.gpi/((g.gpi - g.tau*g.gtaupi)*(g.gpi - g.tau
        *g.gtaupi)/(g.tau*g.tau*g.gtautau) - g.gpipi))));
      vt := g.R_s/g.p*(g.pi*g.gpi - g.tau*g.pi*g.gtaupi);
      vp := g.R_s*g.T/(g.p*g.p)*g.pi*g.pi*g.gpipi;
      pro.kappa := -1/(pro.d*g.p)*pro.cp/(vp*pro.cp + vt*vt*g.T);
      pro.ddhp := -pro.d*pro.d*vt/(pro.cp);
      pro.ddph := -pro.d*pro.d*(vp*pro.cp - vt/pro.d + g.T*vt*vt)/pro.cp;
      pro.duph := -1/pro.d + g.p/(pro.d*pro.d)*pro.ddph;
      pro.duhp := 1 + g.p/(pro.d*pro.d)*pro.ddhp;
    end gibbsToProps_ph;

    function gibbsToBoundaryProps
      "Calculate phase boundary property record from dimensionless Gibbs function"

      extends Modelica.Icons.Function;
      input GibbsDerivs g "Dimensionless derivatives of Gibbs function";
      output PhaseBoundaryProperties sat "Phase boundary properties";
    protected
      Real vt(unit="m3.kg-1.K-1")
        "Derivative of specific volume w.r.t. temperature";
      Real vp(unit="m4.kg-2.s2")
        "Derivative of specific volume w.r.t. pressure";
    algorithm
      sat.d := g.p/(g.R_s*g.T*g.pi*g.gpi);
      sat.h := g.R_s*g.T*g.tau*g.gtau;
      sat.u := g.T*g.R_s*(g.tau*g.gtau - g.pi*g.gpi);
      sat.s := g.R_s*(g.tau*g.gtau - g.g);
      sat.cp := -g.R_s*g.tau*g.tau*g.gtautau;
      sat.cv := g.R_s*(-g.tau*g.tau*g.gtautau + (g.gpi - g.tau*g.gtaupi)*(g.gpi
         - g.tau*g.gtaupi)/(g.gpipi));
      vt := g.R_s/g.p*(g.pi*g.gpi - g.tau*g.pi*g.gtaupi);
      vp := g.R_s*g.T/(g.p*g.p)*g.pi*g.pi*g.gpipi;
      // sat.kappa := -1/(sat.d*g.p)*sat.cp/(vp*sat.cp + vt*vt*g.T);
      sat.pt := -g.p/g.T*(g.gpi - g.tau*g.gtaupi)/(g.gpipi*g.pi);
      sat.pd := -g.R_s*g.T*g.gpi*g.gpi/(g.gpipi);
    end gibbsToBoundaryProps;

    function gibbsToProps_dT
      "Calculate property record for density and temperature as states from dimensionless Gibbs function"

      extends Modelica.Icons.Function;
      input GibbsDerivs g "Dimensionless derivatives of Gibbs function";
      output ThermoProperties_dT pro
        "Property record for density and temperature as dynamic states";
    protected
      Real vt(unit="m3.kg-1.K-1")
        "Derivative of specific volume w.r.t. temperature";
      Real vp(unit="m4.kg-2.s2")
        "Derivative of specific volume w.r.t. pressure";
      SI.Density d;
    algorithm
      pro.R_s := g.R_s;
      pro.p := g.p;
      pro.u := g.T*g.R_s*(g.tau*g.gtau - g.pi*g.gpi);
      pro.h := g.R_s*g.T*g.tau*g.gtau;
      pro.s := pro.R_s*(g.tau*g.gtau - g.g);
      pro.cp := -pro.R_s*g.tau*g.tau*g.gtautau;
      pro.cv := pro.R_s*(-g.tau*g.tau*g.gtautau + (g.gpi - g.tau*g.gtaupi)*(g.gpi
         - g.tau*g.gtaupi)/g.gpipi);
      vt := g.R_s/g.p*(g.pi*g.gpi - g.tau*g.pi*g.gtaupi);
      vp := g.R_s*g.T/(g.p*g.p)*g.pi*g.pi*g.gpipi;
      pro.kappa := -1/((g.p/(pro.R_s*g.T*g.pi*g.gpi))*g.p)*pro.cp/(vp*pro.cp + vt
        *vt*g.T);
      pro.a := sqrt(abs(g.R_s*g.T*(g.gpi*g.gpi/((g.gpi - g.tau*g.gtaupi)*(g.gpi - g.tau
        *g.gtaupi)/(g.tau*g.tau*g.gtautau) - g.gpipi))));

      d := g.p/(pro.R_s*g.T*g.pi*g.gpi);
      pro.dudT := (pro.p - g.T*vt/vp)/(d*d);
    end gibbsToProps_dT;

    function gibbsToProps_pT
      "Calculate property record for pressure and temperature as states from dimensionless Gibbs function"

      extends Modelica.Icons.Function;
      input GibbsDerivs g "Dimensionless derivatives of Gibbs function";
      output ThermoProperties_pT pro
        "Property record for pressure and temperature as dynamic states";
    protected
      Real vt(unit="m3.kg-1.K-1")
        "Derivative of specific volume w.r.t. temperature";
      Real vp(unit="m4.kg-2.s2")
        "Derivative of specific volume w.r.t. pressure";
    algorithm
      pro.R_s := g.R_s;
      pro.d := g.p/(pro.R_s*g.T*g.pi*g.gpi);
      pro.u := g.T*g.R_s*(g.tau*g.gtau - g.pi*g.gpi);
      pro.h := g.R_s*g.T*g.tau*g.gtau;
      pro.s := pro.R_s*(g.tau*g.gtau - g.g);
      pro.cp := -pro.R_s*g.tau*g.tau*g.gtautau;
      pro.cv := pro.R_s*(-g.tau*g.tau*g.gtautau + (g.gpi - g.tau*g.gtaupi)*(g.gpi
         - g.tau*g.gtaupi)/g.gpipi);
      vt := g.R_s/g.p*(g.pi*g.gpi - g.tau*g.pi*g.gtaupi);
      vp := g.R_s*g.T/(g.p*g.p)*g.pi*g.pi*g.gpipi;
      pro.kappa := -1/(pro.d*g.p)*pro.cp/(vp*pro.cp + vt*vt*g.T);
      pro.a := sqrt(abs(g.R_s*g.T*(g.gpi*g.gpi/((g.gpi - g.tau*g.gtaupi)*(g.gpi - g.tau
        *g.gtaupi)/(g.tau*g.tau*g.gtautau) - g.gpipi))));
      pro.ddpT := -(pro.d*pro.d)*vp;
      pro.ddTp := -(pro.d*pro.d)*vt;
      pro.duTp := pro.cp - g.p*vt;
      pro.dupT := -g.T*vt - g.p*vp;
    end gibbsToProps_pT;

    function helmholtzToProps_ph
      "Calculate property record for pressure and specific enthalpy as states from dimensionless Helmholtz function"

      extends Modelica.Icons.Function;
      input HelmholtzDerivs f "Dimensionless derivatives of Helmholtz function";
      output ThermoProperties_ph pro
        "Property record for pressure and specific enthalpy as dynamic states";
    protected
      SI.Pressure p "Pressure";
      DerPressureByDensity pd "Derivative of pressure w.r.t. density";
      DerPressureByTemperature pt "Derivative of pressure w.r.t. temperature";
      DerPressureBySpecificVolume pv
        "Derivative of pressure w.r.t. specific volume";
    algorithm
      pro.d := f.d;
      pro.T := f.T;
      pro.R_s := f.R_s;
      pro.s := f.R_s*(f.tau*f.ftau - f.f);
      pro.u := f.R_s*f.T*f.tau*f.ftau;
      p := pro.d*pro.R_s*pro.T*f.delta*f.fdelta;
      pd := f.R_s*f.T*f.delta*(2.0*f.fdelta + f.delta*f.fdeltadelta);
      pt := f.R_s*f.d*f.delta*(f.fdelta - f.tau*f.fdeltatau);
      pv := -pd*f.d*f.d;

      // calculating cp near the critical point may be troublesome (cp -> inf).
      pro.cp := f.R_s*(-f.tau*f.tau*f.ftautau + (f.delta*f.fdelta - f.delta*f.tau
        *f.fdeltatau)^2/(2*f.delta*f.fdelta + f.delta*f.delta*f.fdeltadelta));
      pro.cv := f.R_s*(-f.tau*f.tau*f.ftautau);
      pro.kappa := 1/(f.d*f.R_s*f.d*f.T*f.delta*f.fdelta)*((-pv*pro.cv + pt*pt*f.T)
        /(pro.cv));
      pro.a := sqrt(abs(f.R_s*f.T*(2*f.delta*f.fdelta + f.delta*f.delta*f.fdeltadelta
         - ((f.delta*f.fdelta - f.delta*f.tau*f.fdeltatau)*(f.delta*f.fdelta -
        f.delta*f.tau*f.fdeltatau))/(f.tau*f.tau*f.ftautau))));
      pro.ddph := (f.d*(pro.cv*f.d + pt))/(f.d*f.d*pd*pro.cv + f.T*pt*pt);
      pro.ddhp := -f.d*f.d*pt/(f.d*f.d*pd*pro.cv + f.T*pt*pt);
      pro.duph := -1/pro.d + p/(pro.d*pro.d)*pro.ddph;
      pro.duhp := 1 + p/(pro.d*pro.d)*pro.ddhp;
    end helmholtzToProps_ph;

    function helmholtzToProps_pT
      "Calculate property record for pressure and temperature as states from dimensionless Helmholtz function"

      extends Modelica.Icons.Function;
      input HelmholtzDerivs f "Dimensionless derivatives of Helmholtz function";
      output ThermoProperties_pT pro
        "Property record for pressure and temperature as dynamic states";
    protected
      DerPressureByDensity pd "Derivative of pressure w.r.t. density";
      DerPressureByTemperature pt "Derivative of pressure w.r.t. temperature";
      DerPressureBySpecificVolume pv
        "Derivative of pressure w.r.t. specific volume";
      IsobaricVolumeExpansionCoefficient alpha
        "Isobaric volume expansion coefficient";
      // beta in Bejan
      IsothermalCompressibility gamma "Isothermal compressibility";
      // kappa in Bejan
      SI.Pressure p "Pressure";
    algorithm
      pro.d := f.d;
      pro.R_s := f.R_s;
      pro.s := f.R_s*(f.tau*f.ftau - f.f);
      pro.h := f.R_s*f.T*(f.tau*f.ftau + f.delta*f.fdelta);
      pro.u := f.R_s*f.T*f.tau*f.ftau;
      pd := f.R_s*f.T*f.delta*(2.0*f.fdelta + f.delta*f.fdeltadelta);
      pt := f.R_s*f.d*f.delta*(f.fdelta - f.tau*f.fdeltatau);
      pv := -(f.d*f.d)*pd;
      alpha := -f.d*pt/pv;
      gamma := -f.d/pv;
      p := f.R_s*f.d*f.T*f.delta*f.fdelta;
      // calculating cp near the critical point may be troublesome (cp -> inf).
      pro.cp := f.R_s*(-f.tau*f.tau*f.ftautau + (f.delta*f.fdelta - f.delta*f.tau
        *f.fdeltatau)^2/(2*f.delta*f.fdelta + f.delta*f.delta*f.fdeltadelta));
      pro.cv := f.R_s*(-f.tau*f.tau*f.ftautau);
      pro.kappa := 1/(f.d*f.R_s*f.d*f.T*f.delta*f.fdelta)*((-pv*pro.cv + pt*pt*f.T)
        /(pro.cv));
      pro.a := sqrt(abs(f.R_s*f.T*(2*f.delta*f.fdelta + f.delta*f.delta*f.fdeltadelta
         - ((f.delta*f.fdelta - f.delta*f.tau*f.fdeltatau)*(f.delta*f.fdelta -
        f.delta*f.tau*f.fdeltatau))/(f.tau*f.tau*f.ftautau))));
      pro.ddTp := -pt/pd;
      pro.ddpT := 1/pd;
      //problem with units in last two lines
      pro.dupT := gamma*p/f.d - alpha*f.T/f.d;
      pro.duTp := pro.cp - alpha*p/f.d;
    end helmholtzToProps_pT;

    function helmholtzToProps_dT
      "Calculate property record for density and temperature as states from dimensionless Helmholtz function"

      extends Modelica.Icons.Function;
      input HelmholtzDerivs f "Dimensionless derivatives of Helmholtz function";
      output ThermoProperties_dT pro
        "Property record for density and temperature as dynamic states";
    protected
      DerPressureByTemperature pt "Derivative of pressure w.r.t. temperature";
      DerPressureBySpecificVolume pv "Derivative of pressure w.r.t. pressure";
    algorithm
      pro.p := f.R_s*f.d*f.T*f.delta*f.fdelta;
      pro.R_s := f.R_s;
      pro.s := f.R_s*(f.tau*f.ftau - f.f);
      pro.h := f.R_s*f.T*(f.tau*f.ftau + f.delta*f.fdelta);
      pro.u := f.R_s*f.T*f.tau*f.ftau;
      pv := -(f.d*f.d)*f.R_s*f.T*f.delta*(2.0*f.fdelta + f.delta*f.fdeltadelta);
      pt := f.R_s*f.d*f.delta*(f.fdelta - f.tau*f.fdeltatau);

      // calculating cp near the critical point may be troublesome (cp -> inf).
      pro.cp := f.R_s*(-f.tau*f.tau*f.ftautau + (f.delta*f.fdelta - f.delta*f.tau
        *f.fdeltatau)^2/(2*f.delta*f.fdelta + f.delta*f.delta*f.fdeltadelta));
      pro.cv := f.R_s*(-f.tau*f.tau*f.ftautau);
      pro.kappa := 1/(f.d*pro.p)*((-pv*pro.cv + pt*pt*f.T)/(pro.cv));
      pro.a := sqrt(abs(f.R_s*f.T*(2*f.delta*f.fdelta + f.delta*f.delta*f.fdeltadelta
         - ((f.delta*f.fdelta - f.delta*f.tau*f.fdeltatau)*(f.delta*f.fdelta -
        f.delta*f.tau*f.fdeltatau))/(f.tau*f.tau*f.ftautau))));
      pro.dudT := (pro.p - f.T*pt)/(f.d*f.d);
    end helmholtzToProps_dT;

    function TwoPhaseToProps_ph
      "Compute property record for pressure and specific enthalpy as states from saturation properties"

      extends Modelica.Icons.Function;
      input SaturationProperties sat "Saturation property record";
      output ThermoProperties_ph pro
        "Property record for pressure and specific enthalpy as dynamic states";
    protected
      Real dht(unit="(J/kg)/K")
        "Derivative of specific enthalpy w.r.t. temperature";
      Real dhd(unit="(J/kg)/(kg/m3)")
        "Derivative of specific enthalpy w.r.t. density";
      Real detph(unit="m4.s4/(K.s8)") "Thermodynamic determinant";
    algorithm
      pro.d := sat.d;
      pro.T := sat.T;
      pro.u := sat.u;
      pro.s := sat.s;
      pro.cv := sat.cv;
      pro.R_s := sat.R_s;
      pro.cp := Modelica.Constants.inf;
      pro.kappa := -1/(sat.d*sat.p)*sat.dpT*sat.dpT*sat.T/sat.cv;
      pro.a := Modelica.Constants.inf;
      dht := sat.cv + sat.dpT/sat.d;
      dhd := -sat.T*sat.dpT/(sat.d*sat.d);
      detph := -sat.dpT*dhd;
      pro.ddph := dht/detph;
      pro.ddhp := -sat.dpT/detph;
    end TwoPhaseToProps_ph;

    function TwoPhaseToProps_dT
      "Compute property record for density and temperature as states from saturation properties"

      extends Modelica.Icons.Function;
      input SaturationProperties sat "Saturation properties";
      output ThermoProperties_dT pro
        "Property record for density and temperature as dynamic states";
    algorithm
      pro.p := sat.p;
      pro.h := sat.h;
      pro.u := sat.u;
      pro.s := sat.s;
      pro.cv := sat.cv;
      pro.cp := Modelica.Constants.inf;
      pro.R_s := sat.R_s;
      pro.kappa := -1/(sat.d*sat.p)*sat.dpT*sat.dpT*sat.T/sat.cv;
      pro.a := Modelica.Constants.inf;
      pro.dudT := (sat.p - sat.T*sat.dpT)/(sat.d*sat.d);
    end TwoPhaseToProps_dT;

  end ThermoFluidSpecial;

public
  record SaturationProperties "Properties in the two phase region"
    extends Modelica.Icons.Record;
    SI.Temperature T "Temperature";
    SI.Density d "Density";
    SI.Pressure p "Pressure";
    SI.SpecificEnergy u "Specific inner energy";
    SI.SpecificEnthalpy h "Specific enthalpy";
    SI.SpecificEntropy s "Specific entropy";
    SI.SpecificHeatCapacity cp "Heat capacity at constant pressure";
    SI.SpecificHeatCapacity cv "Heat capacity at constant volume";
    SI.SpecificHeatCapacity R_s "Gas constant";
    SI.RatioOfSpecificHeatCapacities kappa "Isentropic expansion coefficient";
    PhaseBoundaryProperties liq
      "Thermodynamic base properties on the boiling curve";
    PhaseBoundaryProperties vap
      "Thermodynamic base properties on the dew curve";
    Real dpT(unit="Pa/K")
      "Derivative of saturation pressure w.r.t. temperature";
    SI.MassFraction x "Vapour mass fraction";
  end SaturationProperties;

  record SaturationBoundaryProperties
    "Properties on both phase boundaries, including some derivatives"

    extends Modelica.Icons.Record;
    SI.Temperature T "Saturation temperature";
    SI.Density dl "Liquid density";
    SI.Density dv "Vapour density";
    SI.SpecificEnthalpy hl "Liquid specific enthalpy";
    SI.SpecificEnthalpy hv "Vapour specific enthalpy";
    Real dTp "Derivative of temperature w.r.t. saturation pressure";
    Real ddldp "Derivative of density along boiling curve";
    Real ddvdp "Derivative of density along dew curve";
    Real dhldp "Derivative of specific enthalpy along boiling curve";
    Real dhvdp "Derivative of specific enthalpy along dew curve";
    SI.MassFraction x "Vapour mass fraction";
  end SaturationBoundaryProperties;

  record IF97BaseTwoPhase "Intermediate property data record for IF 97"
    extends Modelica.Icons.Record;
    Integer phase(start=0)
      "Phase: 2 for two-phase, 1 for one phase, 0 if unknown";
    Integer region(min=1, max=5) "IF 97 region";
    SI.Pressure p "Pressure";
    SI.Temperature T "Temperature";
    SI.SpecificEnthalpy h "Specific enthalpy";
    SI.SpecificHeatCapacity R_s "Gas constant";
    SI.SpecificHeatCapacity cp "Specific heat capacity";
    SI.SpecificHeatCapacity cv "Specific heat capacity";
    SI.Density rho "Density";
    SI.SpecificEntropy s "Specific entropy";
    DerPressureByTemperature pt "Derivative of pressure w.r.t. temperature";
    DerPressureByDensity pd "Derivative of pressure w.r.t. density";
    Real vt "Derivative of specific volume w.r.t. temperature";
    Real vp "Derivative of specific volume w.r.t. pressure";
    Real x "Dryness fraction";
    Real dpT "dp/dT derivative of saturation curve";
  end IF97BaseTwoPhase;

  record IF97PhaseBoundaryProperties
    "Thermodynamic base properties on the phase boundary for IF97 steam tables"

    extends Modelica.Icons.Record;
    Boolean region3boundary "= true, if boundary between 2-phase and region 3";
    SI.SpecificHeatCapacity R_s "Specific heat capacity";
    SI.Temperature T "Temperature";
    SI.Density d "Density";
    SI.SpecificEnthalpy h "Specific enthalpy";
    SI.SpecificEntropy s "Specific entropy";
    SI.SpecificHeatCapacity cp "Heat capacity at constant pressure";
    SI.SpecificHeatCapacity cv "Heat capacity at constant volume";
    DerPressureByTemperature dpT "dp/dT derivative of saturation curve";
    DerPressureByTemperature pt "Derivative of pressure w.r.t. temperature";
    DerPressureByDensity pd "Derivative of pressure w.r.t. density";
    Real vt(unit="m3/(kg.K)")
      "Derivative of specific volume w.r.t. temperature";
    Real vp(unit="m3/(kg.Pa)") "Derivative of specific volume w.r.t. pressure";
  end IF97PhaseBoundaryProperties;

  record GibbsDerivs
    "Derivatives of dimensionless Gibbs-function w.r.t. dimensionless pressure and temperature"

    extends Modelica.Icons.Record;
    SI.Pressure p "Pressure";
    SI.Temperature T "Temperature";
    SI.SpecificHeatCapacity R_s "Specific heat capacity";
    Real pi(unit="1") "Dimensionless pressure";
    Real tau(unit="1") "Dimensionless temperature";
    Real g(unit="1") "Dimensionless Gibbs-function";
    Real gpi(unit="1") "Derivative of g w.r.t. pi";
    Real gpipi(unit="1") "2nd derivative of g w.r.t. pi";
    Real gtau(unit="1") "Derivative of g w.r.t. tau";
    Real gtautau(unit="1") "2nd derivative of g w.r.t. tau";
    Real gtaupi(unit="1") "Mixed derivative of g w.r.t. pi and tau";
  end GibbsDerivs;

  record HelmholtzDerivs
    "Derivatives of dimensionless Helmholtz-function w.r.t. dimensionless pressure, density and temperature"
    extends Modelica.Icons.Record;
    SI.Density d "Density";
    SI.Temperature T "Temperature";
    SI.SpecificHeatCapacity R_s "Specific heat capacity";
    Real delta(unit="1") "Dimensionless density";
    Real tau(unit="1") "Dimensionless temperature";
    Real f(unit="1") "Dimensionless Helmholtz-function";
    Real fdelta(unit="1") "Derivative of f w.r.t. delta";
    Real fdeltadelta(unit="1") "2nd derivative of f w.r.t. delta";
    Real ftau(unit="1") "Derivative of f w.r.t. tau";
    Real ftautau(unit="1") "2nd derivative of f w.r.t. tau";
    Real fdeltatau(unit="1") "Mixed derivative of f w.r.t. delta and tau";
  end HelmholtzDerivs;

  record TwoPhaseTransportProps
    "Defines properties on both phase boundaries, needed in the two phase region"
    extends Modelica.Icons.Record;
    SI.Density d_vap "Density on the dew line";
    SI.Density d_liq "Density on the bubble line";
    SI.DynamicViscosity eta_vap "Dynamic viscosity on the dew line";
    SI.DynamicViscosity eta_liq "Dynamic viscosity on the bubble line";
    SI.ThermalConductivity lam_vap "Thermal conductivity on the dew line";
    SI.ThermalConductivity lam_liq "Thermal conductivity on the bubble line";
    SI.SpecificHeatCapacity cp_vap "Cp on the dew line";
    SI.SpecificHeatCapacity cp_liq "Cp on the bubble line";
    SI.MassFraction x "Steam quality";
  end TwoPhaseTransportProps;

  record PhaseBoundaryProperties
    "Thermodynamic base properties on the phase boundary"
    extends Modelica.Icons.Record;
    SI.Density d "Density";
    SI.SpecificEnthalpy h "Specific enthalpy";
    SI.SpecificEnergy u "Inner energy";
    SI.SpecificEntropy s "Specific entropy";
    SI.SpecificHeatCapacity cp "Heat capacity at constant pressure";
    SI.SpecificHeatCapacity cv "Heat capacity at constant volume";
    DerPressureByTemperature pt "Derivative of pressure w.r.t. temperature";
    DerPressureByDensity pd "Derivative of pressure w.r.t. density";
  end PhaseBoundaryProperties;

  record NewtonDerivatives_ph
    "Derivatives for fast inverse calculations of Helmholtz functions: p & h"

    extends Modelica.Icons.Record;
    SI.Pressure p "Pressure";
    SI.SpecificEnthalpy h "Specific enthalpy";
    DerPressureByDensity pd "Derivative of pressure w.r.t. density";
    DerPressureByTemperature pt "Derivative of pressure w.r.t. temperature";
    Real hd "Derivative of specific enthalpy w.r.t. density";
    Real ht "Derivative of specific enthalpy w.r.t. temperature";
  end NewtonDerivatives_ph;

  record NewtonDerivatives_ps
    "Derivatives for fast inverse calculation of Helmholtz functions: p & s"

    extends Modelica.Icons.Record;
    SI.Pressure p "Pressure";
    SI.SpecificEntropy s "Specific entropy";
    DerPressureByDensity pd "Derivative of pressure w.r.t. density";
    DerPressureByTemperature pt "Derivative of pressure w.r.t. temperature";
    Real sd "Derivative of specific entropy w.r.t. density";
    Real st "Derivative of specific entropy w.r.t. temperature";
  end NewtonDerivatives_ps;

  record NewtonDerivatives_pT
    "Derivatives for fast inverse calculations of Helmholtz functions:p & T"

    extends Modelica.Icons.Record;
    SI.Pressure p "Pressure";
    DerPressureByDensity pd "Derivative of pressure w.r.t. density";
  end NewtonDerivatives_pT;

  record ExtraDerivatives "Additional thermodynamic derivatives"
    extends Modelica.Icons.Record;
    IsentropicExponent kappa "Isentropic expansion coefficient";
    // k in Bejan
    IsenthalpicExponent theta "Isenthalpic exponent";
    // same as kappa, except derivative at constant h
    IsobaricVolumeExpansionCoefficient alpha
      "Isobaric volume expansion coefficient";
    // beta in Bejan
    IsochoricPressureCoefficient beta "Isochoric pressure coefficient";
    // kT in Bejan
    IsothermalCompressibility gamma "Isothermal compressibility";
    // kappa in Bejan
    JouleThomsonCoefficient mu "Joule-Thomson coefficient";
    // mu_J in Bejan
  end ExtraDerivatives;

  record BridgmansTables
    "Calculates all entries in Bridgmans tables if first seven variables given"
    extends Modelica.Icons.Record;
    // the first 7 need to calculated in a function!
    SI.SpecificVolume v "Specific volume";
    SI.Pressure p "Pressure";
    SI.Temperature T "Temperature";
    SI.SpecificEntropy s "Specific entropy";
    SI.SpecificHeatCapacity cp "Heat capacity at constant pressure";
    IsobaricVolumeExpansionCoefficient alpha
      "Isobaric volume expansion coefficient";
    // beta in Bejan
    IsothermalCompressibility gamma "Isothermal compressibility";
    // kappa in Bejan
    // Derivatives at constant pressure
    Real dTp=1 "Coefficient in Bridgmans table, see info for usage";
    Real dpT=-dTp "Coefficient in Bridgmans table, see info for usage";
    Real dvp=alpha*v "Coefficient in Bridgmans table, see info for usage";
    Real dpv=-dvp "Coefficient in Bridgmans table, see info for usage";
    Real dsp=cp/T "Coefficient in Bridgmans table, see info for usage";
    Real dps=-dsp "Coefficient in Bridgmans table, see info for usage";
    Real dup=cp - alpha*p*v
      "Coefficient in Bridgmans table, see info for usage";
    Real dpu=-dup "Coefficient in Bridgmans table, see info for usage";
    Real dhp=cp "Coefficient in Bridgmans table, see info for usage";
    Real dph=-dhp "Coefficient in Bridgmans table, see info for usage";
    Real dfp=-s - alpha*p*v
      "Coefficient in Bridgmans table, see info for usage";
    Real dpf=-dfp "Coefficient in Bridgmans table, see info for usage";
    Real dgp=-s "Coefficient in Bridgmans table, see info for usage";
    Real dpg=-dgp "Coefficient in Bridgmans table, see info for usage";
    // Derivatives at constant Temperature
    Real dvT=gamma*v "Coefficient in Bridgmans table, see info for usage";
    Real dTv=-dvT "Coefficient in Bridgmans table, see info for usage";
    Real dsT=alpha*v "Coefficient in Bridgmans table, see info for usage";
    Real dTs=-dsT "Coefficient in Bridgmans table, see info for usage";
    Real duT=alpha*T*v - gamma*p*v
      "Coefficient in Bridgmans table, see info for usage";
    Real dTu=-duT "Coefficient in Bridgmans table, see info for usage";
    Real dhT=-v + alpha*T*v
      "Coefficient in Bridgmans table, see info for usage";
    Real dTh=-dhT "Coefficient in Bridgmans table, see info for usage";
    Real dfT=-gamma*p*v "Coefficient in Bridgmans table, see info for usage";
    Real dTf=-dfT "Coefficient in Bridgmans table, see info for usage";
    Real dgT=-v "Coefficient in Bridgmans table, see info for usage";
    Real dTg=-dgT "Coefficient in Bridgmans table, see info for usage";
    // Derivatives at constant v
    Real dsv=alpha*alpha*v*v - gamma*v*cp/T
      "Coefficient in Bridgmans table, see info for usage";
    Real dvs=-dsv "Coefficient in Bridgmans table, see info for usage";
    Real duv=T*alpha*alpha*v*v - gamma*v*cp
      "Coefficient in Bridgmans table, see info for usage";
    Real dvu=-duv "Coefficient in Bridgmans table, see info for usage";
    Real dhv=T*alpha*alpha*v*v - alpha*v*v - gamma*v*cp
      "Coefficient in Bridgmans table, see info for usage";
    Real dvh=-dhv "Coefficient in Bridgmans table, see info for usage";
    Real dfv=gamma*v*s "Coefficient in Bridgmans table, see info for usage";
    Real dvf=-dfv "Coefficient in Bridgmans table, see info for usage";
    Real dgv=gamma*v*s - alpha*v*v
      "Coefficient in Bridgmans table, see info for usage";
    Real dvg=-dgv "Coefficient in Bridgmans table, see info for usage";
    // Derivatives at constant s
    Real dus=dsv*p "Coefficient in Bridgmans table, see info for usage";
    Real dsu=-dus "Coefficient in Bridgmans table, see info for usage";
    Real dhs=-v*cp/T "Coefficient in Bridgmans table, see info for usage";
    Real dsh=-dhs "Coefficient in Bridgmans table, see info for usage";
    Real dfs=alpha*v*s + dus
      "Coefficient in Bridgmans table, see info for usage";
    Real dsf=-dfs "Coefficient in Bridgmans table, see info for usage";
    Real dgs=alpha*v*s - v*cp/T
      "Coefficient in Bridgmans table, see info for usage";
    Real dsg=-dgs "Coefficient in Bridgmans table, see info for usage";
    // Derivatives at constant u
    Real dhu=p*alpha*v*v + gamma*v*cp*p - v*cp - p*T*alpha*alpha*v*v
      "Coefficient in Bridgmans table, see info for usage";
    Real duh=-dhu "Coefficient in Bridgmans table, see info for usage";
    Real dfu=s*T*alpha*v - gamma*v*cp*p - gamma*v*s*p + p*T*alpha*alpha*v*v
      "Coefficient in Bridgmans table, see info for usage";
    Real duf=-dfu "Coefficient in Bridgmans table, see info for usage";
    Real dgu=alpha*v*v*p + alpha*v*s*T - v*cp - gamma*v*s*p
      "Coefficient in Bridgmans table, see info for usage";
    Real dug=-dgu "Coefficient in Bridgmans table, see info for usage";
    //  Derivatives at constant h
    Real dfh=(s - v*alpha*p)*(v - v*alpha*T) - gamma*v*cp*p
      "Coefficient in Bridgmans table, see info for usage";
    Real dhf=-dfh "Coefficient in Bridgmans table, see info for usage";
    Real dgh=alpha*v*s*T - v*(s + cp)
      "Coefficient in Bridgmans table, see info for usage";
    Real dhg=-dgh "Coefficient in Bridgmans table, see info for usage";
    // Derivatives at constant g
    Real dfg=gamma*v*s*p - v*s - alpha*v*v*p
      "Coefficient in Bridgmans table, see info for usage";
    Real dgf=-dfg "Coefficient in Bridgmans table, see info for usage";
bt.p = p[1];
bt.T = T[1];
bt.v = 1/pro[1].d;
bt.s = pro[1].s;
bt.cp = pro[1].cp;
bt.alpha = dpro.alpha;
bt.gamma = dpro.gamma;
derOfsByTAtConstantv =  bt.dsv/bt.dTv;
                ...
end BridgmansTablesForWater;

                "));
  end BridgmansTables;

  record FundamentalConstants "Constants of the medium"
    extends Modelica.Icons.Record;
    SI.MolarHeatCapacity R_bar;
    SI.SpecificHeatCapacity R_s;
    SI.MolarMass MM;
    SI.MolarDensity rhored;
    SI.Temperature Tred;
    SI.AbsolutePressure pred;
    SI.SpecificEnthalpy h_off;
    SI.SpecificEntropy s_off;
  end FundamentalConstants;

  record AuxiliaryProperties "Intermediate property data record"
    extends Modelica.Icons.Record;
    SI.Pressure p "Pressure";
    SI.Temperature T "Temperature";
    SI.SpecificEnthalpy h "Specific enthalpy";
    SI.SpecificHeatCapacity R_s "Gas constant";
    SI.SpecificHeatCapacity cp "Specific heat capacity";
    SI.SpecificHeatCapacity cv "Specific heat capacity";
    SI.Density rho "Density";
    SI.SpecificEntropy s "Specific entropy";
    SI.DerPressureByTemperature pt "Derivative of pressure w.r.t. temperature";
    SI.DerPressureByDensity pd "Derivative of pressure w.r.t. density";
    Real vt "Derivative of specific volume w.r.t. temperature";
    Real vp "Derivative of specific volume w.r.t. pressure";
  end AuxiliaryProperties;

  record GibbsDerivs2
    "Derivatives of Gibbs function w.r.t. pressure and temperature"

    extends Modelica.Icons.Record;
    SI.Pressure p "Pressure";
    SI.Temperature T "Temperature";
    SI.SpecificHeatCapacity R_s "Specific heat capacity";
    Real pi(unit="1") "Dimensionless pressure";
    Real theta(unit="1") "Dimensionless temperature";
    Real g(unit="J/kg") "Gibbs function";
    Real gp(unit="m3/kg") "Derivative of g w.r.t. p";
    Real gpp(unit="m3/(kg.Pa)") "2nd derivative of g w.r.t. p";
    Real gT(unit="J/(kg.K)") "Derivative of g w.r.t. T";
    Real gTT(unit="J/(kg.K2)") "2nd derivative of g w.r.t. T";
    Real gTp(unit="m3/(kg.K)") "Mixed derivative of g w.r.t. T and p";
  end GibbsDerivs2;

  record NewtonDerivatives_dT
    "Derivatives for fast inverse calculations of Gibbs function"
    extends Modelica.Icons.Record;
    SI.SpecificVolume v "Specific volume";
    Real vp "Derivative of specific volume w.r.t. pressure";
  end NewtonDerivatives_dT;

  function gibbsToBridgmansTables
    "Calculates base coefficients for Bridgman's tables from gibbs enthalpy"

    extends Modelica.Icons.Function;
    input GibbsDerivs g "Dimensionless derivatives of Gibbs function";
    output SI.SpecificVolume v "Specific volume";
    output SI.Pressure p=g.p "Pressure";
    output SI.Temperature T=g.T "Temperature";
    output SI.SpecificEntropy s "Specific entropy";
    output SI.SpecificHeatCapacity cp "Heat capacity at constant pressure";
    output IsobaricVolumeExpansionCoefficient alpha
      "Isobaric volume expansion coefficient";
    // beta in Bejan
    output IsothermalCompressibility gamma "Isothermal compressibility";
    // kappa in Bejan
  protected
    Real vt(unit="m3/(kg.K)")
      "Derivative of specific volume w.r.t. temperature";
    Real vp(unit="m4.kg-2.s2") "Derivative of specific volume w.r.t. pressure";
  algorithm
    vt := g.R_s/g.p*(g.pi*g.gpi - g.tau*g.pi*g.gtaupi);
    vp := g.R_s*g.T/(g.p*g.p)*g.pi*g.pi*g.gpipi;
    v := (g.R_s*g.T*g.pi*g.gpi)/g.p;
    s := g.R_s*(g.tau*g.gtau - g.g);
    cp := -g.R_s*g.tau*g.tau*g.gtautau;
    alpha := vt/v;
    gamma := -vp/v;
  end gibbsToBridgmansTables;

  function helmholtzToBridgmansTables
    "Calculates base coefficients for Bridgmans tables from Helmholtz energy"
    extends Modelica.Icons.Function;
    input HelmholtzDerivs f "Dimensionless derivatives of Helmholtz function";
    output SI.SpecificVolume v=1/f.d "Specific volume";
    output SI.Pressure p "Pressure";
    output SI.Temperature T=f.T "Temperature";
    output SI.SpecificEntropy s "Specific entropy";
    output SI.SpecificHeatCapacity cp "Heat capacity at constant pressure";
    output IsobaricVolumeExpansionCoefficient alpha
      "Isobaric volume expansion coefficient";
    // beta in Bejan
    output IsothermalCompressibility gamma "Isothermal compressibility";
    // kappa in Bejan
  protected
    DerPressureByTemperature pt "Derivative of pressure w.r.t. temperature";
    DerPressureBySpecificVolume pv
      "Derivative of pressure w.r.t. specific volume";
    SI.SpecificHeatCapacity cv "Isochoric specific heat capacity";
  algorithm
    p := f.R_s*f.d*f.T*f.delta*f.fdelta;
    pv := -(f.d*f.d)*f.R_s*f.T*f.delta*(2.0*f.fdelta + f.delta*f.fdeltadelta);
    pt := f.R_s*f.d*f.delta*(f.fdelta - f.tau*f.fdeltatau);
    s := f.R_s*(f.tau*f.ftau - f.f);
    alpha := -f.d*pt/pv;
    gamma := -f.d/pv;
    cp := f.R_s*(-f.tau*f.tau*f.ftautau + (f.delta*f.fdelta - f.delta*f.tau*f.fdeltatau)
      ^2/(2*f.delta*f.fdelta + f.delta*f.delta*f.fdeltadelta));
  end helmholtzToBridgmansTables;

  function gibbsToBoundaryProps
    "Calculate phase boundary property record from dimensionless Gibbs function"

    extends Modelica.Icons.Function;
    input GibbsDerivs g "Dimensionless derivatives of Gibbs function";
    output PhaseBoundaryProperties sat "Phase boundary properties";
  protected
    Real vt "Derivative of specific volume w.r.t. temperature";
    Real vp "Derivative of specific volume w.r.t. pressure";
  algorithm
    sat.d := g.p/(g.R_s*g.T*g.pi*g.gpi);
    sat.h := g.R_s*g.T*g.tau*g.gtau;
    sat.u := g.T*g.R_s*(g.tau*g.gtau - g.pi*g.gpi);
    sat.s := g.R_s*(g.tau*g.gtau - g.g);
    sat.cp := -g.R_s*g.tau*g.tau*g.gtautau;
    sat.cv := g.R_s*(-g.tau*g.tau*g.gtautau + (g.gpi - g.tau*g.gtaupi)*(g.gpi - g.tau
      *g.gtaupi)/(g.gpipi));
    vt := g.R_s/g.p*(g.pi*g.gpi - g.tau*g.pi*g.gtaupi);
    vp := g.R_s*g.T/(g.p*g.p)*g.pi*g.pi*g.gpipi;
    // sat.kappa := -1/(sat.d*g.p)*sat.cp/(vp*sat.cp + vt*vt*g.T);
    sat.pt := -g.p/g.T*(g.gpi - g.tau*g.gtaupi)/(g.gpipi*g.pi);
    sat.pd := -g.R_s*g.T*g.gpi*g.gpi/(g.gpipi);
  end gibbsToBoundaryProps;

  function helmholtzToBoundaryProps
    "Calculate phase boundary property record from dimensionless Helmholtz function"

    extends Modelica.Icons.Function;
    input HelmholtzDerivs f "Dimensionless derivatives of Helmholtz function";
    output PhaseBoundaryProperties sat "Phase boundary property record";
  protected
    SI.Pressure p "Pressure";
  algorithm
    p := f.R_s*f.d*f.T*f.delta*f.fdelta;
    sat.d := f.d;
    sat.h := f.R_s*f.T*(f.tau*f.ftau + f.delta*f.fdelta);
    sat.s := f.R_s*(f.tau*f.ftau - f.f);
    sat.u := f.R_s*f.T*f.tau*f.ftau;
    sat.cp := f.R_s*(-f.tau*f.tau*f.ftautau + (f.delta*f.fdelta - f.delta*f.tau*f.fdeltatau)
      ^2/(2*f.delta*f.fdelta + f.delta*f.delta*f.fdeltadelta));
    sat.cv := f.R_s*(-f.tau*f.tau*f.ftautau);
    sat.pt := f.R_s*f.d*f.delta*(f.fdelta - f.tau*f.fdeltatau);
    sat.pd := f.R_s*f.T*f.delta*(2.0*f.fdelta + f.delta*f.fdeltadelta);
  end helmholtzToBoundaryProps;

  function cv2Phase
    "Compute isochoric specific heat capacity inside the two-phase region"

    extends Modelica.Icons.Function;
    input PhaseBoundaryProperties liq "Properties on the boiling curve";
    input PhaseBoundaryProperties vap "Properties on the condensation curve";
    input SI.MassFraction x "Vapour mass fraction";
    input SI.Temperature T "Temperature";
    input SI.Pressure p "Properties";
    output SI.SpecificHeatCapacity cv "Isochoric specific heat capacity";
  protected
    Real dpT "Derivative of pressure w.r.t. temperature";
    Real dxv "Derivative of vapour mass fraction w.r.t. specific volume";
    Real dvTl "Derivative of liquid specific volume w.r.t. temperature";
    Real dvTv "Derivative of vapour specific volume w.r.t. temperature";
    Real duTl "Derivative of liquid specific inner energy w.r.t. temperature";
    Real duTv "Derivative of vapour specific inner energy w.r.t. temperature";
    Real dxt "Derivative of vapour mass fraction w.r.t. temperature";
  algorithm
    dxv := if (liq.d <> vap.d) then liq.d*vap.d/(liq.d - vap.d) else 0.0;
    dpT := (vap.s - liq.s)*dxv;
    // wrong at critical point
    dvTl := (liq.pt - dpT)/liq.pd/liq.d/liq.d;
    dvTv := (vap.pt - dpT)/vap.pd/vap.d/vap.d;
    dxt := -dxv*(dvTl + x*(dvTv - dvTl));
    duTl := liq.cv + (T*liq.pt - p)*dvTl;
    duTv := vap.cv + (T*vap.pt - p)*dvTv;
    cv := duTl + x*(duTv - duTl) + dxt*(vap.u - liq.u);
  end cv2Phase;

  function cvdpT2Phase
    "Compute isochoric specific heat capacity inside the two-phase region and derivative of pressure w.r.t. temperature"

    extends Modelica.Icons.Function;
    input PhaseBoundaryProperties liq "Properties on the boiling curve";
    input PhaseBoundaryProperties vap "Properties on the condensation curve";
    input SI.MassFraction x "Vapour mass fraction";
    input SI.Temperature T "Temperature";
    input SI.Pressure p "Properties";
    output SI.SpecificHeatCapacity cv "Isochoric specific heat capacity";
    output Real dpT "Derivative of pressure w.r.t. temperature";
  protected
    Real dxv "Derivative of vapour mass fraction w.r.t. specific volume";
    Real dvTl "Derivative of liquid specific volume w.r.t. temperature";
    Real dvTv "Derivative of vapour specific volume w.r.t. temperature";
    Real duTl "Derivative of liquid specific inner energy w.r.t. temperature";
    Real duTv "Derivative of vapour specific inner energy w.r.t. temperature";
    Real dxt "Derivative of vapour mass fraction w.r.t. temperature";
  algorithm
    dxv := if (liq.d <> vap.d) then liq.d*vap.d/(liq.d - vap.d) else 0.0;
    dpT := (vap.s - liq.s)*dxv;
    // wrong at critical point
    dvTl := (liq.pt - dpT)/liq.pd/liq.d/liq.d;
    dvTv := (vap.pt - dpT)/vap.pd/vap.d/vap.d;
    dxt := -dxv*(dvTl + x*(dvTv - dvTl));
    duTl := liq.cv + (T*liq.pt - p)*dvTl;
    duTv := vap.cv + (T*vap.pt - p)*dvTv;
    cv := duTl + x*(duTv - duTl) + dxt*(vap.u - liq.u);
  end cvdpT2Phase;

  function gibbsToExtraDerivs
    "Compute additional thermodynamic derivatives from dimensionless Gibbs function"

    extends Modelica.Icons.Function;
    input GibbsDerivs g "Dimensionless derivatives of Gibbs function";
    output ExtraDerivatives dpro "Additional property derivatives";
  protected
    Real vt "Derivative of specific volume w.r.t. temperature";
    Real vp "Derivative of specific volume w.r.t. pressure";
    SI.Density d "Density";
    SI.SpecificVolume v "Specific volume";
    SI.SpecificHeatCapacity cv "Isochoric heat capacity";
    SI.SpecificHeatCapacity cp "Isobaric heat capacity";
  algorithm
    d := g.p/(g.R_s*g.T*g.pi*g.gpi);
    v := 1/d;
    vt := g.R_s/g.p*(g.pi*g.gpi - g.tau*g.pi*g.gtaupi);
    vp := g.R_s*g.T/(g.p*g.p)*g.pi*g.pi*g.gpipi;
    cp := -g.R_s*g.tau*g.tau*g.gtautau;
    cv := g.R_s*(-g.tau*g.tau*g.gtautau + (g.gpi - g.tau*g.gtaupi)*(g.gpi - g.tau
      *g.gtaupi)/g.gpipi);
    dpro.kappa := -1/(d*g.p)*cp/(vp*cp + vt*vt*g.T);
    dpro.theta := cp/(d*g.p*(-vp*cp + vt*v - g.T*vt*vt));
    dpro.alpha := d*vt;
    dpro.beta := -vt/(g.p*vp);
    dpro.gamma := -d*vp;
    dpro.mu := -(v - g.T*vt)/cp;
  end gibbsToExtraDerivs;

  function helmholtzToExtraDerivs
    "Compute additional thermodynamic derivatives from dimensionless Helmholtz function"

    extends Modelica.Icons.Function;
    input HelmholtzDerivs f "Dimensionless derivatives of Helmholtz function";
    output ExtraDerivatives dpro "Additional property derivatives";
  protected
    SI.Pressure p "Pressure";
    SI.SpecificVolume v "Specific volume";
    DerPressureByTemperature pt "Derivative of pressure w.r.t. temperature";
    DerPressureBySpecificVolume pv
      "Derivative of pressure w.r.t. specific volume";
    SI.SpecificHeatCapacity cv "Isochoric specific heat capacity";
  algorithm
    v := 1/f.d;
    p := f.R_s*f.d*f.T*f.delta*f.fdelta;
    pv := -(f.d*f.d)*f.R_s*f.T*f.delta*(2.0*f.fdelta + f.delta*f.fdeltadelta);
    pt := f.R_s*f.d*f.delta*(f.fdelta - f.tau*f.fdeltatau);
    cv := f.R_s*(-f.tau*f.tau*f.ftautau);
    dpro.kappa := 1/(f.d*p)*((-pv*cv + pt*pt*f.T)/(cv));
    dpro.theta := -1/(f.d*p)*((-pv*cv + f.T*pt*pt)/(cv + pt*v));
    dpro.alpha := -f.d*pt/pv;
    dpro.beta := pt/p;
    dpro.gamma := -f.d/pv;
    dpro.mu := (v*pv + f.T*pt)/(pt*pt*f.T - pv*cv);
  end helmholtzToExtraDerivs;

  function Helmholtz_ph
    "Function to calculate analytic derivatives for computing d and t given p and h"
    extends Modelica.Icons.Function;
    input HelmholtzDerivs f "Dimensionless derivatives of Helmholtz function";
    output NewtonDerivatives_ph nderivs
      "Derivatives for Newton iteration to calculate d and t from p and h";
  protected
    SI.SpecificHeatCapacity cv "Isochoric heat capacity";
  algorithm
    cv := -f.R_s*(f.tau*f.tau*f.ftautau);
    nderivs.p := f.d*f.R_s*f.T*f.delta*f.fdelta;
    nderivs.h := f.R_s*f.T*(f.tau*f.ftau + f.delta*f.fdelta);
    nderivs.pd := f.R_s*f.T*f.delta*(2.0*f.fdelta + f.delta*f.fdeltadelta);
    nderivs.pt := f.R_s*f.d*f.delta*(f.fdelta - f.tau*f.fdeltatau);
    nderivs.ht := cv + nderivs.pt/f.d;
    nderivs.hd := (nderivs.pd - f.T*nderivs.pt/f.d)/f.d;
  end Helmholtz_ph;

  function Helmholtz_pT
    "Function to calculate analytic derivatives for computing d and t given p and t"

    extends Modelica.Icons.Function;
    input HelmholtzDerivs f "Dimensionless derivatives of Helmholtz function";
    output NewtonDerivatives_pT nderivs
      "Derivatives for Newton iteration to compute d and t from p and t";
  algorithm
    nderivs.p := f.d*f.R_s*f.T*f.delta*f.fdelta;
    nderivs.pd := f.R_s*f.T*f.delta*(2.0*f.fdelta + f.delta*f.fdeltadelta);
  end Helmholtz_pT;

  function Helmholtz_ps
    "Function to calculate analytic derivatives for computing d and t given p and s"

    extends Modelica.Icons.Function;
    input HelmholtzDerivs f "Dimensionless derivatives of Helmholtz function";
    output NewtonDerivatives_ps nderivs
      "Derivatives for Newton iteration to compute d and t from p and s";
  protected
    SI.SpecificHeatCapacity cv "Isochoric heat capacity";
  algorithm
    cv := -f.R_s*(f.tau*f.tau*f.ftautau);
    nderivs.p := f.d*f.R_s*f.T*f.delta*f.fdelta;
    nderivs.s := f.R_s*(f.tau*f.ftau - f.f);
    nderivs.pd := f.R_s*f.T*f.delta*(2.0*f.fdelta + f.delta*f.fdeltadelta);
    nderivs.pt := f.R_s*f.d*f.delta*(f.fdelta - f.tau*f.fdeltatau);
    nderivs.st := cv/f.T;
    nderivs.sd := -nderivs.pt/(f.d*f.d);
  end Helmholtz_ps;

  function smoothStep
    "Approximation of a general step, such that the characteristic is continuous and differentiable"
    extends Modelica.Icons.Function;
    input Real x "Abscissa value";
    input Real y1 "Ordinate value for x > 0";
    input Real y2 "Ordinate value for x < 0";
    input Real x_small(min=0) = 1e-5
      "Approximation of step for -x_small <= x <= x_small; x_small > 0 required";
    output Real y "Ordinate value to approximate y = if x > 0 then y1 else y2";
  algorithm
    y := smooth(1, if x > x_small then y1 else if x < -x_small then y2 else if
      abs(x_small) > 0 then (x/x_small)*((x/x_small)^2 - 3)*(y2 - y1)/4 + (y1
       + y2)/2 else (y1 + y2)/2);

In the region -x_small < x < x_small a 2nd order polynomial is used
for a smooth transition from y1 to y2.

If mass fractions X[:] are approximated with this function then this can be performed
for all nX mass fractions, instead of applying it for nX-1 mass fractions and computing
the last one by the mass fraction constraint sum(X)=1. The reason is that the approximating function has the
property that sum(X) = 1, provided sum(X_a) = sum(X_b) = 1
(and y1=X_a[i], y2=X_b[i]).
This can be shown by evaluating the approximating function in the abs(x) < x_small
region (otherwise X is either X_a or X_b):

X[1]  = smoothStep(x, X_a[1] , X_b[1] , x_small);
X[2]  = smoothStep(x, X_a[2] , X_b[2] , x_small);
   ...
X[nX] = smoothStep(x, X_a[nX], X_b[nX], x_small);

or

X[1]  = c*(X_a[1]  - X_b[1])  + (X_a[1]  + X_b[1])/2
X[2]  = c*(X_a[2]  - X_b[2])  + (X_a[2]  + X_b[2])/2;
   ...
X[nX] = c*(X_a[nX] - X_b[nX]) + (X_a[nX] + X_b[nX])/2;
c     = (x/x_small)*((x/x_small)^2 - 3)/4

Summing all mass fractions together results in

sum(X) = c*(sum(X_a) - sum(X_b)) + (sum(X_a) + sum(X_b))/2
       = c*(1 - 1) + (1 + 1)/2
       = 1

"));
  end smoothStep;

  function Gibbs2_ph
    "Function to calculate analytic derivatives for computing T given p and h"
    extends Modelica.Icons.Function;
    input Modelica.Media.Common.GibbsDerivs2 g
      "Dimensionless derivatives of Gibbs function";
    output Modelica.Media.Common.NewtonDerivatives_ph nderivs
      "Derivatives for Newton iteration to calculate d and t from p and h";

  algorithm
    nderivs.h := g.g - g.T*g.gT;
    nderivs.ht := -g.T*g.gTT;

    //dummy values - DO NOT USE
    nderivs.p := 0.0;
    nderivs.pd := 0.0;
    nderivs.pt := 0.0;
    nderivs.hd := 0.0;
  end Gibbs2_ph;

  function Gibbs2_dT
    "Function to calculate analytic derivatives for computing p given d and T"
    extends Modelica.Icons.Function;
    input Modelica.Media.Common.GibbsDerivs2 g
      "Dimensionless derivatives of Gibbs function";
    output Modelica.Media.Common.NewtonDerivatives_dT nderivs
      "Derivatives for Newton iteration to compute p from d and T";

  algorithm
    nderivs.v := g.gp;
    nderivs.vp := nderivs.v*g.gpp/g.gp;
  end Gibbs2_dT;

  function Gibbs2_ps
    "Function to calculate analytic derivatives for computing d and t given p and s"

    extends Modelica.Icons.Function;
    input Modelica.Media.Common.GibbsDerivs2 g
      "Dimensionless derivatives of Gibbs function";
    output Modelica.Media.Common.NewtonDerivatives_ps nderivs
      "Derivatives for Newton iteration to compute T from p and s";

  algorithm
    nderivs.s := -g.gT;
    nderivs.st := -g.gTT;

    //dummy values - DO NOT USE
    nderivs.p := 0.0;
    nderivs.pd := 0.0;
    nderivs.pt := 0.0;
    nderivs.sd := 0.0;
  end Gibbs2_ps;
end Common;
end Media;