
:html_theme.sidebar_secondary.remove:

.. py:currentmodule:: cantera


.. DO NOT EDIT.
.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY.
.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE:
.. "examples/python/thermo/equations_of_state.py"
.. LINE NUMBERS ARE GIVEN BELOW.

.. only:: html

    .. note::
        :class: sphx-glr-download-link-note

        :ref:`Go to the end <sphx_glr_download_examples_python_thermo_equations_of_state.py>`
        to download the full example code.

.. rst-class:: sphx-glr-example-title

.. _sphx_glr_examples_python_thermo_equations_of_state.py:


Non-ideal equations of state
============================

This example demonstrates a comparison between ideal and non-ideal equations of
state (EoS) using Cantera and CoolProp.

The following equations of state are used to evaluate thermodynamic properties
in this example:

1. Ideal-gas EoS from Cantera
2. Non-ideal Redlich-Kwong EoS (R-K EoS) from Cantera
3. Helmholtz energy EoS (HEOS) from CoolProp

Requires: cantera >= 3.0.0, matplotlib >= 2.0, coolprop >= 6.0

.. tags:: Python, thermodynamics, non-ideal fluid

.. GENERATED FROM PYTHON SOURCE LINES 21-32

Import required packages (Cantera and CoolProp)
-----------------------------------------------

`CoolProp <http://coolprop.org>`__ [1]_ is an open-source package that contains a
highly-accurate database for thermophysical properties. The thermodynamic properties
are obtained using pure and pseudo-pure fluid equations of state implemented for 122
components.

If you are using ``conda`` to manage your software environment, activate your cantera
environment and install CoolProp by running ``conda install -c conda-forge coolprop``.


.. GENERATED FROM PYTHON SOURCE LINES 32-42

.. code-block:: Python


    import cantera as ct
    import numpy as np
    from CoolProp.CoolProp import PropsSI
    import matplotlib.pyplot as plt

    plt.rcParams['figure.constrained_layout.use'] = True

    print(f"Running Cantera version: {ct.__version__}")





.. rst-class:: sphx-glr-script-out

 .. code-block:: none

    Running Cantera version: 3.2.0




.. GENERATED FROM PYTHON SOURCE LINES 43-79

Helper functions
----------------

This example uses CO\ :math:`_2` as the only species. The function
``get_thermo_Cantera`` calculates thermodynamic properties based on the thermodynamic
state (:math:`T`, :math:`p`) of the species using Cantera. Applicable phases are
``Ideal-gas`` and ``Redlich-Kwong``. The ideal-gas equation can be stated as

.. math::
    pv = RT,

where :math:`p`, :math:`v` and :math:`T` represent thermodynamic pressure, molar
volume, and the temperature of the gas-phase. :math:`R` is the universal gas constant.
The Redlich-Kwong equation is a cubic, non-ideal equation of state, represented as

.. math::
    p=\frac{RT}{v-b^\ast}-\frac{a^\ast}{v\sqrt{T}(v+b^\ast)}.

In this expression, :math:`R` is the universal gas constant and :math:`v` is the molar
volume. The temperature-dependent van der Waals attraction parameter :math:`a^\ast`
and volume correction parameter (repulsive parameter) :math:`b^\ast` represent
molecular interactions.

The function ``get_thermo_CoolProp`` utilizes the CoolProp package to evaluate
thermodynamic properties based on the thermodynamic state (:math:`T`, :math:`p`) for a
given fluid. The HEOS for CO\ :math:`_2` used in this example is obtained from
http://www.coolprop.org/fluid_properties/fluids/CarbonDioxide.html.

Since the standard-reference thermodynamic states are different for Cantera and
CoolProp, it is necessary to convert these values to an appropriate scale before
comparison. Therefore, both functions ``get_thermo_Cantera`` and
``get_thermo_CoolProp`` return the thermodynamic values relative to a reference state
at 1 bar, 300 K.

To plot the comparison of thermodynamic properties among the three EoS, the ``plot``
function is used.

.. GENERATED FROM PYTHON SOURCE LINES 79-140

.. code-block:: Python


    def get_thermo_Cantera(phase, T, p):
        states = ct.SolutionArray(phase, len(p))
        X = "CO2:1.0"
        states.TPX = T, p, X

        u = states.u / 1000
        h = states.h / 1000
        s = states.s / 1000
        cp = states.cp / 1000
        cv = states.cv / 1000

        # Get the relative enthalpy, entropy and int. energy with reference to the first
        # point
        u = u - u[0]
        s = s - s[0]
        h = h - h[0]

        return h, u, s, cp, cv


    def get_thermo_CoolProp(T, p):
        u = np.zeros_like(p)
        h = np.zeros_like(p)
        s = np.zeros_like(p)
        cp = np.zeros_like(p)
        cv = np.zeros_like(p)

        for i in range(p.shape[0]):
            u[i] = PropsSI("U", "P", p[i], "T", T, "CO2")
            h[i] = PropsSI("H", "P", p[i], "T", T, "CO2")
            s[i] = PropsSI("S", "P", p[i], "T", T, "CO2")
            cp[i] = PropsSI("C", "P", p[i], "T", T, "CO2")
            cv[i] = PropsSI("O", "P", p[i], "T", T, "CO2")

        # Get the relative enthalpy, entropy and int. energy with reference to the first
        # point
        u = (u - u[0]) / 1000
        s = (s - s[0]) / 1000
        h = (h - h[0]) / 1000
        cp = cp / 1000
        cv = cv / 1000

        return h, u, s, cp, cv


    def plot(p, thermo_Ideal, thermo_RK, thermo_CoolProp, name):
        line_width = 3

        fig, ax = plt.subplots()
        ax.plot(
            p / 1e5, thermo_Ideal, "-", color="b", linewidth=line_width, label="Ideal EoS"
        )
        ax.plot(p / 1e5, thermo_RK, "-", color="r", linewidth=line_width, label="R-K EoS")
        ax.plot(
            p / 1e5, thermo_CoolProp, "-", color="k", linewidth=line_width, label="CoolProp"
        )
        ax.set_xlabel("Pressure [bar]")
        ax.set_ylabel(name)
        ax.legend(prop={"size": 14}, frameon=False)








.. GENERATED FROM PYTHON SOURCE LINES 141-146

EoS Comparison based on thermodynamic properties
------------------------------------------------

This is the main subroutine that compares and plots the thermodynamic values obtained
using three equations of state.

.. GENERATED FROM PYTHON SOURCE LINES 146-166

.. code-block:: Python


    # Input parameters
    T = 300  # Temperature is constant [unit: K]
    p = 1e5 * np.linspace(1, 100, 1000)  # Pressure is varied from 1 to 100 bar [unit: Pa]

    # Read the ideal-gas phase
    ideal_gas_phase = ct.Solution("example_data/co2-thermo.yaml", "CO2-Ideal")
    h_ideal, u_ideal, s_ideal, cp_ideal, cv_ideal = get_thermo_Cantera(
        ideal_gas_phase, T, p
    )

    # Read the Redlich-Kwong phase
    redlich_kwong_phase = ct.Solution("example_data/co2-thermo.yaml", "CO2-RK")
    h_RK, u_RK, s_RK, cp_RK, cv_RK = get_thermo_Cantera(redlich_kwong_phase, T, p)

    # Read the thermo data using CoolProp
    h_CoolProp, u_CoolProp, s_CoolProp, cp_CoolProp, cv_CoolProp = get_thermo_CoolProp(T, p)

    # Plot the results
    plot(p, u_ideal, u_RK, u_CoolProp, "Relative Internal Energy [kJ/kg]")



.. image-sg:: /examples/python/thermo/images/sphx_glr_equations_of_state_001.png
   :alt: equations of state
   :srcset: /examples/python/thermo/images/sphx_glr_equations_of_state_001.png, /examples/python/thermo/images/sphx_glr_equations_of_state_001_2_00x.png 2.00x
   :class: sphx-glr-single-img





.. GENERATED FROM PYTHON SOURCE LINES 167-168

.. code-block:: Python

    plot(p, h_ideal, h_RK, h_CoolProp, "Relative Enthalpy [kJ/kg]")



.. image-sg:: /examples/python/thermo/images/sphx_glr_equations_of_state_002.png
   :alt: equations of state
   :srcset: /examples/python/thermo/images/sphx_glr_equations_of_state_002.png, /examples/python/thermo/images/sphx_glr_equations_of_state_002_2_00x.png 2.00x
   :class: sphx-glr-single-img





.. GENERATED FROM PYTHON SOURCE LINES 169-171

.. code-block:: Python

    plot(p, s_ideal, s_RK, s_CoolProp, "Relative Entropy [kJ/kg-K]")




.. image-sg:: /examples/python/thermo/images/sphx_glr_equations_of_state_003.png
   :alt: equations of state
   :srcset: /examples/python/thermo/images/sphx_glr_equations_of_state_003.png, /examples/python/thermo/images/sphx_glr_equations_of_state_003_2_00x.png 2.00x
   :class: sphx-glr-single-img





.. GENERATED FROM PYTHON SOURCE LINES 172-185

The thermodynamic properties such as internal energy, enthalpy, and entropy are
plotted against the operating pressure at a constant temperature :math:`T = 300` K.
The three equations follow each other closely at low pressures (:math:`P < 10` bar).
However, the ideal gas EoS departs significantly from the observed behavior of gases
near the critical regime (:math:`P_{\rm {crit}} = 73.77` bar).

The ideal gas EoS does not consider inter-molecular interactions and the volume
occupied by individual gas particles. At low temperatures and high pressures,
inter-molecular forces become particularly significant due to a reduction in
inter-molecular distances. Additionally, at high density, the volume of individual
molecules becomes significant. Both of these factors contribute to the deviation from
ideal behavior at high pressures. The cubic Redlich-Kwong EoS, on the other hand,
predicts thermodynamic properties accurately near the critical regime.

.. GENERATED FROM PYTHON SOURCE LINES 185-188

.. code-block:: Python


    # Specific heat at constant pressure
    plot(p, cp_ideal, cp_RK, cp_CoolProp, "$C_p$ [kJ/kg-K]")



.. image-sg:: /examples/python/thermo/images/sphx_glr_equations_of_state_004.png
   :alt: equations of state
   :srcset: /examples/python/thermo/images/sphx_glr_equations_of_state_004.png, /examples/python/thermo/images/sphx_glr_equations_of_state_004_2_00x.png 2.00x
   :class: sphx-glr-single-img





.. GENERATED FROM PYTHON SOURCE LINES 189-193

.. code-block:: Python


    # Specific heat at constant volume
    plot(p, cv_ideal, cv_RK, cv_CoolProp, "$C_v$ [kJ/kg-K]")




.. image-sg:: /examples/python/thermo/images/sphx_glr_equations_of_state_005.png
   :alt: equations of state
   :srcset: /examples/python/thermo/images/sphx_glr_equations_of_state_005.png, /examples/python/thermo/images/sphx_glr_equations_of_state_005_2_00x.png 2.00x
   :class: sphx-glr-single-img





.. GENERATED FROM PYTHON SOURCE LINES 194-214

In the case of Ideal gas EoS, the specific heats at constant pressure (:math:`C_{\rm
p}`) and constant volume (:math:`C_{\rm v}`) are independent of the pressure. Hence,
:math:`C_{\rm p}` and :math:`C_{\rm v}` for ideal EoS do not change as the pressure is
varied from :math:`1` bar to :math:`100` bar in this study.

:math:`C_{\rm p}` for the R-K EoS follows the trend closely with the Helmholtz EoS
from CoolProp up to the critical regime. Although :math:`C_{\rm p}` shows reasonable
agreement with the Helmholtz EoS in sub-critical and supercritical regimes, it
inaccurately predicts a very high value near the critical point. However,
:math:`C_{\rm p}` at the critical point is finite for the real fluid. The sudden rise
in :math:`C_{\rm p}` in the case of the R-K EoS is just a numerical artifact, due to
the EoS yielding infinite values in the limiting case, and not a real singularity.

:math:`C_{\rm v}`, on the other hand, predicts smaller values in the subcritical and
critical regime. However, it shows completely incorrect values in the super-critical
region, making it invalid at very high pressures. It is well known that the cubic
equations typically fail to predict accurate constant-volume heat capacity in the
transcritical region [2]_. Certain cubic EoS models have been extended to resolve this
discrepancy using crossover models. For further information, see the work of Span [2]_
and Saeed et al. [3]_.

.. GENERATED FROM PYTHON SOURCE LINES 216-222

Temperature-Density plots
-------------------------

The following function plots the :math:`T`-:math:`\rho` diagram over a wide pressure
and temperature range. The temperature is varied from 250 K to 400 K. The pressure is
changed from 1 bar to 600 bar.

.. GENERATED FROM PYTHON SOURCE LINES 222-259

.. code-block:: Python


    # Input parameters
    # Set up arrays for pressure and temperature
    p_array = np.logspace(1, np.log10(600), 10, endpoint=True)
    T_array = np.linspace(250, 401, 20)  # Temperature is varied from 250K to 400K
    p_array = 1e5 * np.array(p_array)[:, np.newaxis]

    # Calculate densities for Ideal gas and R-K EoS phases
    states = ct.SolutionArray(ideal_gas_phase, shape=(p_array.size, T_array.size))
    states.TP = T_array, p_array
    density_ideal = states.density_mass

    states = ct.SolutionArray(redlich_kwong_phase, shape=(p_array.size, T_array.size))
    states.TP = T_array, p_array
    density_RK = states.density_mass

    p, T = np.meshgrid(p_array, T_array)
    density_coolprop = PropsSI("D", "P", np.ravel(p), "T", np.ravel(T), "CO2")
    density_coolprop = density_coolprop.reshape(p.shape)

    # Plot
    import cycler
    color = plt.cm.viridis(np.linspace(0, 1, p_array.size))
    plt.rcParams['axes.prop_cycle'] = cycler.cycler('color', color)

    fig, ax = plt.subplots()
    ideal_line = ax.plot(density_ideal.T, T_array, "--", label="Ideal EoS")
    RK_line = ax.plot(density_RK.T, T_array, "o", label="R-K EoS")
    CP_line = ax.plot(density_coolprop, T_array, "-", label="CoolProp")
    ax.text(-27.5, 320, "p = 1 bar", color=color[0], rotation="vertical")
    ax.text(430, 318, "p = 97 bar", color=color[5], rotation=-12)
    ax.text(960, 320, "p = 600 bar", color=color[9], rotation=-68)
    ax.set_xlabel("Density [kg/m$^3$]")
    ax.set_ylabel("Temperature [K]")
    ax.legend(handles=[ideal_line[0], RK_line[0], CP_line[0]])
    plt.show()




.. image-sg:: /examples/python/thermo/images/sphx_glr_equations_of_state_006.png
   :alt: equations of state
   :srcset: /examples/python/thermo/images/sphx_glr_equations_of_state_006.png, /examples/python/thermo/images/sphx_glr_equations_of_state_006_2_00x.png 2.00x
   :class: sphx-glr-single-img





.. GENERATED FROM PYTHON SOURCE LINES 260-280

The figure compares :math:`T-\rho` plots for ideal, R-K, and Helmholtz EoS at
different operating pressures. All three EoS yield the same plots at low pressures (0
bar and 10 bar). However, the Ideal gas EoS departs significantly at high pressures
(:math:`P > 10` bar), where non-ideal effects are prominent. The R-K EoS closely
matches the Helmholtz EoS at supercritical pressures (:math:`P \ge 70` bar). However,
it does depart in the liquid-vapor region that exists at :math:`P < P_{\rm {crit}}`
and low temperatures (:math:`~T_{\rm {crit}}`).

.. [1] I.H. Bell, J. Wronski, S. Quoilin, V. Lemort, "Pure and Pseudo-pure Fluid
   Thermophysical Property Evaluation and the Open-Source Thermophysical Property
   Library CoolProp," Industrial & Engineering Chemistry Research 53 (2014),
   https://pubs.acs.org/doi/10.1021/ie4033999

.. [2] R. Span, "Multiparameter Equations of State - An Accurate Source of
   Thermodynamic Property Data," Springer Berlin Heidelberg (2000),
   https://dx.doi.org/10.1007/978-3-662-04092-8

.. [3] A. Saeed, S. Ghader, "Calculation of density, vapor pressure and heat capacity
   near the critical point by incorporating cubic SRK EoS and crossover translation,"
   Fluid Phase Equilibria (2019) 493, https://doi.org/10.1016/j.fluid.2019.03.027


.. rst-class:: sphx-glr-timing

   **Total running time of the script:** (0 minutes 8.106 seconds)


.. _sphx_glr_download_examples_python_thermo_equations_of_state.py:

.. only:: html

  .. container:: sphx-glr-footer sphx-glr-footer-example

    .. container:: sphx-glr-download sphx-glr-download-jupyter

      :download:`Download Jupyter notebook: equations_of_state.ipynb <equations_of_state.ipynb>`

    .. container:: sphx-glr-download sphx-glr-download-python

      :download:`Download Python source code: equations_of_state.py <equations_of_state.py>`

    .. container:: sphx-glr-download sphx-glr-download-zip

      :download:`Download zipped: equations_of_state.zip <equations_of_state.zip>`


.. only:: html

 .. rst-class:: sphx-glr-signature

    `Gallery generated by Sphinx-Gallery <https://sphinx-gallery.github.io>`_
