Equations of State

Equations of State

yaeos is a library based on Equation-of-State calculations. An Equation of State (EOS) is a mathematical model that describes the relationship between thermodynamic properties of a system, typically pressure (P), volume (V), and temperature (T). These equations are fundamental in physics, chemistry, and engineering, especially in thermodynamics and fluid mechanics.

EOS models are used to predict phase behavior, thermodynamic properties, and equilibrium conditions of gases, liquids, solids, and mixtures. They are widely applied in areas such as chemical engineering, petroleum engineering, and material science. From the ideal gas law, to cubic equations of state and SAFT, to more complex models, EOS are essential tools for the study and design of processes.

The classic way of defining an EOS is though a mathematical equation that express the pressure as a function of the volume and temperature. For example, the Van der Waals EOS is defined as:

Where is the pressure, is the molar volume, is the temperature, is the gas constant, and and are parameters that depend on the substance. For mixtures, the and parameters are calculated from the pure components parameters using a mixing rule.

The modern way of defining an EOS is through a mathematical model that express the residual Helmholtz free energy as a function of the mole number, volume and temperature. This approach is more general and allows for a more flexible definition of the EOS in the context of implementing it in code. All thermodynamic properties can be calculated from the residual Helmholtz free energy and its derivatives. yaeos is based on this approach.

This section of the documentation will guide you through the thermodynamic properties that can be calculated with yaeos using Helmholtz free energy models. Additionally, this page serves as a summary of how all these properties can be derived from the Helmholtz free energy and its derivatives.

First of all, you might be used to the classic way of defining an EOS. This is normal since it is the most common way of explaining them in undergraduate thermodynamics courses. So, a very reasonable question is: “how can I obtain the expression for the residual Helmholtz free energy from the expression?”. The answer is the following:

We are ready to go now. We leave an example of how to instantiate a Peng-Robinson EOS model in the next code block if you want to start evaluating properties right away.

program main
   use yaeos

   ! We will instantiate a Peng-Robinson EOS model with two components

   ! Set the variable `model` as a generic `ArModel`
   class(ArModel), allocatable :: model

   ! Set the variables that we're going to use as variable length arrays
   real(pr), allocatable :: n(:), tc(:), pc(:), w(:)

   n = [0.3, 0.7]    ! Number of moles of each component [mol]
   tc = [190, 310]   ! Critical temperatures [K]
   pc = [14, 30]     ! Critical pressures [bar]
   w = [0.001, 0.03] ! Acentric factors [-]

   ! Now we set our model as the PengRobinson76 equation of state.
   model = PengRobinson76(tc, pc, w)

end program main

The examples provided for each property must be treated as a continuation of the previous code block (before the end program statement).

Thermodynamic properties

Pressure:

Pressure can be calculated from the residual Helmholtz free energy as follows:

pressure: block
    real(pr) :: T, V, P, dPdV, dPdT, dPdn(2)

    T = 300.0_pr   ! Set temperature to 300 K
    V = 0.1_pr     ! Set volume to 0.1 L

    ! Calculate pressure and its derivatives
    call model%pressure(n, V, T, P=P, dPdV=dPdV, dPdT=dPdT, dPdn=dPdn)

    print *, "Pressure: ", P
    print *, "dPdV: ", dPdV
    print *, "dPdT: ", dPdT
    print *, "dPdn: ", dPdn

end block pressure

Volume:

As you must know, given a temperature and pressure, the volume has not a unique solution. In the case of a cubic EOS, there are three possible solutions for the volume. For this reason, the volume is calculated iteratively. Provided , , and we fix and and iterate over the volume until the pressure is the same as the input pressure.

To learm how the initial guess for is obtained, please refer to the book “Thermodynamic Models: Fundamentals and Computational Aspects” by Michael L. Michelsen and Jørgen M. Mollerup. Also, you will find how to calculate derivatives in terms of and derivatives.

volume: block
    real(pr) :: T, P, V

    T = 300.0_pr   ! Set temperature to 300 K
    P = 1.0_pr     ! Set pressure to 1 bar

    ! Calculate different volume roots
    call model%volume(n, P, T, V=V, root="liquid")
    print *, "Liquid volume: ", V

    call model%volume(n, P, T, V=V, root="vapor")
    print *, "Vapor volume: ", V

    call model%volume(n, P, T, V=V, root="stable")
    print *, "Stable volume root: ", V

end block volume

Fugacity coefficients (V,T):

Natural logarithm of fugacity coefficients specifing and are calculated as follows:

Remember that the compressibility factor is calculated as:

Next, the derivatives:

With:

lnphi_vt: block
    real(pr) :: T, V, lnPhi(2), dlnPhidP(2), dlnPhidT(2), dlnPhidn(2,2)

    T = 300.0_pr   ! Set temperature to 300 K
    V = 1.0_pr     ! Set volume to 1 liter

    call eos%lnphi_vt(&
      n, V, T, lnPhi=lnPhi, &
      dlnPhidP=dlnPhidP, dlnPhidT=dlnPhidT, dlnPhidn=dlnPhidn &
      )

end block lnphi_vt

The subroutine lnphi_vt also allows to obtain the value of pressure and its derivatives at the specified volume and temperature. Since those values are needed to calculate the fugacity coefficients, you can take advantage of this feature to avoid calculating them twice.

lnphi_vt_p: block
    real(pr) :: T, V, lnPhi(2), P, dPdV, dPdT, dPdn(2)

    T = 300.0_pr   ! Set temperature to 300 K
    V = 1.0_pr     ! Set volume to 1 liter

    call eos%lnphi_vt(&
      n, V, T, lnPhi=lnPhi, P=P, dPdV=dPdV, dPdT=dPdT, dPdn=dPdn &
      )

end block lnphi_vt_p

Fugacity coefficients (P,T):

There is no need for a direct way of calculating natural logarithm the fugacity coefficients specifing and since we can solve volume from those specifications and then calculate the fugacity coefficients using the method. For that reason you can choose the root of the volume that you want to use to calculate the fugacity coefficients in the same way as the volume method.

lnphi_pt: block
    real(pr) :: T, P, lnPhi(2), dlnPhidP(2), dlnPhidT(2), dlnPhidn(2,2)

    T = 300.0_pr   ! Set temperature to 300 K
    P = 1.0_pr     ! Set volume to 1 bar

    call eos%lnphi_pt(&
      n, P, T, root_type="liquid", lnPhi=lnPhi, &
      dlnPhidP=dlnPhidP, dlnPhidT=dlnPhidT, dlnPhidn=dlnPhidn &
      )

    call eos%lnphi_pt(&
      n, P, T, root_type="vapor", lnPhi=lnPhi, &
      dlnPhidP=dlnPhidP, dlnPhidT=dlnPhidT, dlnPhidn=dlnPhidn &
      )

    call eos%lnphi_pt(&
      n, P, T, root_type="stable", lnPhi=lnPhi, &
      dlnPhidP=dlnPhidP, dlnPhidT=dlnPhidT, dlnPhidn=dlnPhidn &
      )

end block lnphi_pt

As in the method you can take advantage and the retrieve the value of the calculated volume and the pressure derivatives.

lnphi_pt_v: block
    real(pr) :: T, P, V, lnPhi(2), dPdV, dPdT, dPdn(2)

    T = 300.0_pr   ! Set temperature to 300 K
    P = 1.0_pr     ! Set volume to 1 bar

    call eos%lnphi_pt(&
      n, P, T, V=V, root_type="stable", lnPhi=lnPhi, &
      dPdV=dPdV, dPdT=dPdT, dPdn=dPdn &
      )

end block lnphi_pt_v

Fugacity (V,T):

Alternative way of calculating fugacity directly from the residual Helmholtz free energy:

fugacity_vt: block
    real(pr) :: T, V, lnf(2), dlnfdV(2), dlnfdT(2), dlnfdn(2,2)

    T = 300.0_pr   ! Set temperature to 300 K
    V = 1.0_pr     ! Set volume to 1 liter

    call eos%lnfug_vt(&
      n, V, T, lnf, &
      dlnfdV, dlnfdT, dlnfdn, &
      )

end block fugacity_vt

Residual entropy (V,T):

We start explaining the residual entropy because it is the easiest property to calculate. Also, helps us to understand how to calculate the next properties.

The residual entropy is calculated as follows:

And its derivatives:

residual_entropy: block
    real(pr) :: T, V, S, dSdV, dSdT, dSdn(2)

    T = 300.0_pr   ! Set temperature to 300 K
    V = 1.0_pr     ! Set volume to 1 liter

    ! Calculate residual entropy and its derivatives
    call eos%residual_entropy(n, V, T, Sr=Sr, SrT=SrT, SrV=SrV, Srn=Srn)

    print *, "Residual entropy: ", Sr
    print *, "SrV: ", SrV
    print *, "SrT: ", SrT
    print *, "Srn: ", Srn

end block residual_entropy

Residual enthalpy (V,T):

The residual enthalpy is calculated as follows:

We know that pressure can be calculated as:

Then, we can obtain:

And we also know how to calculate residual entropy as:

Then, we can obtain the residual enthalpy as:

And its derivatives:

residual_enthalpy: block
    real(pr) :: T, V, Hr, HrV, HrT, Hrn(2)

    T = 300.0_pr   ! Set temperature to 300 K
    V = 1.0_pr     ! Set volume to 1 liter

    ! Calculate residual enthalpy and its derivatives
    call eos%residual_enthalpy(n, V, T, Hr=Hr, HrV=HrV, HrT=HrT, Hrn=Hrn)

    print *, "Residual enthalpy: ", Hr
    print *, "HrV: ", HrV
    print *, "HrT: ", HrT
    print *, "Hrn: ", Hrn

end block residual_enthalpy

Residual Gibbs free energy (V,T):

The residual Gibbs free energy is calculated as follows:

As with residual enthalpy we can easily deduce:

And its derivatives:

residual_gibbs: block
    real(pr) :: T, V, Gr, GrV, GrT, Grn(2)

    T = 300.0_pr   ! Set temperature to 300 K
    V = 1.0_pr     ! Set volume to 1 liter

    ! Calculate residual Gibbs free energy and its derivatives
    call eos%residual_gibbs(n, V, T, Gr=Gr, GrV=GrV, GrT=GrT, Grn=Grn)

    print *, "Residual Gibbs free energy: ", Gr
    print *, "GrV: ", GrV
    print *, "GrT: ", GrT
    print *, "Grn: ", Grn

end block residual_gibbs

Residual internal energy (V,T):

The residual internal energy is calculated as follows:

Therefore:

And its derivatives:

residual_internal_energy: block
    real(pr) :: T, V, Ur, UrV, UrT, Urn(2)

    T = 300.0_pr   ! Set temperature to 300 K
    V = 1.0_pr     ! Set volume to 1 liter

    ! Calculate residual internal energy and its derivatives
    call eos%internal_energyresidual(n, V, T, Ur=Ur, UrV=UrV, UrT=UrT, Urn=Urn)

    print *, "Residual internal energy: ", Ur
    print *, "UrV: ", UrV
    print *, "UrT: ", UrT
    print *, "Urn: ", Urn

end block residual_internal_energy

Residual constant volume heat capacity (V,T):

The residual constant volume heat capacity is calculated as follows:

residual_cv: block
    real(pr) :: T, V, Cv

    T = 300.0_pr   ! Set temperature to 300 K
    V = 1.0_pr     ! Set volume to 1 liter

    ! Calculate residual constant volume heat capacity
    call eos%residual_cv(n, V, T, Cv=Cv)

    print *, "Residual constant volume heat capacity: ", Cv

end block residual_cv

Residual constant pressure heat capacity (V,T):

The residual constant pressure heat capacity is calculated as follows:

residual_cp: block
    real(pr) :: T, V, Cp

    T = 300.0_pr   ! Set temperature to 300 K
    V = 1.0_pr     ! Set volume to 1 liter

    ! Calculate residual constant pressure heat capacity
    call eos%residual_cp(n, V, T, Cp=Cp)

    print *, "Residual constant pressure heat capacity: ", Cp

end block residual_cp

References

[1] 1. Michelsen, M. L., & Mollerup, J. M. (2007). Thermodynamic models: Fundamentals & computational aspects (2. ed). Tie-Line Publications.