Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
trixi-framework
GitHub Repository: trixi-framework/Trixi.jl
Path: blob/main/src/equations/equation_of_state_peng_robinson.jl
5586 views
1
# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).
2
# Since these FMAs can increase the performance of many numerical algorithms,
3
# we need to opt-in explicitly.
4
# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.
5
@muladd begin
6
#! format: noindent
7
8
@doc raw"""
9
PengRobinson{RealT <: Real} <: AbstractEquationOfState
10
11
This defines the Peng-Robinson equation of state
12
given by the pressure and internal energy relations
13
```math
14
p = \frac{R T}{V - b} - \frac{a(T)}{V^2 + 2bV - b^2}, \quad e_{\text{internal}} = c_{v,0} T + K(a(T) - Ta'(T))
15
```
16
where ``V = \rho^{-1}`` and auxiliary expressions for ``a(T)`` and ``K`` are given by
17
```math
18
a(T) = a_0\left(1 + \kappa \left(1 - \sqrt{\frac{T}{T_0}}\right)\right)^2, \quad
19
K = \frac{1}{b 2\sqrt{2}} \log\left( \frac{V + (1 - b \sqrt{2})}{V + (1 + b\sqrt{2})}\right).
20
```
21
Moreover, ``c_v = c_{v,0} - K T a''(T)``.
22
23
All expressions used here are taken from the following references:
24
25
- P. Ma, Y. Lv, M. Ihme (2017)
26
An entropy-stable hybrid scheme for simulations of transcritical real-fluid flows
27
[DOI: 10.1016/j.jcp.2017.03.022](https://doi.org/10.1016/j.jcp.2017.03.022)
28
29
- V. Michel-Dansac, A. Thomann (2024)
30
Towards a fully well-balanced and entropy-stable scheme for the Euler equations with
31
gravity: preserving isentropic steady solutions
32
[DOI: 10.1016/j.compfluid.2025.106853](https://doi.org/10.1016/j.compfluid.2025.106853)
33
34
"""
35
struct PengRobinson{RealT <: Real} <: AbstractEquationOfState
36
R::RealT
37
a0::RealT
38
b::RealT
39
cv0::RealT
40
kappa::RealT
41
Tc::RealT
42
inv2sqrt2b::RealT
43
one_minus_sqrt2_b::RealT
44
one_plus_sqrt2_b::RealT
45
end
46
47
"""
48
PengRobinson(a0, b, cv0, kappa, Tc, R = 8.31446261815324)
49
50
Initializes a Peng-Robinson equation of state given values for physical constants.
51
Here, `R` is the universal gas constant, and the constants `a0, b, cv0, kappa, Tc`
52
follow the naming conventions in Section 2.2 of the following reference:
53
54
- V. Michel-Dansac, A. Thomann (2025)
55
Towards a fully well-balanced and entropy-stable scheme for the Euler equations
56
with gravity: General equations of state
57
[DOI: 10.1016/j.compfluid.2025.106853](https://doi.org/10.1016/j.compfluid.2025.106853)
58
"""
59
function PengRobinson(a0, b, cv0, kappa, Tc, R = 8.31446261815324)
60
inv2sqrt2b = inv(2 * sqrt(2) * b)
61
one_minus_sqrt2_b = (1 - sqrt(2)) * b
62
one_plus_sqrt2_b = (1 + sqrt(2)) * b
63
return PengRobinson{typeof(a0)}(R, a0, b, cv0, kappa, Tc,
64
inv2sqrt2b, one_minus_sqrt2_b, one_plus_sqrt2_b)
65
end
66
67
"""
68
PengRobinson(; RealT = Float64)
69
70
By default, the units for the Peng-Robinson parameters are in mass basis
71
(such as kg / m^3) as opposed to molar basis units (such as kg / mol).
72
73
The default parameters are for N2.
74
"""
75
function PengRobinson(; RealT = Float64)
76
Rgas = 8.31446261815324
77
molar_mass = 0.02801 * 1000 # kg/m3
78
R = Rgas * 1000 / molar_mass
79
pc = 3.40e6
80
Tc = 126.2
81
omega = 0.0372
82
cv0 = 743.2
83
b = 0.077796 * R * Tc / pc
84
a0 = 0.457236 * (R * Tc)^2 / pc
85
kappa = 0.37464 + 1.54226 * omega - 0.26992 * omega^2
86
return PengRobinson(RealT.((a0, b, cv0, kappa, Tc, R))...)
87
end
88
89
# the default tolerance of 10 * eps() does not converge for most Peng-Robinson examples,
90
# so we choose a looser tolerance here. Researchers at the US Naval Research Lab noted
91
# that they typically just use 8 fixed Newton iterations for Peng-Robinson.
92
eos_newton_tol(eos::PengRobinson) = 1e-8
93
94
"""
95
pressure(V, T, eos::PengRobinson)
96
97
Computes pressure for a Peng-Robinson gas from specific volume `V` and temperature `T`,
98
see also [`NonIdealCompressibleEulerEquations1D`](@ref).
99
"""
100
function pressure(V, T, eos::PengRobinson)
101
(; R, b) = eos
102
p = R * T / (V - b) - peng_robinson_a(T, eos) / (V^2 + 2 * b * V - b^2)
103
return p
104
end
105
106
@doc raw"""
107
energy_internal_specific(V, T, eos::PengRobinson)
108
109
Computes specific internal energy for a Peng-Robinson gas from specific volume `V` and temperature `T` as
110
``e_{\text{internal}} = c_{v,0} T + K_1 (a(T) - T a'(T))``.
111
"""
112
function energy_internal_specific(V, T, eos::PengRobinson)
113
(; cv0) = eos
114
K1 = calc_K1(V, eos)
115
e_internal = cv0 * T + K1 * (peng_robinson_a(T, eos) - T * peng_robinson_da(T, eos))
116
return e_internal
117
end
118
119
@inline function heat_capacity_constant_volume(V, T, eos::PengRobinson)
120
(; cv0) = eos
121
K1 = calc_K1(V, eos)
122
cv = cv0 - K1 * T * peng_robinson_d2a(T, eos)
123
return cv
124
end
125
126
function entropy_specific(V, T, eos::PengRobinson)
127
(; cv0, R, b) = eos
128
129
# The specific entropy is defined up to some reference value s0, which is
130
# arbitrarily set to zero here.
131
K1 = calc_K1(V, eos)
132
return cv0 * log(T) + R * log(V - b) - peng_robinson_da(T, eos) * K1
133
end
134
135
function speed_of_sound(V, T, eos::PengRobinson)
136
(; cv0, R, b) = eos
137
138
dpdT_V, dpdV_T = calc_pressure_derivatives(V, T, eos)
139
140
# calculate ratio of specific heats
141
K1 = calc_K1(V, eos)
142
d2aT = peng_robinson_d2a(T, eos)
143
cp0 = cv0 + R
144
cv = cv0 - K1 * T * d2aT
145
cp = cp0 - R - K1 * T * d2aT - T * dpdT_V^2 / dpdV_T
146
gamma = cp / cv
147
148
# calculate bulk modulus, which should be positive
149
# for admissible thermodynamic states.
150
inv_kappa_T = -(V * dpdV_T)
151
c2 = gamma * V * inv_kappa_T
152
return sqrt(c2)
153
end
154
155
function calc_pressure_derivatives(V, T, eos::PengRobinson)
156
(; R, b) = eos
157
denom = (V^2 + 2 * b * V - b^2)
158
a_T = peng_robinson_a(T, eos)
159
inv_V_minus_b = inv(V - b)
160
RdivVb = R * inv_V_minus_b
161
dpdT_V = RdivVb - peng_robinson_da(T, eos) / denom
162
dpdV_T = -RdivVb * T * inv_V_minus_b *
163
(1 - 2 * a_T / (R * T * (V + b) * (denom / (V^2 - b^2))^2))
164
return dpdT_V, dpdV_T
165
end
166
167
# The following are auxiliary functions used in calculating the PR EOS
168
@inline function peng_robinson_a(T, eos::PengRobinson)
169
(; a0, kappa, Tc) = eos
170
return a0 * (1 + kappa * (1 - sqrt(T / Tc)))^2
171
end
172
@inline peng_robinson_da(T, eos) = ForwardDiff.derivative(T -> peng_robinson_a(T, eos),
173
T)
174
@inline peng_robinson_d2a(T, eos) = ForwardDiff.derivative(T -> peng_robinson_da(T, eos),
175
T)
176
177
@inline function calc_K1(V, eos::PengRobinson)
178
(; inv2sqrt2b, one_minus_sqrt2_b, one_plus_sqrt2_b) = eos
179
K1 = inv2sqrt2b * log((V + one_minus_sqrt2_b) / (V + one_plus_sqrt2_b))
180
return K1
181
end
182
end # @muladd
183
184