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_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
+