DiadFit 0.0.78__py3-none-any.whl → 0.0.80__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- DiadFit/CO2_EOS.py +14 -3
- DiadFit/CO2_H2O_EOS.py +1088 -0
- DiadFit/CO2_in_bubble_error.py +4 -1
- DiadFit/Highrho_polyfit_dataUCB_1117_1400.pkl +0 -0
- DiadFit/Highrho_polyfit_data_CCMR.pkl +0 -0
- DiadFit/Lowrho_polyfit_dataUCB_1117_1400.pkl +0 -0
- DiadFit/Lowrho_polyfit_data_CCMR.pkl +0 -0
- DiadFit/Mediumrho_polyfit_dataUCB_1117_1400.pkl +0 -0
- DiadFit/Mediumrho_polyfit_data_CCMR.pkl +0 -0
- DiadFit/__init__.py +1 -0
- DiadFit/_version.py +1 -1
- DiadFit/cosmicray_filter.py +2 -0
- DiadFit/densimeters.py +124 -58
- DiadFit/diads.py +129 -52
- DiadFit/error_propagation.py +19 -4
- DiadFit/importing_data_files.py +37 -1
- DiadFit/ne_lines.py +108 -5
- {DiadFit-0.0.78.dist-info → DiadFit-0.0.80.dist-info}/METADATA +1 -1
- {DiadFit-0.0.78.dist-info → DiadFit-0.0.80.dist-info}/RECORD +21 -14
- {DiadFit-0.0.78.dist-info → DiadFit-0.0.80.dist-info}/WHEEL +1 -1
- {DiadFit-0.0.78.dist-info → DiadFit-0.0.80.dist-info}/top_level.txt +0 -0
DiadFit/CO2_H2O_EOS.py
ADDED
@@ -0,0 +1,1088 @@
|
|
1
|
+
import numpy as np
|
2
|
+
import matplotlib.pyplot as plt
|
3
|
+
import pandas as pd
|
4
|
+
import inspect
|
5
|
+
from scipy.interpolate import CubicSpline
|
6
|
+
import scipy
|
7
|
+
from scipy.optimize import minimize
|
8
|
+
|
9
|
+
from pathlib import Path
|
10
|
+
from pickle import load
|
11
|
+
import pickle
|
12
|
+
import math
|
13
|
+
from DiadFit.CO2_EOS import *
|
14
|
+
|
15
|
+
|
16
|
+
DiadFit_dir=Path(__file__).parent
|
17
|
+
|
18
|
+
# Set up constants.
|
19
|
+
Tc1 = 647.25
|
20
|
+
Pc1 = 221.19
|
21
|
+
Tc2 = 301.1282
|
22
|
+
Pc2 = 73.773
|
23
|
+
|
24
|
+
# Set up low pressure and high pressure parameters for CO2.
|
25
|
+
|
26
|
+
aL1 = [0] * 16 # Assuming the array is 1-indexed like in C.
|
27
|
+
#So we dont need to adjust everything
|
28
|
+
|
29
|
+
aL1[1] = 4.38269941 / 10**2
|
30
|
+
aL1[2] = -1.68244362 / 10**1
|
31
|
+
aL1[3] = -2.36923373 / 10**1
|
32
|
+
aL1[4] = 1.13027462 / 10**2
|
33
|
+
aL1[5] = -7.67764181 / 10**2
|
34
|
+
aL1[6] = 9.71820593 / 10**2
|
35
|
+
aL1[7] = 6.62674916 / 10**5
|
36
|
+
aL1[8] = 1.06637349 / 10**3
|
37
|
+
aL1[9] = -1.23265258 / 10**3
|
38
|
+
aL1[10] = -8.93953948 / 10**6
|
39
|
+
aL1[11] = -3.88124606 / 10**5
|
40
|
+
aL1[12] = 5.61510206 / 10**5
|
41
|
+
aL1[13] = 7.51274488 / 10**3 # alpha for H2O
|
42
|
+
aL1[14] = 2.51598931 # beta for H2O
|
43
|
+
aL1[15] = 3.94 / 10**2 # gamma for H2O
|
44
|
+
|
45
|
+
# Higher pressure parameters - 0.2- 1 GPa
|
46
|
+
aH1 = [0] * 16 # Assuming the array is 1-indexed like in C
|
47
|
+
|
48
|
+
aH1[1] = 4.68071541 / 10**2
|
49
|
+
aH1[2] = -2.81275941 / 10**1
|
50
|
+
aH1[3] = -2.43926365 / 10**1
|
51
|
+
aH1[4] = 1.10016958 / 10**2
|
52
|
+
aH1[5] = -3.86603525 / 10**2
|
53
|
+
aH1[6] = 9.30095461 / 10**2
|
54
|
+
aH1[7] = -1.15747171 / 10**5
|
55
|
+
aH1[8] = 4.19873848 / 10**4
|
56
|
+
aH1[9] = -5.82739501 / 10**4
|
57
|
+
aH1[10] = 1.00936000 / 10**6
|
58
|
+
aH1[11] = -1.01713593 / 10**5
|
59
|
+
aH1[12] = 1.63934213 / 10**5
|
60
|
+
aH1[13] = -4.49505919 / 10**2 # alpha for H2O
|
61
|
+
aH1[14] = -3.15028174 / 10**1 # beta for H2O
|
62
|
+
aH1[15] = 1.25 / 10**2 # gamma for H2O
|
63
|
+
|
64
|
+
|
65
|
+
# Low presure CO2 parameters.
|
66
|
+
|
67
|
+
aL2 = [0] * 16 # Assuming the array is 1-indexed like in C
|
68
|
+
|
69
|
+
aL2[1] = 1.14400435 / 10**1
|
70
|
+
aL2[2] = -9.38526684 / 10**1
|
71
|
+
aL2[3] = 7.21857006 / 10**1
|
72
|
+
aL2[4] = 8.81072902 / 10**3
|
73
|
+
aL2[5] = 6.36473911 / 10**2
|
74
|
+
aL2[6] = -7.70822213 / 10**2
|
75
|
+
aL2[7] = 9.01506064 / 10**4
|
76
|
+
aL2[8] = -6.81834166 / 10**3
|
77
|
+
aL2[9] = 7.32364258 / 10**3
|
78
|
+
aL2[10] = -1.10288237 / 10**4
|
79
|
+
aL2[11] = 1.26524193 / 10**3
|
80
|
+
aL2[12] = -1.49730823 / 10**3
|
81
|
+
aL2[13] = 7.81940730 / 10**3 # alpha for CO2
|
82
|
+
aL2[14] = -4.22918013 # beta for CO2
|
83
|
+
aL2[15] = 1.585 / 10**1 # gamma for CO2
|
84
|
+
|
85
|
+
|
86
|
+
|
87
|
+
# High pressure CO2 parameters.
|
88
|
+
aH2 = [0] * 16 # Assuming the array is 1-indexed like in C
|
89
|
+
aH2[1] = 5.72573440 / 10**3
|
90
|
+
aH2[2] = 7.94836769
|
91
|
+
aH2[3] = -3.84236281 * 10.0
|
92
|
+
aH2[4] = 3.71600369 / 10**2
|
93
|
+
aH2[5] = -1.92888994
|
94
|
+
aH2[6] = 6.64254770
|
95
|
+
aH2[7] = -7.02203950 / 10**6
|
96
|
+
aH2[8] = 1.77093234 / 10**2
|
97
|
+
aH2[9] = -4.81892026 / 10**2
|
98
|
+
aH2[10] = 3.88344869 / 10**6
|
99
|
+
aH2[11] = -5.54833167 / 10**4
|
100
|
+
aH2[12] = 1.70489748 / 10**3
|
101
|
+
aH2[13] = -4.13039220 / 10**1 # alpha for CO2
|
102
|
+
aH2[14] = -8.47988634 # beta for CO2
|
103
|
+
aH2[15] = 2.800 / 10**2 # gamma for CO2
|
104
|
+
|
105
|
+
## This is for when you only feed a numpy array
|
106
|
+
# def ensure_series(a, b, c):
|
107
|
+
# # Determine the target length
|
108
|
+
# lengths = [len(a) if isinstance(a, pd.Series) else None,
|
109
|
+
# len(b) if isinstance(b, pd.Series) else None,
|
110
|
+
# len(c) if isinstance(c, pd.Series) else None]
|
111
|
+
# lengths = [l for l in lengths if l is not None]
|
112
|
+
# target_length = max(lengths) if lengths else 1
|
113
|
+
#
|
114
|
+
# # Convert each input to a Series of the target length
|
115
|
+
# if not isinstance(a, pd.Series):
|
116
|
+
# a = pd.Series([a] * target_length)
|
117
|
+
# if not isinstance(b, pd.Series):
|
118
|
+
# b = pd.Series([b] * target_length)
|
119
|
+
# if not isinstance(c, pd.Series):
|
120
|
+
# c = pd.Series([c] * target_length)
|
121
|
+
#
|
122
|
+
# return a, b, c
|
123
|
+
#
|
124
|
+
#
|
125
|
+
# def ensure_series_4(a, b, c, d):
|
126
|
+
# # Determine the target length
|
127
|
+
# lengths = [len(a) if isinstance(a, pd.Series) else None,
|
128
|
+
# len(b) if isinstance(b, pd.Series) else None,
|
129
|
+
# len(c) if isinstance(c, pd.Series) else None,
|
130
|
+
# len(d) if isinstance(d, pd.Series) else None]
|
131
|
+
# lengths = [l for l in lengths if l is not None]
|
132
|
+
# target_length = max(lengths) if lengths else 1
|
133
|
+
#
|
134
|
+
# # Convert each input to a Series of the target length
|
135
|
+
# if not isinstance(a, pd.Series):
|
136
|
+
# a = pd.Series([a] * target_length)
|
137
|
+
# if not isinstance(b, pd.Series):
|
138
|
+
# b = pd.Series([b] * target_length)
|
139
|
+
# if not isinstance(c, pd.Series):
|
140
|
+
# c = pd.Series([c] * target_length)
|
141
|
+
# if not isinstance(d, pd.Series):
|
142
|
+
# d = pd.Series([d] * target_length)
|
143
|
+
# return a, b, c, d
|
144
|
+
|
145
|
+
import pandas as pd
|
146
|
+
import numpy as np
|
147
|
+
|
148
|
+
def ensure_series(a, b, c):
|
149
|
+
# Determine the target length
|
150
|
+
lengths = [len(a) if isinstance(a, (pd.Series, np.ndarray)) else None,
|
151
|
+
len(b) if isinstance(b, (pd.Series, np.ndarray)) else None,
|
152
|
+
len(c) if isinstance(c, (pd.Series, np.ndarray)) else None]
|
153
|
+
lengths = [l for l in lengths if l is not None]
|
154
|
+
target_length = max(lengths) if lengths else 1
|
155
|
+
|
156
|
+
# Convert each input to a Series of the target length
|
157
|
+
if not isinstance(a, (pd.Series, np.ndarray)):
|
158
|
+
a = pd.Series([a] * target_length)
|
159
|
+
else:
|
160
|
+
a = pd.Series(a)
|
161
|
+
|
162
|
+
if not isinstance(b, (pd.Series, np.ndarray)):
|
163
|
+
b = pd.Series([b] * target_length)
|
164
|
+
else:
|
165
|
+
b = pd.Series(b)
|
166
|
+
|
167
|
+
if not isinstance(c, (pd.Series, np.ndarray)):
|
168
|
+
c = pd.Series([c] * target_length)
|
169
|
+
else:
|
170
|
+
c = pd.Series(c)
|
171
|
+
|
172
|
+
return a, b, c
|
173
|
+
|
174
|
+
|
175
|
+
def ensure_series_4(a, b, c, d):
|
176
|
+
# Determine the target length
|
177
|
+
lengths = [len(a) if isinstance(a, (pd.Series, np.ndarray)) else None,
|
178
|
+
len(b) if isinstance(b, (pd.Series, np.ndarray)) else None,
|
179
|
+
len(c) if isinstance(c, (pd.Series, np.ndarray)) else None,
|
180
|
+
len(d) if isinstance(d, (pd.Series, np.ndarray)) else None]
|
181
|
+
lengths = [l for l in lengths if l is not None]
|
182
|
+
target_length = max(lengths) if lengths else 1
|
183
|
+
|
184
|
+
# Convert each input to a Series of the target length
|
185
|
+
if not isinstance(a, (pd.Series, np.ndarray)):
|
186
|
+
a = pd.Series([a] * target_length)
|
187
|
+
else:
|
188
|
+
a = pd.Series(a)
|
189
|
+
|
190
|
+
if not isinstance(b, (pd.Series, np.ndarray)):
|
191
|
+
b = pd.Series([b] * target_length)
|
192
|
+
else:
|
193
|
+
b = pd.Series(b)
|
194
|
+
|
195
|
+
if not isinstance(c, (pd.Series, np.ndarray)):
|
196
|
+
c = pd.Series([c] * target_length)
|
197
|
+
else:
|
198
|
+
c = pd.Series(c)
|
199
|
+
|
200
|
+
if not isinstance(d, (pd.Series, np.ndarray)):
|
201
|
+
d = pd.Series([d] * target_length)
|
202
|
+
else:
|
203
|
+
d = pd.Series(d)
|
204
|
+
|
205
|
+
return a, b, c, d
|
206
|
+
|
207
|
+
|
208
|
+
|
209
|
+
## Pure EOS functions
|
210
|
+
# First, we need the pure EOS
|
211
|
+
def pureEOS(i, V, P, B, C, D, E, F, Vc, TK, b, g):
|
212
|
+
"""
|
213
|
+
This function calculates the compressability factor for a pure EOS using the modified Lee-Kesler
|
214
|
+
equation.
|
215
|
+
|
216
|
+
i=0 for H2O, i=1 for CO2.
|
217
|
+
|
218
|
+
You input a volume, and it returns the difference between the compresability factor, and that calculated at the input P, V and T_K.
|
219
|
+
E.g. gives the residual so that you can iterate.
|
220
|
+
"""
|
221
|
+
CF = (1.0 + (B[i] * Vc[i] / V) + (C[i] * Vc[i] *
|
222
|
+
Vc[i] / (V * V)) + (D[i] * Vc[i] * Vc[i] * Vc[i] * Vc[i] /
|
223
|
+
(V * V * V * V)))
|
224
|
+
|
225
|
+
CF += ((E[i] * Vc[i] * Vc[i] * Vc[i] * Vc[i] * Vc[i] /
|
226
|
+
(V * V * V * V * V)))
|
227
|
+
|
228
|
+
CF += ((F[i] * Vc[i] * Vc[i] / (V * V)) *
|
229
|
+
(b[i] + g[i] * Vc[i] * Vc[i] / (V * V)) *
|
230
|
+
math.exp(-g[i] * Vc[i] * Vc[i] / (V * V)))
|
231
|
+
|
232
|
+
return CF - (P * V) / (83.14467 * TK)
|
233
|
+
|
234
|
+
def pureEOS_CF(i, V, P, B, C, D, E, F, Vc, TK, b, g):
|
235
|
+
"""
|
236
|
+
This function calculates the compressability factor for a pure EOS using the modified Lee-Kesler
|
237
|
+
equation.
|
238
|
+
|
239
|
+
i=0 for H2O, i=1 for CO2.
|
240
|
+
|
241
|
+
You input a volume, and it returns the difference between the compresability factor, and that calculated at the input P, V and T_K.
|
242
|
+
E.g. gives the residual so that you can iterate.
|
243
|
+
"""
|
244
|
+
CF = (1.0 + (B[i] * Vc[i] / V) + (C[i] * Vc[i] *
|
245
|
+
Vc[i] / (V * V)) + (D[i] * Vc[i] * Vc[i] * Vc[i] * Vc[i] /
|
246
|
+
(V * V * V * V)))
|
247
|
+
|
248
|
+
CF += ((E[i] * Vc[i] * Vc[i] * Vc[i] * Vc[i] * Vc[i] /
|
249
|
+
(V * V * V * V * V)))
|
250
|
+
|
251
|
+
CF += ((F[i] * Vc[i] * Vc[i] / (V * V)) *
|
252
|
+
(b[i] + g[i] * Vc[i] * Vc[i] / (V * V)) *
|
253
|
+
math.exp(-g[i] * Vc[i] * Vc[i] / (V * V)))
|
254
|
+
|
255
|
+
return CF
|
256
|
+
|
257
|
+
|
258
|
+
# Volume iterative function using Netwon-Raphson method.
|
259
|
+
def purevolume(i, V, P, B, C, D, E, F, Vc, TK, b, g):
|
260
|
+
""" Using the pure EOS, this function solves for the best volume using the pureEOS residual calculated above
|
261
|
+
|
262
|
+
It returns the volume.
|
263
|
+
|
264
|
+
"""
|
265
|
+
for iter in range(1, 51):
|
266
|
+
# Calculate the derivative of the pureEOS function at (V, P)
|
267
|
+
diff = (pureEOS(i, V + 0.0001, P,B, C, D, E, F, Vc, TK, b, g) - pureEOS(i, V, P, B, C, D, E, F, Vc, TK, b, g)) / 0.0001
|
268
|
+
|
269
|
+
# Update the volume using the Newton-Raphson method
|
270
|
+
Vnew = V - pureEOS(i, V, P, B, C, D, E, F, Vc, TK, b, g) / diff
|
271
|
+
|
272
|
+
# Check if the update is within the tolerance (0.000001)
|
273
|
+
if abs(Vnew - V) <= 0.000001:
|
274
|
+
break
|
275
|
+
|
276
|
+
# Update V for the next iteration
|
277
|
+
V = Vnew
|
278
|
+
|
279
|
+
# Return the final estimated volume
|
280
|
+
return V
|
281
|
+
|
282
|
+
def purepressure(i, V, P, TK):
|
283
|
+
""" Using the pure EOS, this function solves for the best pressure using the pureEOS residual calculated above
|
284
|
+
|
285
|
+
It returns the pressure.
|
286
|
+
|
287
|
+
"""
|
288
|
+
for iter in range(1, 51):
|
289
|
+
# Calculate the derivative of the pureEOS function at (V, P)
|
290
|
+
k1_temperature, k2_temperature, k3_temperature, a1, a2, g, b, Vc, B, C, D, E, F, Vguess=get_EOS_params(P, TK)
|
291
|
+
|
292
|
+
diff = (pureEOS(i, V, P + 0.0001, B, C, D, E, F, Vc, TK, b, g) - pureEOS(i, V, P, B, C, D, E, F, Vc, TK, b, g)) / 0.0001
|
293
|
+
|
294
|
+
# Update the pressure using the Newton-Raphson method
|
295
|
+
Pnew = P - pureEOS(i, V, P, B, C, D, E, F, Vc, TK, b, g) / diff
|
296
|
+
|
297
|
+
# Check if the update is within the tolerance (0.000001)
|
298
|
+
if abs(Pnew - P) <= 0.000001:
|
299
|
+
break
|
300
|
+
|
301
|
+
# Update P for the next iteration
|
302
|
+
P = Pnew
|
303
|
+
|
304
|
+
# Return the final estimated pressure
|
305
|
+
return P
|
306
|
+
|
307
|
+
|
308
|
+
|
309
|
+
|
310
|
+
def mol_vol_to_density(mol_vol, XH2O):
|
311
|
+
""" Converts molar mass to molar density for a given XH2O"""
|
312
|
+
density=((1-XH2O)*44 + (XH2O)*18)/mol_vol
|
313
|
+
return density
|
314
|
+
|
315
|
+
def pure_lnphi(i, Z, B, Vc, V, C, D, E, F, g, b):
|
316
|
+
"""
|
317
|
+
This function calculates the fugacity coefficient from the equation of state for a pure fluid
|
318
|
+
|
319
|
+
"""
|
320
|
+
lnph = Z[i] - 1.0 - math.log(Z[i]) + (B[i] * Vc[i] / V[i]) + (C[i] * Vc[i] * Vc[i] / (2.0 * V[i] * V[i]))
|
321
|
+
lnph += (D[i] * Vc[i] * Vc[i] * Vc[i] * Vc[i] / (4.0 * V[i] * V[i] * V[i] * V[i]))
|
322
|
+
lnph += (E[i] * Vc[i] * Vc[i] * Vc[i] * Vc[i] * Vc[i] / (5.0 * V[i] * V[i] * V[i] * V[i] * V[i]))
|
323
|
+
lnph += (F[i] / (2.0 * g[i])) * (b[i] + 1.0 - (b[i] + 1.0 + g[i] * Vc[i] * Vc[i] / (V[i] * V[i])) * math.exp(-g[i] * Vc[i] * Vc[i] / (V[i] * V[i])))
|
324
|
+
|
325
|
+
return lnph
|
326
|
+
|
327
|
+
|
328
|
+
|
329
|
+
## Mixing between species
|
330
|
+
def cbrt_calc(x):
|
331
|
+
"""
|
332
|
+
This function calculates the cubic root that can deal with negative numbers.
|
333
|
+
"""
|
334
|
+
if x >= 0:
|
335
|
+
return math.pow(x, 1/3)
|
336
|
+
else:
|
337
|
+
return -math.pow(-x, 1/3)
|
338
|
+
|
339
|
+
|
340
|
+
def mixEOS(V, P, BVc, CVc2, DVc4, EVc5, FVc2, bmix, gVc2, TK):
|
341
|
+
""" This function is like the one for the pureEOS. It calculates the compressability factor, and
|
342
|
+
then calculates the compressability factor based on the P-V-T you entered. It returns the residual between those two values.
|
343
|
+
"""
|
344
|
+
CF = 1.0 + (BVc / V) + (CVc2 / (V * V)) + (DVc4 / (V * V * V * V)) + (EVc5 / (V * V * V * V * V))
|
345
|
+
CF += (FVc2 / (V * V)) * (bmix + gVc2 / (V * V)) * np.exp(-gVc2 / (V * V))
|
346
|
+
|
347
|
+
return CF - (P * V) / (83.14467 * TK)
|
348
|
+
|
349
|
+
|
350
|
+
def mixEOS_CF(V, P, BVc, CVc2, DVc4, EVc5, FVc2, bmix, gVc2, TK):
|
351
|
+
""" This function is like the one for the pureEOS. It calculates the compressability factor, and
|
352
|
+
then calculates that based on the P-V-T you entered. It does not return the residual
|
353
|
+
"""
|
354
|
+
CF = 1.0 + (BVc / V) + (CVc2 / (V * V)) + (DVc4 / (V * V * V * V)) + (EVc5 / (V * V * V * V * V))
|
355
|
+
CF += (FVc2 / (V * V)) * (bmix + gVc2 / (V * V)) * np.exp(-gVc2 / (V * V))
|
356
|
+
|
357
|
+
return CF
|
358
|
+
|
359
|
+
|
360
|
+
def mixvolume(V, P, BVc, CVc2, DVc4, EVc5, FVc2, bmix, gVc2, TK):
|
361
|
+
""" This function iterates in volume space to get the best match to the entered pressure using the mixEOS function above.
|
362
|
+
|
363
|
+
"""
|
364
|
+
for iter in range(1, 51):
|
365
|
+
diff = ((mixEOS(V + 0.0001, P, BVc, CVc2, DVc4, EVc5, FVc2, bmix, gVc2, TK)
|
366
|
+
- mixEOS(V, P, BVc, CVc2, DVc4, EVc5, FVc2, bmix, gVc2, TK)) / 0.0001)
|
367
|
+
Vnew = V - mixEOS(V, P, BVc, CVc2, DVc4, EVc5, FVc2, bmix, gVc2, TK) / diff
|
368
|
+
if abs(Vnew - V) <= 0.000001:
|
369
|
+
break
|
370
|
+
V = Vnew
|
371
|
+
|
372
|
+
return V
|
373
|
+
|
374
|
+
def mixpressure(P, V, TK, Y):
|
375
|
+
""" This function iterates in pressure space to get the best match to the entered volume using the mixEOS function above.
|
376
|
+
|
377
|
+
"""
|
378
|
+
for iter in range(1, 51):
|
379
|
+
k1_temperature, k2_temperature, k3_temperature, a1, a2, g, b, Vc, B, C, D, E, F, Vguess=get_EOS_params(P, TK)
|
380
|
+
Bij, Vcij, BVc_prm, BVc, Cijk, Vcijk, CVc2_prm, CVc2, Dijklm, Vcijklm, DVc4_prm, DVc4, Eijklmn, Vcijklmn, EVc5_prm, EVc5, Fij, FVc2_prm, FVc2, bmix, b_prm, gijk, gVc2_prm, gVc2=mixing_rules(B, C,D, E, F, Vc, Y, b, g, k1_temperature, k2_temperature, k3_temperature)
|
381
|
+
|
382
|
+
diff = ((mixEOS(V, P + 0.0001, BVc, CVc2, DVc4, EVc5, FVc2, bmix, gVc2, TK)
|
383
|
+
- mixEOS(V, P, BVc, CVc2, DVc4, EVc5, FVc2, bmix, gVc2, TK)) / 0.0001)
|
384
|
+
Pnew = P - mixEOS(V, P, BVc, CVc2, DVc4, EVc5, FVc2, bmix, gVc2, TK) / diff
|
385
|
+
if abs(Pnew - P) <= 0.000001:
|
386
|
+
break
|
387
|
+
P = Pnew
|
388
|
+
|
389
|
+
return P
|
390
|
+
|
391
|
+
|
392
|
+
|
393
|
+
def mix_lnphi(i, Zmix, BVc_prm, CVc2_prm, DVc4_prm, EVc5_prm, FVc2_prm, FVc2, bmix, b_prm, gVc2, gVc2_prm, Vmix):
|
394
|
+
lnph=0
|
395
|
+
|
396
|
+
lnph = -math.log(Zmix)
|
397
|
+
lnph += (BVc_prm[i] / Vmix)
|
398
|
+
lnph += (CVc2_prm[i] / (2.0 * Vmix ** 2))
|
399
|
+
lnph += (DVc4_prm[i] / (4.0 * Vmix ** 4))
|
400
|
+
lnph += (EVc5_prm[i] / (5.0 * Vmix ** 5))
|
401
|
+
lnph += ((FVc2_prm[i] * bmix + b_prm[i] * FVc2) / (2 * gVc2)) * (1.0 - math.exp(-gVc2 / (Vmix ** 2)))
|
402
|
+
lnph += ((FVc2_prm[i] * gVc2 + gVc2_prm[i] * FVc2 - FVc2 * bmix * (gVc2_prm[i] - gVc2)) / (2.0 * gVc2 ** 2)) * (1.0 - (gVc2 / (Vmix ** 2) + 1.0) * math.exp(-gVc2 / (Vmix ** 2)))
|
403
|
+
lnph += -(((gVc2_prm[i] - gVc2) * FVc2) / (2 * gVc2 ** 2)) * (2.0 - (((gVc2 ** 2) / (Vmix ** 4)) + (2.0 * gVc2 / (Vmix ** 2)) + 2.0) * math.exp(-gVc2 / (Vmix ** 2)))
|
404
|
+
|
405
|
+
|
406
|
+
return lnph
|
407
|
+
|
408
|
+
|
409
|
+
|
410
|
+
def mix_fugacity_ind(*, P_kbar, T_K, XH2O, Vmix):
|
411
|
+
""" This function calculates fugacity for a single sample.
|
412
|
+
It returns the activity of each component (fugacity/fugacity in pure component)
|
413
|
+
|
414
|
+
|
415
|
+
"""
|
416
|
+
|
417
|
+
P=P_kbar*1000
|
418
|
+
TK=T_K
|
419
|
+
XCO2=1-XH2O
|
420
|
+
Y = [0] * 2
|
421
|
+
Y[0]=XH2O
|
422
|
+
Y[1]=XCO2
|
423
|
+
|
424
|
+
# Calculate the constants you neeed
|
425
|
+
k1_temperature, k2_temperature, k3_temperature, a1, a2, g, b, Vc, B, C, D, E, F, Vguess=get_EOS_params(P, TK)
|
426
|
+
|
427
|
+
|
428
|
+
lnphi2kbL = [0.0, 0.0]
|
429
|
+
lnphi2kbH = [0.0, 0.0]
|
430
|
+
|
431
|
+
lnphi = [0.0, 0.0]
|
432
|
+
phi_mix = [0.0, 0.0]
|
433
|
+
activity = [0.0, 0.0]
|
434
|
+
f = [0.0, 0.0]
|
435
|
+
|
436
|
+
# Calculate at pressure of interest
|
437
|
+
Z_pure = [0.0, 0.0]
|
438
|
+
V_pure = [0.0, 0.0]
|
439
|
+
|
440
|
+
|
441
|
+
# Initial guess for volumne
|
442
|
+
|
443
|
+
if P<=2000:
|
444
|
+
Vguess=1000
|
445
|
+
elif P>20000:
|
446
|
+
Vguess=10
|
447
|
+
else:
|
448
|
+
Vguess=100
|
449
|
+
|
450
|
+
V_pure[1]=purevolume(1, Vguess, P, B, C, D, E, F, Vc, TK, b, g)
|
451
|
+
V_pure[0]=purevolume(0, Vguess, P, B, C, D, E, F, Vc, TK, b, g)
|
452
|
+
Z_pure[0]=P*V_pure[0]/(83.14467*TK)
|
453
|
+
Z_pure[1]=P*V_pure[1]/(83.14467*TK)
|
454
|
+
|
455
|
+
|
456
|
+
#H2O pure
|
457
|
+
lnphi0=pure_lnphi(0, Z_pure, B, Vc, V_pure, C, D, E, F, g, b)
|
458
|
+
#CO2 pure
|
459
|
+
lnphi1=pure_lnphi(1, Z_pure, B, Vc, V_pure, C, D, E, F, g, b)
|
460
|
+
|
461
|
+
|
462
|
+
# Funny maths you have to do incase P>2000 bars
|
463
|
+
|
464
|
+
|
465
|
+
# First, calculate parameters with low pressure coefficients
|
466
|
+
k1_temperature_LP, k2_temperature_LP, k3_temperature_LP, a1_LP, a2_LP, g_LP, b_LP, Vc_LP, B_LP, C_LP, D_LP, E_LP, F_LP, Vguess=get_EOS_params(500, TK)
|
467
|
+
Z_pure_LP_2000 = [0.0, 0.0]
|
468
|
+
V_pure_LP_2000 = [0.0, 0.0]
|
469
|
+
V_pure_LP_2000[0]=purevolume(0, 100, 2000, B_LP, C_LP, D_LP, E_LP, F_LP, Vc_LP, TK, b_LP, g_LP)
|
470
|
+
V_pure_LP_2000[1]=purevolume(1, 100, 2000, B_LP, C_LP, D_LP, E_LP, F_LP, Vc_LP, TK, b_LP, g_LP)
|
471
|
+
|
472
|
+
Z_pure_LP_2000[0]=2000.0*V_pure_LP_2000[0]/(83.14467*TK)
|
473
|
+
Z_pure_LP_2000[1]=2000.0*V_pure_LP_2000[1]/(83.14467*TK)
|
474
|
+
|
475
|
+
# Low pressure
|
476
|
+
lnphi0_LP=pure_lnphi(0, Z_pure_LP_2000, B_LP, Vc_LP, V_pure_LP_2000, C_LP, D_LP, E_LP, F_LP, g_LP, b_LP)
|
477
|
+
lnphi1_LP=pure_lnphi(1, Z_pure_LP_2000, B_LP, Vc_LP, V_pure_LP_2000, C_LP, D_LP, E_LP, F_LP, g_LP, b_LP)
|
478
|
+
|
479
|
+
|
480
|
+
|
481
|
+
# Same with high P
|
482
|
+
k1_temperature_HP, k2_temperature_HP, k3_temperature_HP, a1_HP, a2_HP, g_HP, b_HP, Vc_HP, B_HP, C_HP, D_HP, E_HP, F_HP, Vguess=get_EOS_params(3000, TK)
|
483
|
+
Z_pure_HP_2000 = [0.0, 0.0]
|
484
|
+
V_pure_HP_2000 = [0.0, 0.0]
|
485
|
+
V_pure_HP_2000[0]=purevolume(0, 100, 2000, B_HP, C_HP, D_HP, E_HP, F_HP, Vc_HP, TK, b_HP, g_HP)
|
486
|
+
V_pure_HP_2000[1]=purevolume(1, 100, 2000, B_HP, C_HP, D_HP, E_HP, F_HP, Vc_HP, TK, b_HP, g_HP)
|
487
|
+
Z_pure_HP_2000[0]=2000.0*V_pure_HP_2000[0]/(83.14467*TK)
|
488
|
+
Z_pure_HP_2000[1]=2000.0*V_pure_HP_2000[1]/(83.14467*TK)
|
489
|
+
|
490
|
+
|
491
|
+
#pure_HP
|
492
|
+
lnphi0_HP=pure_lnphi(0, Z_pure_HP_2000, B_HP, Vc_HP, V_pure_HP_2000, C_HP, D_HP, E_HP, F_HP, g_HP, b_HP)
|
493
|
+
lnphi1_HP=pure_lnphi(1, Z_pure_HP_2000, B_HP, Vc_HP, V_pure_HP_2000, C_HP, D_HP, E_HP, F_HP, g_HP, b_HP)
|
494
|
+
|
495
|
+
if P>2000:
|
496
|
+
# This is a weird thing described on Page 6 of Yoshimura -
|
497
|
+
lnphi0=lnphi0-lnphi0_HP+lnphi0_LP
|
498
|
+
lnphi1=lnphi1-lnphi1_HP+lnphi1_LP
|
499
|
+
|
500
|
+
phi0_pure=math.exp(lnphi0)
|
501
|
+
phi1_pure=math.exp(lnphi1)
|
502
|
+
|
503
|
+
|
504
|
+
|
505
|
+
# Now we need to do the mixed fugacity part of this
|
506
|
+
#--------------------------------------------------------------------------
|
507
|
+
k1_temperature, k2_temperature, k3_temperature, a1, a2, g, b, Vc, B, C, D, E, F, Vguess=get_EOS_params(P, TK)
|
508
|
+
Bij, Vcij, BVc_prm, BVc, Cijk, Vcijk, CVc2_prm, CVc2, Dijklm, Vcijklm, DVc4_prm, DVc4, Eijklmn, Vcijklmn, EVc5_prm, EVc5, Fij, FVc2_prm, FVc2, bmix, b_prm, gijk, gVc2_prm, gVc2=mixing_rules(B, C, D, E, F, Vc, Y, b, g, k1_temperature, k2_temperature, k3_temperature)
|
509
|
+
Zmix=(P*Vmix)/(83.14467*TK)
|
510
|
+
lnphi_mix = [0.0, 0.0]
|
511
|
+
phi_mix = [0.0, 0.0]
|
512
|
+
lnphi_mix[0]=mix_lnphi(0, Zmix, BVc_prm, CVc2_prm, DVc4_prm, EVc5_prm, FVc2_prm,FVc2, bmix, b_prm, gVc2, gVc2_prm, Vmix)
|
513
|
+
lnphi_mix[1]=mix_lnphi(1, Zmix, BVc_prm, CVc2_prm, DVc4_prm, EVc5_prm, FVc2_prm,FVc2, bmix, b_prm, gVc2, gVc2_prm, Vmix)
|
514
|
+
|
515
|
+
|
516
|
+
|
517
|
+
# But what if P>2000, well we need to do these calcs at low and high P
|
518
|
+
# High P - using Parameters from up above
|
519
|
+
Bij_HP, Vcij_HP, BVc_prm_HP, BVc_HP, Cijk_HP, Vcijk_HP, CVc2_prm_HP, CVc2_HP, Dijklm_HP, Vcijklm_HP, DVc4_prm_HP, DVc4_HP, Eijklmn_HP, Vcijklmn_HP, EVc5_prm_HP, EVc5_HP, Fij_HP, FVc2_prm_HP, FVc2_HP, bmix_HP, b_prm_HP, gijk_HP, gVc2_prm_HP, gVc2_HP=mixing_rules(B_HP, C_HP, D_HP, E_HP, F_HP, Vc_HP, Y, b_HP, g_HP, k1_temperature_HP, k2_temperature_HP, k3_temperature_HP)
|
520
|
+
Vmix_HP=mixvolume(100, 2000, BVc_HP, CVc2_HP, DVc4_HP, EVc5_HP, FVc2_HP, bmix_HP, gVc2_HP, TK)
|
521
|
+
Zmix_HP=(2000*Vmix_HP)/(83.14467*TK)
|
522
|
+
lnphi_mix_HP = [0.0, 0.0]
|
523
|
+
lnphi_mix_HP[0]=mix_lnphi(0, Zmix_HP, BVc_prm_HP, CVc2_prm_HP, DVc4_prm_HP, EVc5_prm_HP, FVc2_prm_HP,FVc2_HP, bmix_HP, b_prm_HP, gVc2_HP, gVc2_prm_HP, Vmix_HP)
|
524
|
+
lnphi_mix_HP[1]=mix_lnphi(1, Zmix_HP, BVc_prm_HP, CVc2_prm_HP, DVc4_prm_HP, EVc5_prm_HP, FVc2_prm_HP, FVc2_HP, bmix_HP, b_prm_HP, gVc2_HP, gVc2_prm_HP, Vmix_HP)
|
525
|
+
|
526
|
+
|
527
|
+
# Same for LP
|
528
|
+
Bij_LP, Vcij_LP, BVc_prm_LP, BVc_LP, Cijk_LP, Vcijk_LP, CVc2_prm_LP, CVc2_LP, Dijklm_LP, Vcijklm_LP, DVc4_prm_LP, DVc4_LP, Eijklmn_LP, Vcijklmn_LP, EVc5_prm_LP, EVc5_LP, Fij_LP, FVc2_prm_LP, FVc2_LP, bmix_LP, b_prm_LP, gijk_LP, gVc2_prm_LP, gVc2_LP=mixing_rules(B_LP, C_LP, D_LP, E_LP, F_LP,Vc_LP, Y, b_LP, g_LP, k1_temperature_LP, k2_temperature_LP, k3_temperature_LP)
|
529
|
+
Vmix_LP=mixvolume(100, 2000, BVc_LP, CVc2_LP, DVc4_LP, EVc5_LP, FVc2_LP, bmix_LP, gVc2_LP, TK)
|
530
|
+
Zmix_LP=(2000*Vmix_LP)/(83.14467*TK)
|
531
|
+
lnphi_mix_LP = [0.0, 0.0]
|
532
|
+
lnphi_mix_LP[0]=mix_lnphi(0, Zmix_LP, BVc_prm_LP, CVc2_prm_LP, DVc4_prm_LP, EVc5_prm_LP, FVc2_prm_LP, FVc2_LP, bmix_LP, b_prm_LP, gVc2_LP, gVc2_prm_LP, Vmix_LP)
|
533
|
+
lnphi_mix_LP[1]=mix_lnphi(1, Zmix_LP, BVc_prm_LP, CVc2_prm_LP, DVc4_prm_LP, EVc5_prm_LP, FVc2_prm_LP,FVc2_LP, bmix_LP, b_prm_LP, gVc2_LP, gVc2_prm_LP, Vmix_LP)
|
534
|
+
|
535
|
+
if P>2000:
|
536
|
+
lnphi_mix[0]=lnphi_mix[0]-lnphi_mix_HP[0]+lnphi_mix_LP[0]
|
537
|
+
lnphi_mix[1]=lnphi_mix[1]-lnphi_mix_HP[1]+lnphi_mix_LP[1]
|
538
|
+
|
539
|
+
|
540
|
+
phi_mix[0]=math.exp(lnphi_mix[0])
|
541
|
+
phi_mix[1]=math.exp(lnphi_mix[1])
|
542
|
+
|
543
|
+
|
544
|
+
|
545
|
+
|
546
|
+
|
547
|
+
|
548
|
+
|
549
|
+
activity[0] = phi_mix[0] * Y[0] / phi0_pure
|
550
|
+
activity[1] = phi_mix[1] * Y[1] / phi1_pure
|
551
|
+
|
552
|
+
f[0] = Y[0] * P * phi_mix[0] / 1000.0 # fugacity in kbar
|
553
|
+
f[1] = Y[1] * P * phi_mix[1] / 1000.0 # fugacity in kbar
|
554
|
+
|
555
|
+
|
556
|
+
|
557
|
+
|
558
|
+
return f[0], f[1], activity[0], activity[1], Zmix
|
559
|
+
|
560
|
+
|
561
|
+
|
562
|
+
|
563
|
+
|
564
|
+
def mixing_rules(B, C, D, E, F, Vc, Y, b, g, k1_temperature, k2_temperature, k3_temperature):
|
565
|
+
Bij = np.zeros((2, 2))
|
566
|
+
Vcij = np.zeros((2, 2))
|
567
|
+
BVc_prm = np.zeros(2)
|
568
|
+
b_prm=np.zeros(2)
|
569
|
+
BVc = 0.0
|
570
|
+
Cijk = np.zeros((2, 2, 2))
|
571
|
+
Vcijk = np.zeros((2, 2, 2))
|
572
|
+
CVc2_prm = np.zeros(2)
|
573
|
+
CVc2 = 0.0
|
574
|
+
Dijklm = np.zeros((2, 2, 2, 2, 2))
|
575
|
+
Vcijklm = np.zeros((2, 2, 2, 2, 2))
|
576
|
+
Eijklmn=np.zeros((2, 2, 2, 2, 2, 2))
|
577
|
+
Vcijklmn=np.zeros((2, 2, 2, 2, 2, 2))
|
578
|
+
DVc4_prm=np.zeros(2)
|
579
|
+
DVc4=0
|
580
|
+
|
581
|
+
EVc5_prm = np.zeros(2)
|
582
|
+
EVc5 = 0.0
|
583
|
+
Fij = np.zeros((2, 2))
|
584
|
+
FVc2_prm = np.zeros(2)
|
585
|
+
FVc2 = 0.0
|
586
|
+
gijk = np.zeros((2, 2, 2))
|
587
|
+
gVc2_prm = np.zeros(2)
|
588
|
+
gVc2 = 0.0
|
589
|
+
|
590
|
+
for i in range(2):
|
591
|
+
for j in range(2):
|
592
|
+
k1 = 1.0 if i == j else k1_temperature
|
593
|
+
Bij[i, j] = pow((cbrt_calc(B[i]) + cbrt_calc(B[j]))/2, 3.0) * k1
|
594
|
+
|
595
|
+
for i in range(2):
|
596
|
+
for j in range(2):
|
597
|
+
Vcij[i, j] = pow((cbrt_calc(Vc[i]) + cbrt_calc(Vc[j]))/2, 3.0)
|
598
|
+
|
599
|
+
|
600
|
+
for i in range(2):
|
601
|
+
for j in range(2):
|
602
|
+
BVc_prm[i] += 2 * Y[j] * Bij[i, j] * Vcij[i, j]
|
603
|
+
|
604
|
+
|
605
|
+
|
606
|
+
for i in range(2):
|
607
|
+
for j in range(2):
|
608
|
+
|
609
|
+
BVc += Y[i] * Y[j] * Bij[i, j] * Vcij[i, j]
|
610
|
+
|
611
|
+
for i in range(2):
|
612
|
+
for j in range(2):
|
613
|
+
for k in range(2):
|
614
|
+
k2 = 1.0 if i == j and j == k else k2_temperature
|
615
|
+
Cijk[i, j, k] = pow((cbrt_calc(C[i]) + cbrt_calc(C[j]) + cbrt_calc(C[k]))/3, 3.0) * k2
|
616
|
+
|
617
|
+
for i in range(2):
|
618
|
+
for j in range(2):
|
619
|
+
for k in range(2):
|
620
|
+
Vcijk[i, j, k] = pow((cbrt_calc(Vc[i]) + cbrt_calc(Vc[j]) + cbrt_calc(Vc[k]))/3, 3.0)
|
621
|
+
|
622
|
+
for i in range(2):
|
623
|
+
for j in range(2):
|
624
|
+
for k in range(2):
|
625
|
+
CVc2_prm[i] += 3 * Y[j] * Y[k] * Cijk[i, j, k] * Vcijk[i, j, k] * Vcijk[i, j, k]
|
626
|
+
|
627
|
+
CVc2=0.0
|
628
|
+
for i in range(2):
|
629
|
+
for j in range(2):
|
630
|
+
for k in range(2):
|
631
|
+
CVc2 += Y[i] * Y[j] * Y[k] * Cijk[i, j, k] * Vcijk[i, j, k] * Vcijk[i, j, k]
|
632
|
+
|
633
|
+
|
634
|
+
for i in range(2):
|
635
|
+
for j in range(2):
|
636
|
+
for k in range(2):
|
637
|
+
for l in range(2):
|
638
|
+
for m in range(2):
|
639
|
+
Dijklm[i, j, k, l, m] = pow((cbrt_calc(D[i]) + cbrt_calc(D[j]) + cbrt_calc(D[k]) + cbrt_calc(D[l]) + cbrt_calc(D[m]))/5, 3.0)
|
640
|
+
|
641
|
+
for i in range(2):
|
642
|
+
for j in range(2):
|
643
|
+
for k in range(2):
|
644
|
+
for l in range(2):
|
645
|
+
for m in range(2):
|
646
|
+
Vcijklm[i, j, k, l, m] = pow((cbrt_calc(Vc[i]) + cbrt_calc(Vc[j]) + cbrt_calc(Vc[k]) + cbrt_calc(Vc[l]) + cbrt_calc(Vc[m]))/5, 3.0)
|
647
|
+
|
648
|
+
|
649
|
+
for i in range(2):
|
650
|
+
for j in range(2):
|
651
|
+
for k in range(2):
|
652
|
+
for l in range(2):
|
653
|
+
for m in range(2):
|
654
|
+
DVc4_prm[i] += 5.0 * Y[j] * Y[k] * Y[l] * Y[m] * Dijklm[i, j, k, l, m] * pow(Vcijklm[i, j, k, l, m], 4.0)
|
655
|
+
|
656
|
+
|
657
|
+
for i in range(2):
|
658
|
+
for j in range(2):
|
659
|
+
for k in range(2):
|
660
|
+
for l in range(2):
|
661
|
+
for m in range(2):
|
662
|
+
DVc4 += Y[i] * Y[j] * Y[k] * Y[l] * Y[m] * Dijklm[i, j, k, l, m] * pow(Vcijklm[i, j, k, l, m], 4)
|
663
|
+
|
664
|
+
# Missing Eijklmn,
|
665
|
+
for i in range(2):
|
666
|
+
for j in range(2):
|
667
|
+
for k in range(2):
|
668
|
+
for l in range(2):
|
669
|
+
for m in range(2):
|
670
|
+
for n in range(2):
|
671
|
+
Eijklmn[i, j, k, l, m, n] = pow((cbrt_calc(E[i]) + cbrt_calc(E[j]) + cbrt_calc(E[k]) + cbrt_calc(E[l]) + cbrt_calc(E[m]) + cbrt_calc(E[n]))/6, 3.0)
|
672
|
+
|
673
|
+
|
674
|
+
for i in range(2):
|
675
|
+
for j in range(2):
|
676
|
+
for k in range(2):
|
677
|
+
for l in range(2):
|
678
|
+
for m in range(2):
|
679
|
+
for n in range(2):
|
680
|
+
Vcijklmn[i, j, k, l, m, n] = pow((cbrt_calc(Vc[i]) + cbrt_calc(Vc[j]) + cbrt_calc(Vc[k]) + cbrt_calc(Vc[l]) + cbrt_calc(Vc[m]) + cbrt_calc(Vc[n]))/6, 3.0)
|
681
|
+
|
682
|
+
|
683
|
+
|
684
|
+
for i in range(2):
|
685
|
+
for j in range(2):
|
686
|
+
for k in range(2):
|
687
|
+
for l in range(2):
|
688
|
+
for m in range(2):
|
689
|
+
for n in range(2):
|
690
|
+
EVc5_prm[i] += 6.0 * Y[j] * Y[k] * Y[l] * Y[m] * Y[n] * Eijklmn[i, j, k, l, m, n] * pow(Vcijklmn[i, j, k, l, m, n], 5.0)
|
691
|
+
|
692
|
+
|
693
|
+
for i in range(2):
|
694
|
+
for j in range(2):
|
695
|
+
for k in range(2):
|
696
|
+
for l in range(2):
|
697
|
+
for m in range(2):
|
698
|
+
for n in range(2):
|
699
|
+
EVc5 += Y[i] * Y[j] * Y[k] * Y[l] * Y[m] * Y[n] * Eijklmn[i, j, k, l, m, n] * pow(Vcijklmn[i, j, k, l, m, n], 5.0)
|
700
|
+
|
701
|
+
for i in range(2):
|
702
|
+
for j in range(2):
|
703
|
+
Fij[i, j] = pow((cbrt_calc(F[i]) + cbrt_calc(F[j]))/2, 3.0)
|
704
|
+
|
705
|
+
for i in range(2):
|
706
|
+
for j in range(2):
|
707
|
+
FVc2_prm[i] += 2.0 * Y[j] * Fij[i, j] * Vcij[i, j] * Vcij[i, j]
|
708
|
+
|
709
|
+
for i in range(2):
|
710
|
+
for j in range(2):
|
711
|
+
FVc2 += Y[i] * Y[j] * Fij[i, j] * Vcij[i, j] * Vcij[i, j]
|
712
|
+
|
713
|
+
bmix = Y[0] * b[0] + Y[1] * b[1]
|
714
|
+
|
715
|
+
b_prm[0] = b[0]
|
716
|
+
b_prm[1] = b[1]
|
717
|
+
|
718
|
+
for i in range(2):
|
719
|
+
for j in range(2):
|
720
|
+
for k in range(2):
|
721
|
+
k3 = 1.0 if i == j and j == k else k3_temperature
|
722
|
+
gijk[i, j, k] = pow((cbrt_calc(g[i]) + cbrt_calc(g[j]) + cbrt_calc(g[k]))/3, 3.0) * k3
|
723
|
+
|
724
|
+
for i in range(2):
|
725
|
+
for j in range(2):
|
726
|
+
for k in range(2):
|
727
|
+
gVc2_prm[i] += 3.0 * Y[j] * Y[k] * gijk[i, j, k] * Vcijk[i, j, k] * Vcijk[i, j, k]
|
728
|
+
|
729
|
+
|
730
|
+
for i in range(2):
|
731
|
+
for j in range(2):
|
732
|
+
for k in range(2):
|
733
|
+
gVc2 += Y[i] * Y[j] * Y[k] * gijk[i, j, k] * Vcijk[i, j, k] * Vcijk[i, j, k]
|
734
|
+
|
735
|
+
|
736
|
+
return Bij, Vcij, BVc_prm, BVc, Cijk, Vcijk, CVc2_prm, CVc2, Dijklm, Vcijklm, DVc4_prm, DVc4, Eijklmn, Vcijklmn, EVc5_prm, EVc5, Fij, FVc2_prm, FVc2, bmix, b_prm, gijk, gVc2_prm, gVc2
|
737
|
+
|
738
|
+
|
739
|
+
## Getting EOS contsants themselves
|
740
|
+
|
741
|
+
def get_EOS_params(P, TK):
|
742
|
+
""" This function returns the EOS 'constants' if you know the pressure and temperature
|
743
|
+
|
744
|
+
"""
|
745
|
+
|
746
|
+
a1 = np.zeros(16)
|
747
|
+
a2 = np.zeros(16)
|
748
|
+
b = np.zeros(2)
|
749
|
+
g = np.zeros(2)
|
750
|
+
Vc = np.zeros(1)
|
751
|
+
B = np.zeros(2)
|
752
|
+
C = np.zeros(2)
|
753
|
+
D = np.zeros(2)
|
754
|
+
E = np.zeros(2)
|
755
|
+
F = np.zeros(2)
|
756
|
+
V = np.zeros(2)
|
757
|
+
Vc = np.zeros(2)
|
758
|
+
|
759
|
+
|
760
|
+
|
761
|
+
|
762
|
+
|
763
|
+
|
764
|
+
|
765
|
+
|
766
|
+
# Initial guess for volumne
|
767
|
+
|
768
|
+
if P<=2000:
|
769
|
+
Vguess=1000
|
770
|
+
elif P>20000:
|
771
|
+
Vguess=10
|
772
|
+
else:
|
773
|
+
Vguess=100
|
774
|
+
|
775
|
+
|
776
|
+
if P <= 2000.0:
|
777
|
+
for i in range(16):
|
778
|
+
a1[i] = aL1[i]
|
779
|
+
a2[i] = aL2[i]
|
780
|
+
# These are the binary interaction parameters
|
781
|
+
k1_temperature = 3.131 - (5.0624 / 10**3.0) * TK + (1.8641 / 10**6) * TK**2 - 31.409 / TK
|
782
|
+
k2_temperature = -46.646 + (4.2877 / 10**2.0) * TK - (1.0892 / 10**5) * TK**2 + 1.5782 * 10**4 / TK
|
783
|
+
k3_temperature = 0.9
|
784
|
+
else:
|
785
|
+
for i in range(16):
|
786
|
+
a1[i] = aH1[i]
|
787
|
+
a2[i] = aH2[i]
|
788
|
+
# Same, but for higher pressures
|
789
|
+
k1_temperature = 9.034 - (7.9212 / 10**3) * TK + (2.3285 / 10**6) * TK**2 - 2.4221 * 10**3 / TK
|
790
|
+
k2_temperature = -1.068 + (1.8756 / 10**3) * TK - (4.9371 / 10**7) * TK**2 + 6.6180 * 10**2 / TK
|
791
|
+
k3_temperature = 1.0
|
792
|
+
|
793
|
+
b[0] = a1[14] # beta for H2O
|
794
|
+
b[1] = a2[14] # beta for CO2
|
795
|
+
g[0] = a1[15] # gamma for H2O
|
796
|
+
g[1] = a2[15] # gamma for CO2
|
797
|
+
|
798
|
+
Vc[0] = 83.14467 * Tc1 / Pc1
|
799
|
+
B[0] = a1[1] + a1[2] / ((TK / Tc1) * (TK / Tc1)) + a1[3] / ((TK / Tc1) * (TK / Tc1) * (TK / Tc1))
|
800
|
+
C[0] = a1[4] + a1[5] / ((TK / Tc1) * (TK / Tc1)) + a1[6] / ((TK / Tc1) * (TK / Tc1) * (TK / Tc1))
|
801
|
+
D[0] = a1[7] + a1[8] / ((TK / Tc1) * (TK / Tc1)) + a1[9] / ((TK / Tc1) * (TK / Tc1) * (TK / Tc1))
|
802
|
+
E[0] = a1[10] + a1[11] / ((TK / Tc1) * (TK / Tc1)) + a1[12] / ((TK / Tc1) * (TK / Tc1) * (TK / Tc1))
|
803
|
+
F[0] = a1[13] / ((TK / Tc1) * (TK / Tc1) * (TK / Tc1))
|
804
|
+
|
805
|
+
Vc[1] = 83.14467 * Tc2 / Pc2
|
806
|
+
B[1] = a2[1] + a2[2] / ((TK / Tc2) * (TK / Tc2)) + a2[3] / ((TK / Tc2) * (TK / Tc2) * (TK / Tc2))
|
807
|
+
C[1] = a2[4] + a2[5] / ((TK / Tc2) * (TK / Tc2)) + a2[6] / ((TK / Tc2) * (TK / Tc2) * (TK / Tc2))
|
808
|
+
D[1] = a2[7] + a2[8] / ((TK / Tc2) * (TK / Tc2)) + a2[9] / ((TK / Tc2) * (TK / Tc2) * (TK / Tc2))
|
809
|
+
E[1] = a2[10] + a2[11] / ((TK / Tc2) * (TK / Tc2)) + a2[12] / ((TK / Tc2) * (TK / Tc2) * (TK / Tc2))
|
810
|
+
F[1] = a2[13] / ((TK / Tc2) * (TK / Tc2) * (TK / Tc2))
|
811
|
+
return k1_temperature, k2_temperature, k3_temperature, a1, a2, g, b, Vc, B, C, D, E, F, Vguess
|
812
|
+
|
813
|
+
## Lets wrap all these functions up.
|
814
|
+
|
815
|
+
def calculate_molar_volume_ind_DZ2006(*, P_kbar, T_K, XH2O):
|
816
|
+
""" This function calculates molar volume for a known pressure, T in K and XH2O (mol frac) for a single value
|
817
|
+
"""
|
818
|
+
|
819
|
+
P=P_kbar*1000
|
820
|
+
TK=T_K
|
821
|
+
|
822
|
+
# Calculate the constants you neeed
|
823
|
+
k1_temperature, k2_temperature, k3_temperature, a1, a2, g, b, Vc, B, C, D, E, F, Vguess=get_EOS_params(P, TK)
|
824
|
+
|
825
|
+
if XH2O==0:
|
826
|
+
mol_vol=purevolume(1, Vguess, P, B, C, D, E, F, Vc, TK, b, g)
|
827
|
+
|
828
|
+
if XH2O==1:
|
829
|
+
mol_vol=purevolume(0, Vguess, P, B, C, D, E, F, Vc, TK, b, g)
|
830
|
+
|
831
|
+
else:
|
832
|
+
XCO2=1-XH2O
|
833
|
+
Y = [0] * 2
|
834
|
+
Y[0]=XH2O
|
835
|
+
Y[1]=XCO2
|
836
|
+
Bij, Vcij, BVc_prm, BVc, Cijk, Vcijk, CVc2_prm, CVc2, Dijklm, Vcijklm, DVc4_prm, DVc4, Eijklmn, Vcijklmn, EVc5_prm, EVc5, Fij, FVc2_prm, FVc2, bmix, b_prm, gijk, gVc2_prm, gVc2=mixing_rules(B, C,D, E, F, Vc, Y, b, g, k1_temperature, k2_temperature, k3_temperature)
|
837
|
+
|
838
|
+
|
839
|
+
mol_vol=mixvolume(Vguess, P, BVc, CVc2, DVc4, EVc5, FVc2, bmix, gVc2, T_K)
|
840
|
+
|
841
|
+
return mol_vol
|
842
|
+
|
843
|
+
|
844
|
+
def calculate_molar_volume_DZ2006(*, P_kbar, T_K, XH2O):
|
845
|
+
""" Used to calculate molar volume in a loop for multiple inputs
|
846
|
+
|
847
|
+
|
848
|
+
"""
|
849
|
+
|
850
|
+
P_kbar, T_K, XH2O=ensure_series(P_kbar, T_K, XH2O)
|
851
|
+
|
852
|
+
# Check all the same length
|
853
|
+
lengths = [len(P_kbar), len(T_K), len(XH2O)]
|
854
|
+
if len(set(lengths)) != 1:
|
855
|
+
raise ValueError("All input Pandas Series must have the same length.")
|
856
|
+
|
857
|
+
# Set up loop
|
858
|
+
mol_vol=np.empty(len(P_kbar), float)
|
859
|
+
|
860
|
+
for i in range(0, len(P_kbar)):
|
861
|
+
mol_vol[i]=calculate_molar_volume_ind_DZ2006(P_kbar=P_kbar.iloc[i].astype(float), T_K=T_K.iloc[i].astype(float), XH2O=XH2O.iloc[i].astype(float))
|
862
|
+
|
863
|
+
|
864
|
+
|
865
|
+
|
866
|
+
|
867
|
+
return mol_vol
|
868
|
+
|
869
|
+
def calculate_Pressure_ind_DZ2006(*, mol_vol, T_K, XH2O, Pguess=None):
|
870
|
+
""" This function calculates pressure for a known molar volume, T in K and XH2O (mol frac) for a single value
|
871
|
+
"""
|
872
|
+
V=mol_vol
|
873
|
+
if Pguess is None:
|
874
|
+
if V>1000:
|
875
|
+
Pguess=1000
|
876
|
+
elif V<10:
|
877
|
+
Pguess=20000
|
878
|
+
else:
|
879
|
+
Pguess=200
|
880
|
+
|
881
|
+
TK=T_K
|
882
|
+
|
883
|
+
# lets do for low pressure initially
|
884
|
+
|
885
|
+
|
886
|
+
if XH2O==0:
|
887
|
+
P=purepressure(1, V, Pguess, TK)
|
888
|
+
|
889
|
+
elif XH2O==1:
|
890
|
+
P=purepressure(0, V, Pguess, TK)
|
891
|
+
|
892
|
+
else:
|
893
|
+
XCO2=1-XH2O
|
894
|
+
Y = [0] * 2
|
895
|
+
Y[0]=XH2O
|
896
|
+
Y[1]=XCO2
|
897
|
+
|
898
|
+
P=mixpressure(Pguess, V, T_K, Y)
|
899
|
+
|
900
|
+
return P
|
901
|
+
|
902
|
+
def calculate_Pressure_DZ2006(*, mol_vol=None, density=None, T_K, XH2O):
|
903
|
+
""" Used to calculate molar volume in a loop for multiple inputs
|
904
|
+
|
905
|
+
|
906
|
+
"""
|
907
|
+
# Make all a panda series
|
908
|
+
|
909
|
+
|
910
|
+
|
911
|
+
if mol_vol is None and density is not None:
|
912
|
+
mol_vol=density_to_mol_vol(density=density, XH2O=XH2O)
|
913
|
+
|
914
|
+
mol_vol, T_K, XH2O=ensure_series(mol_vol, T_K, XH2O)
|
915
|
+
|
916
|
+
# Check all the same length
|
917
|
+
lengths = [len(mol_vol), len(T_K), len(XH2O)]
|
918
|
+
if len(set(lengths)) != 1:
|
919
|
+
raise ValueError("All input Pandas Series must have the same length.")
|
920
|
+
|
921
|
+
# Set up loop
|
922
|
+
P=np.empty(len(mol_vol), float)
|
923
|
+
|
924
|
+
for i in range(0, len(mol_vol)):
|
925
|
+
P[i]=calculate_Pressure_ind_DZ2006(mol_vol=mol_vol.iloc[i].astype(float), T_K=T_K.iloc[i].astype(float), XH2O=XH2O.iloc[i].astype(float))
|
926
|
+
|
927
|
+
|
928
|
+
|
929
|
+
return P
|
930
|
+
|
931
|
+
|
932
|
+
def mix_fugacity(*, P_kbar, T_K, XH2O, Vmix):
|
933
|
+
|
934
|
+
""" Used to calculate fugacity, compressability and activities for a panda series
|
935
|
+
|
936
|
+
"""
|
937
|
+
# Make everything a pandas series
|
938
|
+
|
939
|
+
P_kbar, T_K, XH2O, Vmix=ensure_series_4(P_kbar, T_K, XH2O, Vmix)
|
940
|
+
|
941
|
+
|
942
|
+
|
943
|
+
#Check all the same length
|
944
|
+
lengths = [len(P_kbar), len(T_K), len(XH2O), len(Vmix)]
|
945
|
+
if len(set(lengths)) != 1:
|
946
|
+
raise ValueError("All input Pandas Series must have the same length.")
|
947
|
+
|
948
|
+
f=np.empty(len(P_kbar), float)
|
949
|
+
a_CO2=np.empty(len(P_kbar), float)
|
950
|
+
a_H2O=np.empty(len(P_kbar), float)
|
951
|
+
f_CO2=np.empty(len(P_kbar), float)
|
952
|
+
f_H2O=np.empty(len(P_kbar), float)
|
953
|
+
Zmix=np.empty(len(P_kbar), float)
|
954
|
+
for i in range(0, len(P_kbar)):
|
955
|
+
|
956
|
+
f_H2O[i], f_CO2[i], a_H2O[i], a_CO2[i], Zmix[i]=mix_fugacity_ind(P_kbar=P_kbar.iloc[i].astype(float), T_K=T_K.iloc[i].astype(float), XH2O=XH2O.iloc[i].astype(float), Vmix=Vmix.iloc[i].astype(float))
|
957
|
+
|
958
|
+
return f_H2O, f_CO2, a_H2O,a_CO2, Zmix
|
959
|
+
|
960
|
+
|
961
|
+
def mol_vol_to_density(*, mol_vol, XH2O):
|
962
|
+
""" Converts molar mass to density for a given XH2O"""
|
963
|
+
density=((1-XH2O)*44 + (XH2O)*18)/mol_vol
|
964
|
+
return density
|
965
|
+
|
966
|
+
def density_to_mol_vol(*, density, XH2O):
|
967
|
+
""" Converts density in g/cm3 to molar volume for a given XH2O"""
|
968
|
+
mol_vol=((1-XH2O)*44 + (XH2O)*18)/density
|
969
|
+
return mol_vol
|
970
|
+
|
971
|
+
|
972
|
+
|
973
|
+
def calc_prop_knownP_EOS_DZ2006(*, P_kbar=1, T_K=1200, XH2O=1):
|
974
|
+
""" This function calculates molar volume, density, compressability factor, fugacity, and activity for mixed H2O-CO2 fluids
|
975
|
+
using the EOS of Span and Wanger. It assumes you know P, T, and XH2O.
|
976
|
+
|
977
|
+
Parameters
|
978
|
+
-------------------
|
979
|
+
P_kbar: float, np.array, pd.Series
|
980
|
+
Pressure in kbar
|
981
|
+
T_K: float, np.array, pd.Series
|
982
|
+
Temperature in Kelvin
|
983
|
+
XH2O: float, np.array, pd.Series
|
984
|
+
Molar fraction of H2O in the fluid phase.
|
985
|
+
|
986
|
+
Returns
|
987
|
+
-------------------
|
988
|
+
pd.DataFrame
|
989
|
+
|
990
|
+
"""
|
991
|
+
|
992
|
+
|
993
|
+
|
994
|
+
# First, check all pd Series
|
995
|
+
|
996
|
+
|
997
|
+
mol_vol=calculate_molar_volume_DZ2006(P_kbar=P_kbar, T_K=T_K, XH2O=XH2O)
|
998
|
+
|
999
|
+
|
1000
|
+
f_H2O, f_CO2, a_H2O, a_CO2, Zmix=mix_fugacity(P_kbar=P_kbar, T_K=T_K, XH2O=XH2O,
|
1001
|
+
Vmix=mol_vol)
|
1002
|
+
density=mol_vol_to_density(mol_vol=mol_vol, XH2O=XH2O)
|
1003
|
+
# 'T_K': T_K,
|
1004
|
+
# 'P_kbar': P_kbar,
|
1005
|
+
# 'XH2O': XH2O,
|
1006
|
+
#
|
1007
|
+
|
1008
|
+
|
1009
|
+
df=pd.DataFrame(data={'P_kbar': P_kbar,
|
1010
|
+
'T_K': T_K,
|
1011
|
+
'XH2O': XH2O,
|
1012
|
+
'XCO2': 1-XH2O,
|
1013
|
+
'Molar Volume (cm3/mol)': mol_vol,
|
1014
|
+
'Density (g/cm3)': density,
|
1015
|
+
'Compressability_factor': Zmix,
|
1016
|
+
'fugacity_H2O (kbar)': f_H2O,
|
1017
|
+
'fugacity_CO2 (kbar)': f_CO2,
|
1018
|
+
'activity_H2O': a_H2O,
|
1019
|
+
'activity_CO2': a_CO2})
|
1020
|
+
|
1021
|
+
return df
|
1022
|
+
|
1023
|
+
|
1024
|
+
|
1025
|
+
def calculate_entrapment_P_XH2O(*, XH2O, CO2_dens_gcm3, T_K):
|
1026
|
+
"""" This function calculates pressure for a measured CO$_2$ density, temperature and estimate of initial XH2O.
|
1027
|
+
It first corrects the density to obtain a bulk density for a CO2-H2O mix, assuming that H2O was lost from the inclusion.
|
1028
|
+
correcting for XH2O. It assumes that H2O has been lost from the inclusion (see Hansteen and Klugel, 2008 for method). It also calculates using other
|
1029
|
+
pure CO2 equation of states for comparison
|
1030
|
+
|
1031
|
+
Parameters
|
1032
|
+
----------------------
|
1033
|
+
XH2O: float, pd.Series.
|
1034
|
+
The molar fraction of H2O in the fluid. Should be between 0 and 1. Can get an estimate from say VESical.
|
1035
|
+
|
1036
|
+
CO2_dens_gcm3: float, pd.Series
|
1037
|
+
Measured CO2 density in g/cm3
|
1038
|
+
|
1039
|
+
T_K: float, pd.Series
|
1040
|
+
Temperature in Kelvin.
|
1041
|
+
|
1042
|
+
Returns
|
1043
|
+
-----------------------------
|
1044
|
+
pd.DataFrame:
|
1045
|
+
Columns showing:
|
1046
|
+
P_kbar_pureCO2_SW96: Pressure calculated for the measured CO$_2$ density using the pure CO2 EOS from Span and Wanger (1996)
|
1047
|
+
P_kbar_pureCO2_SP94: Pressure calculated for the measured CO$_2$ density using the pure CO2 EOS from Sterner and Pitzer (1994)
|
1048
|
+
P_kbar_pureCO2_DZ06: Pressure calculated from the measured CO$_2$ density using the pure CO2 EOs from Duan and Zhang (2006)
|
1049
|
+
P_kbar_mixCO2_DZ06: Pressure calculated from the reconstructed mixed fluid density using the mixed EOS from Duan and Zhang (2006)
|
1050
|
+
P Mix/P Pure DZ06: Correction factor - e.g. how much deeper the pressure is from the mixed EOS
|
1051
|
+
rho_mix_calc: Bulk density calculated (C+H) at time of entrapment
|
1052
|
+
CO2_dens_gcm3: Input CO2 density
|
1053
|
+
T_K: input temperature
|
1054
|
+
XH2O: input molar fraction of H2O
|
1055
|
+
|
1056
|
+
"""
|
1057
|
+
XH2O, rho_meas, T_K=ensure_series(a=XH2O, b=CO2_dens_gcm3, c=T_K)
|
1058
|
+
alpha=XH2O/(1-XH2O)
|
1059
|
+
# This gets the bulk density of the CO2-H2O fluid
|
1060
|
+
rho_orig=rho_meas*(1+alpha*(18/44))
|
1061
|
+
# Lets calculate the pressure using SW96
|
1062
|
+
P_SW=calculate_P_for_rho_T(T_K=T_K, CO2_dens_gcm3=rho_meas, EOS='SW96')
|
1063
|
+
P_SP=calculate_P_for_rho_T(T_K=T_K, CO2_dens_gcm3=rho_meas, EOS='SP94')
|
1064
|
+
# Same for DZ2006
|
1065
|
+
P_DZ=calculate_Pressure_DZ2006(density=rho_meas, T_K=T_K, XH2O=XH2O*0)
|
1066
|
+
# Now doing it with XH2O
|
1067
|
+
P_DZ_mix=calculate_Pressure_DZ2006(density=rho_orig, T_K=T_K, XH2O=XH2O)
|
1068
|
+
|
1069
|
+
df=pd.DataFrame(data={
|
1070
|
+
'P_kbar_pureCO2_SW96': P_SW['P_kbar'],
|
1071
|
+
'P_kbar_pureCO2_SP94': P_SW['P_kbar'],
|
1072
|
+
'P_kbar_pureCO2_DZ06': P_DZ/1000,
|
1073
|
+
'P_kbar_mixCO2_DZ06': P_DZ_mix/1000,
|
1074
|
+
'P Mix/P Pure DZ06': P_DZ_mix/P_DZ,
|
1075
|
+
'rho_mix_calc': rho_orig,
|
1076
|
+
'CO2_dens_gcm3': rho_meas,
|
1077
|
+
'T_K': T_K,
|
1078
|
+
'XH2O': XH2O})
|
1079
|
+
|
1080
|
+
return df
|
1081
|
+
|
1082
|
+
|
1083
|
+
|
1084
|
+
|
1085
|
+
|
1086
|
+
|
1087
|
+
|
1088
|
+
|