Multi-fluid EOS¶
Peering into the innards of teqp
[1]:
import timeit, json
import pandas
import numpy as np
import teqp
teqp.__version__
[1]:
'0.22.0'
Ancillary Equations¶
Ancillary equations are provided along with multiparameter equations of state. The give a good approximation to the phase equilibrium densities. There are routines in teqp to use the ancillary equations provided with the EOS. First a class containing the ancillary equations is obtained, then methods on that class are called
[2]:
model = teqp.build_multifluid_model(["Methane"], teqp.get_datapath())
anc = model.build_ancillaries()
T = 100.0 # [K]
rhoL, rhoV = anc.rhoL(T), anc.rhoV(T)
print('Densities are:', rhoL, rhoV, 'mol/m^3')
Densities are: 27357.335621492966 42.04100696197727 mol/m^3
But those densities do not correspond to the true phase equilibrium solution, so we need to polish the solution:
[3]:
Niter = 10
rhoLtrue, rhoVtrue = model.pure_VLE_T(T, rhoL, rhoV, Niter)
print('VLE densities are:', rhoLtrue, rhoVtrue, 'mol/m^3')
VLE densities are: 27357.147019094475 42.04798227835163 mol/m^3
And looking the densities, they are slightly different after the phase equilibrium calculation
Ammonia-Water¶
Tillner-Roth and Friend provided a hard-coded model that is in a form not compatible with the other multi-fluid models. It is available via the high-level factory function
[4]:
AW = teqp.AmmoniaWaterTillnerRoth()
AW.get_Ar01(300, 300, np.array([0.9, 0.0]))
[4]:
-0.09731055757504622
Pure fluid loading¶
[5]:
# By default teqp looks for fluids relative to the set of fluids in ROOT/dev/fluids
# The name (case-sensitive) should match the .json file, without the json extension.
%timeit model = teqp.build_multifluid_model(["Methane"], teqp.get_datapath())
32.3 ms ± 70.7 μs per loop (mean ± std. dev. of 7 runs, 10 loops each)
[6]:
# And if you provide valid aliases, alias lookup will be used to resolve the name
# But beware, this is rather a lot slower than the above because all fluid files need to be read
# in to build the alias map
%timeit model = teqp.build_multifluid_model(["n-C1H4"], teqp.get_datapath())
32.4 ms ± 127 μs per loop (mean ± std. dev. of 7 runs, 10 loops each)
So, how to make it faster? Only do it once and cache
[7]:
# Here is the set of possible aliases to absolute paths of files
# Building this map takes a little while (somewhat faster in C++) due to all the file reads
# If you know your files will not change, good idea to build this alias map yourself.
%timeit aliasmap = teqp.build_alias_map(teqp.get_datapath())
aliasmap = teqp.build_alias_map(teqp.get_datapath())
list(aliasmap.keys())[0:10] # the first 10 aliases in the dict
31.2 ms ± 228 μs per loop (mean ± std. dev. of 7 runs, 10 loops each)
[7]:
['1,2-DICHLOROETHANE',
'1,2-dichloroethane',
'1-BUTENE',
'1-Butene',
'100-41-4',
'10024-97-2',
'102687-65-0',
'106-42-3',
'106-97-8',
'106-98-9']
[8]:
# Then load the absolute paths from the alias map,
# which will guarantee that you hit exactly what you were looking for,
# resolving aliases as needed
identifiers = [aliasmap[n] for n in ["n-C1H4"]]
%timeit model = teqp.build_multifluid_model(identifiers, teqp.get_datapath())
533 μs ± 3.64 μs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
At some point soon teqp will support in-memory loading of JSON data for the pure components, without requiring reads from the operating system
[9]:
# And you can also load the JSON that teqp is loading for the pure fluids
pureJSON = teqp.collect_component_json(['Neon','Hydrogen'], teqp.get_datapath())
Mixture model loading¶
[10]:
# Load the default JSON for the binary interaction parameters
BIP = json.load(open(teqp.get_datapath()+'/dev/mixtures/mixture_binary_pairs.json'))
[11]:
# You can obtain interaction parameters either by pairs of names, where name is the name that teqp uses, the ["INFO"]["NAME"] field
params, swap_needed = teqp.get_BIPdep(BIP, ['Methane','Ethane'])
params
[11]:
{'BibTeX': 'Kunz-JCED-2012',
'CAS1': '74-82-8',
'CAS2': '74-84-0',
'F': 1.0,
'Name1': 'Methane',
'Name2': 'Ethane',
'betaT': 0.996336508,
'betaV': 0.997547866,
'function': 'Methane-Ethane',
'gammaT': 1.049707697,
'gammaV': 1.006617867}
[12]:
# Or also by CAS#
params, swap_needed = teqp.get_BIPdep(BIP, ['74-82-8','74-84-0'])
params
[12]:
{'BibTeX': 'Kunz-JCED-2012',
'CAS1': '74-82-8',
'CAS2': '74-84-0',
'F': 1.0,
'Name1': 'Methane',
'Name2': 'Ethane',
'betaT': 0.996336508,
'betaV': 0.997547866,
'function': 'Methane-Ethane',
'gammaT': 1.049707697,
'gammaV': 1.006617867}
[13]:
# But mixing is not allowed
params, swap_needed = teqp.get_BIPdep(BIP, ['74-82-8','Ethane'])
params
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[13], line 2
1 # But mixing is not allowed
----> 2 params, swap_needed = teqp.get_BIPdep(BIP, ['74-82-8','Ethane'])
3 params
ValueError: Can't match the binary pair for: 74-82-8/Ethane
Estimation of interaction parameters¶
Estimation of interaction parameters can be used when no mixture model is present. The flags
keyword argument allows the user to control how estimation is applied. The flags
keyword argument should be a dictionary, with keys of "estimate"
to provide the desired estimation scheme as-needed. For now, the only allowed estimation scheme is Lorentz-Berthelot
.
If it is desired to force the estimation, the "force-estimate"
to force the use of the provided estimation scheme for all binaries, even when a proper mixture model is available. The value associated with "force-estimate"
is ignored.
[14]:
params, swap_needed = teqp.get_BIPdep(BIP, ['74-82-8','74-84-0'], flags={'force-estimate':'yes', 'estimate': 'Lorentz-Berthelot'})
params
[14]:
{'F': 0.0, 'betaT': 1.0, 'betaV': 1.0, 'gammaT': 1.0, 'gammaV': 1.0}
[15]:
# And without the force, the forcing is ignored
params, swap_needed = teqp.get_BIPdep(BIP, ['74-82-8','74-84-0'], flags={'estimate': 'Lorentz-Berthelot'})
params
[15]:
{'BibTeX': 'Kunz-JCED-2012',
'CAS1': '74-82-8',
'CAS2': '74-84-0',
'F': 1.0,
'Name1': 'Methane',
'Name2': 'Ethane',
'betaT': 0.996336508,
'betaV': 0.997547866,
'function': 'Methane-Ethane',
'gammaT': 1.049707697,
'gammaV': 1.006617867}
[16]:
# And the same flags can be passed to the multifluid model constructor
model = teqp.build_multifluid_model(
['74-82-8','74-84-0'],
teqp.get_datapath(),
flags={'force-estimate':'yes', 'estimate': 'Lorentz-Berthelot'})