Thermodynamic Derivatives¶
Helmholtz energy derivatives¶
Thermodynamic derivatives are at the very heart of teqp. All models are defined in the form \(\alpha^r(T, \rho, z)\), where \(\rho\) is the molar density, and z are mole fractions. There are exceptions for models for which the independent variables are in simulation units (Lennard-Jones and its ilk).
Thereofore, to obtain the residual pressure, it is obtained as a derivative:
and other residual thermodynamic properties are defined likewise.
We can define the concise derivative
so we can re-write the derivative above as
Similar definitions apply for all the other thermodynamic properties, with the tot superscript indicating it is the sum of the residual and ideal-gas (not included in teqp) contributions:
Internal energy (\(u= a+Ts\)):
Enthalpy (\(h= u+p/\rho\)):
Entropy (\(s\equiv -(\partial a/\partial T)_v\)):
Gibbs energy (\(g= h-Ts\)):
Derivatives of pressure:
Isochoric specific heat (\(c_v\equiv (\partial u/\partial T)_v\)):
Isobaric specific heat (\(c_p\equiv (\partial h/\partial T)_p\); see Eq. 3.56 from Span for the derivation):
In teqp, these derivatives are obtained from methods like
where the A
in this context indicates the variable \(\Lambda\) above. This naming is perhaps not ideal, since \(A\) is sometimes the total Helmholtz energy, but it was a close visual mnemonic to the character \(\Lambda\).
[1]:
import teqp
teqp.__version__
[1]:
'0.22.0'
[2]:
import numpy as np
[3]:
Tc_K = [300]
pc_Pa = [4e6]
acentric = [0.01]
model = teqp.canonical_PR(Tc_K, pc_Pa, acentric)
[4]:
z = np.array([1.0])
model.get_Ar01(300,300,z)
[4]:
-0.06836660379313926
And there are additional methods to obtain all the derivatives up to a given order:
[5]:
model.get_Ar06n(300,300,z) # derivatives 00, 01, 02, ... 06
[5]:
array([-6.96613834e-02, -6.83666038e-02, 2.53578225e-03, -1.57011622e-04,
1.68186288e-05, -2.23059409e-06, 3.82592585e-07])
But more derivatives are slower than fewer:
[6]:
%timeit model.get_Ar01(300,300,z)
%timeit model.get_Ar04n(300,300,z)
567 ns ± 0.747 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
1.11 μs ± 4.12 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
Note: calling overhead is usually on the order of 1 microsecond
Virial coefficients¶
Virial coefficients represent the thermodynamics of the interaction of two-, three-, … bodies interacting with each other. They can be obtained rigorously if the potential energy surface of interaction is fully known. In general, such a surface can only be constructed for small rigid molecules. Many simple thermodynamic models do a poor job of predicting the thermodynamics captured by the virial coefficients.
The i-th virial coefficient is defined by
with the concise derivative term
teqp supports the virial coefficient directly, there is the get_B2vir
method for the second virial coefficient:
[7]:
model.get_B2vir(300, z)
[7]:
-0.00023661263734465424
And the get_Bnvir
method that allows for the calculation of higher virial coefficients:
[8]:
model.get_Bnvir(7, 300, z)
[8]:
{2: -0.0002366126373446542,
3: 3.001768410777936e-08,
4: -3.2409760373816364e-12,
5: 3.961781646633723e-16,
6: -4.5529239838367004e-20,
7: 5.375927851118494e-24}
The get_Bnvir
method was implemented because when doing automatic differentiation, all the intermediate derivatives are also retained.
There is also a method to calculate temperature derivatives of a given virial coefficient
[9]:
model.get_dmBnvirdTm(2, 3, 300, z) # third temperature derivative of the second virial coefficient
[9]:
1.0095625628421257e-10
Isochoric Thermodynamics Derivatives¶
In the isochoric thermodynamics formalism, the EOS is expressed in the Helmholtz energy density \(\Psi\) as a function of temperature and molar densities \(\vec\rho\). This formalism is handy because it allows for a concise mathematical structure, well suited to implementation in teqp. For instance the pressure is obtained from (see https://doi.org/10.1002/aic.16074):
with the chemical potential \(\mu_i\) obtained from
The molar densities \(\rho_i\) are related to the total density and the mole fractions:
In teqp, the isochoric derivative functions like get_fugacity_coefficients
, get_partial_molar_volumes
take as arguments the temperature T
and the vector of molar concentrations rhovec
=\(\vec\rho\), which are obtained by multiplying the mole fractions by the total density.
Example:
[10]:
model = teqp.build_multifluid_model(["CO2","Argon"], teqp.get_datapath())
T, rhovec = 300, np.array([0.3,0.4])*300 # K, mol/m^3
display(model.get_fugacity_coefficients(T, rhovec))
display(model.get_partial_molar_volumes(T, rhovec))
array([0.97884567, 0.99866748])
array([0.00470644, 0.00480351])