pyrestoolbox 2.1.4__tar.gz → 2.2.1__tar.gz

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.
Files changed (74) hide show
  1. {pyrestoolbox-2.1.4/pyrestoolbox.egg-info → pyrestoolbox-2.2.1}/PKG-INFO +1 -3
  2. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox/brine/brine.py +187 -65
  3. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox/classes/classes.py +2 -0
  4. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox/docs/brine.rst +102 -1
  5. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox/docs/changelist.rst +5 -0
  6. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox/gas/gas.py +310 -276
  7. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox/layer/layer.py +2 -13
  8. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox/library/library.py +3 -2
  9. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox/oil/oil.py +23 -40
  10. pyrestoolbox-2.2.1/pyrestoolbox/shared_fns/shared_fns.py +163 -0
  11. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox/simtools/simtools.py +20 -43
  12. pyrestoolbox-2.2.1/pyrestoolbox/tests/__init__.py +0 -0
  13. pyrestoolbox-2.2.1/pyrestoolbox/tests/run_all_tests.py +92 -0
  14. pyrestoolbox-2.2.1/pyrestoolbox/tests/test_brine.py +220 -0
  15. pyrestoolbox-2.2.1/pyrestoolbox/tests/test_gas.py +389 -0
  16. pyrestoolbox-2.2.1/pyrestoolbox/tests/test_layer.py +115 -0
  17. pyrestoolbox-2.2.1/pyrestoolbox/tests/test_oil.py +266 -0
  18. pyrestoolbox-2.2.1/pyrestoolbox/tests/test_simtools.py +175 -0
  19. pyrestoolbox-2.2.1/pyrestoolbox/validate/validate.py +22 -0
  20. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1/pyrestoolbox.egg-info}/PKG-INFO +1 -3
  21. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox.egg-info/SOURCES.txt +8 -15
  22. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox.egg-info/requires.txt +0 -2
  23. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/setup.cfg +1 -3
  24. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/setup.py +2 -4
  25. pyrestoolbox-2.1.4/pyrestoolbox/brine/__pycache__/__init__.cpython-39.pyc +0 -0
  26. pyrestoolbox-2.1.4/pyrestoolbox/brine/__pycache__/brine.cpython-39.pyc +0 -0
  27. pyrestoolbox-2.1.4/pyrestoolbox/classes/__pycache__/__init__.cpython-39.pyc +0 -0
  28. pyrestoolbox-2.1.4/pyrestoolbox/classes/__pycache__/classes.cpython-39.pyc +0 -0
  29. pyrestoolbox-2.1.4/pyrestoolbox/constants/__pycache__/__init__.cpython-39.pyc +0 -0
  30. pyrestoolbox-2.1.4/pyrestoolbox/constants/__pycache__/constants.cpython-39.pyc +0 -0
  31. pyrestoolbox-2.1.4/pyrestoolbox/gas/__pycache__/__init__.cpython-39.pyc +0 -0
  32. pyrestoolbox-2.1.4/pyrestoolbox/gas/__pycache__/gas.cpython-39.pyc +0 -0
  33. pyrestoolbox-2.1.4/pyrestoolbox/oil/__pycache__/__init__.cpython-39.pyc +0 -0
  34. pyrestoolbox-2.1.4/pyrestoolbox/oil/__pycache__/oil.cpython-39.pyc +0 -0
  35. pyrestoolbox-2.1.4/pyrestoolbox/shared_fns/__pycache__/__init__.cpython-39.pyc +0 -0
  36. pyrestoolbox-2.1.4/pyrestoolbox/shared_fns/__pycache__/shared_fns.cpython-39.pyc +0 -0
  37. pyrestoolbox-2.1.4/pyrestoolbox/shared_fns/shared_fns.py +0 -92
  38. pyrestoolbox-2.1.4/pyrestoolbox/validate/__pycache__/__init__.cpython-39.pyc +0 -0
  39. pyrestoolbox-2.1.4/pyrestoolbox/validate/__pycache__/validate.cpython-39.pyc +0 -0
  40. pyrestoolbox-2.1.4/pyrestoolbox/validate/validate.py +0 -16
  41. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/LICENSE +0 -0
  42. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/MANIFEST.in +0 -0
  43. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/README.md +0 -0
  44. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/README.rst +0 -0
  45. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyproject.toml +0 -0
  46. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox/__init__.py +0 -0
  47. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox/brine/__init__.py +0 -0
  48. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox/classes/__init__.py +0 -0
  49. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox/constants/__init__.py +0 -0
  50. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox/constants/constants.py +0 -0
  51. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox/docs/gas.rst +0 -0
  52. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox/docs/img/bot.png +0 -0
  53. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox/docs/img/bot_PVTO.png +0 -0
  54. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox/docs/img/bot_img.png +0 -0
  55. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox/docs/img/dry_gas.png +0 -0
  56. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox/docs/img/grid_sat_df.png +0 -0
  57. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox/docs/img/influence.png +0 -0
  58. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox/docs/img/properties_df.png +0 -0
  59. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox/docs/img/sgof.png +0 -0
  60. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox/docs/img/swof.png +0 -0
  61. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox/docs/layer.rst +0 -0
  62. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox/docs/library.rst +0 -0
  63. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox/docs/oil.rst +0 -0
  64. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox/docs/simtools.rst +0 -0
  65. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox/gas/__init__.py +0 -0
  66. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox/layer/__init__.py +0 -0
  67. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox/library/__init__.py +0 -0
  68. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox/library/component_library.xlsx +0 -0
  69. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox/oil/__init__.py +0 -0
  70. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox/shared_fns/__init__.py +0 -0
  71. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox/simtools/__init__.py +0 -0
  72. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox/validate/__init__.py +0 -0
  73. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox.egg-info/dependency_links.txt +0 -0
  74. {pyrestoolbox-2.1.4 → pyrestoolbox-2.2.1}/pyrestoolbox.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyrestoolbox
3
- Version: 2.1.4
3
+ Version: 2.2.1
4
4
  Summary: pyResToolbox - A collection of Reservoir Engineering Utilities
5
5
  Home-page: https://github.com/mwburgoyne/pyResToolbox
6
6
  Author: Mark W. Burgoyne
@@ -12,7 +12,6 @@ Classifier: License :: OSI Approved :: GNU General Public License v3 or later (G
12
12
  Classifier: Operating System :: OS Independent
13
13
  Description-Content-Type: text/markdown
14
14
  License-File: LICENSE
15
- Requires-Dist: requests
16
15
  Requires-Dist: numpy
17
16
  Requires-Dist: scipy
18
17
  Requires-Dist: pandas
@@ -20,7 +19,6 @@ Requires-Dist: tabulate
20
19
  Requires-Dist: gwr_inversion
21
20
  Requires-Dist: mpmath
22
21
  Requires-Dist: openpyxl
23
- Requires-Dist: setuptools
24
22
  Dynamic: author
25
23
  Dynamic: author-email
26
24
  Dynamic: description
@@ -21,25 +21,39 @@
21
21
  Contact author at mark.w.burgoyne@gmail.com
22
22
  """
23
23
 
24
- import sys
25
- from collections import Counter
26
- import glob
27
- from enum import Enum
28
- import pkg_resources
29
-
30
24
  import numpy as np
31
25
  import numpy.typing as npt
32
-
33
26
  import pandas as pd
27
+
28
+ from typing import Tuple
34
29
  from tabulate import tabulate
35
- from typing import Union, List, Tuple
36
30
 
37
31
  import pyrestoolbox.gas as gas # Needed for Z-Factor
38
32
  from pyrestoolbox.classes import z_method, c_method, pb_method, rs_method, bo_method, uo_method, deno_method, co_method, kr_family, kr_table, class_dic
39
- from pyrestoolbox.shared_fns import convert_to_numpy, process_output
33
+ from pyrestoolbox.shared_fns import convert_to_numpy, process_output, halley_solve_cubic
40
34
  from pyrestoolbox.validate import validate_methods
41
35
  from pyrestoolbox.constants import R, psc, tsc, degF2R, tscr, scf_per_mol, CUFTperBBL, WDEN, MW_CO2, MW_H2S, MW_N2, MW_AIR, MW_H2
42
36
 
37
+ def _Eq41(t, input_array):
38
+ """Eq 4.1 from McCain Petroleum Reservoir Fluid Properties"""
39
+ t2 = t / 100
40
+ return (
41
+ input_array[1] * t2 ** 2 + input_array[2] * t2 + input_array[3]
42
+ ) / (input_array[4] * t2 ** 2 + input_array[5] * t2 + 1)
43
+
44
+ # Spivey coefficient tables (shared by brine_props and brine_props_co2)
45
+ _RHOW_T70_ARR = [0, -0.127213, 0.645486, 1.03265, -0.070291, 0.639589]
46
+ _EWT_ARR = [0, 4.221, -3.478, 6.221, 0.5182, -0.4405]
47
+ _FWT_ARR = [0, -11.403, 29.932, 27.952, 0.20684, 0.3768]
48
+ _DM2T_ARR = [0, -0.00011149, 0.000175105, -0.00043766, 0, 0]
49
+ _DM32T_ARR = [0, -0.0008878, -0.0001388, -0.00296318, 0, 0.51103]
50
+ _DM1T_ARR = [0, 0.0021466, 0.012427, 0.042648, -0.081009, 0.525417]
51
+ _DM12T_ARR = [0, 0.0002356, -0.0003636, -0.0002278, 0, 0]
52
+ _EMT_ARR = [0, 0, 0, 0.1249, 0, 0]
53
+ _FM32T_ARR = [0, -0.617, -0.747, -0.4339, 0, 10.26]
54
+ _FM1T_ARR = [0, 0, 9.917, 5.1128, 0, 3.892]
55
+ _FM12T_ARR = [0, 0.0365, -0.0369, 0, 0, 0]
56
+
43
57
  def brine_props(p: float, degf: float, wt: float=0, ch4_sat: float=0) -> Tuple:
44
58
  """ Calculates Brine properties from modified Spivey Correlation per McCain Petroleum Reservoir Fluid Properties pg 160
45
59
  Returns Tuple of (Bw (rb/stb), Density (sg), viscosity (cP), Compressibility (1/psi), Rw GOR (scf/stb))
@@ -48,12 +62,7 @@ def brine_props(p: float, degf: float, wt: float=0, ch4_sat: float=0) -> Tuple:
48
62
  wt: Salt wt% (0-100)
49
63
  ch4_sat: Degree of methane saturation (0 - 1)
50
64
  """
51
-
52
- def Eq41(t, input_array): # From McCain Petroleum Reservoir Fluid Properties
53
- t2 = t / 100
54
- return (
55
- input_array[1] * t2 ** 2 + input_array[2] * t2 + input_array[3]
56
- ) / (input_array[4] * t2 ** 2 + input_array[5] * t2 + 1)
65
+ Eq41 = _Eq41
57
66
 
58
67
  Mpa = p * 0.00689476 # Pressure in mPa
59
68
  degc = (degf - 32) / 1.8 # Temperature in deg C
@@ -62,17 +71,17 @@ def brine_props(p: float, degf: float, wt: float=0, ch4_sat: float=0) -> Tuple:
62
71
  1000 * (wt / 100) / (58.4428 * (1 - (wt / 100)))
63
72
  ) # Molar concentration of NaCl from wt % in gram mol/kg water
64
73
 
65
- rhow_t70_arr = [0, -0.127213, 0.645486, 1.03265, -0.070291, 0.639589]
66
- Ewt_arr = [0, 4.221, -3.478, 6.221, 0.5182, -0.4405]
67
- Fwt_arr = [0, -11.403, 29.932, 27.952, 0.20684, 0.3768]
68
- Dm2t_arr = [0, -0.00011149, 0.000175105, -0.00043766, 0, 0]
69
- Dm32t_arr = [0, -0.0008878, -0.0001388, -0.00296318, 0, 0.51103]
70
- Dm1t_arr = [0, 0.0021466, 0.012427, 0.042648, -0.081009, 0.525417]
71
- Dm12t_arr = [0, 0.0002356, -0.0003636, -0.0002278, 0, 0]
72
- Emt_arr = [0, 0, 0, 0.1249, 0, 0]
73
- Fm32t_arr = [0, -0.617, -0.747, -0.4339, 0, 10.26]
74
- Fm1t_arr = [0, 0, 9.917, 5.1128, 0, 3.892]
75
- Fm12t_arr = [0, 0.0365, -0.0369, 0, 0, 0]
74
+ rhow_t70_arr = _RHOW_T70_ARR
75
+ Ewt_arr = _EWT_ARR
76
+ Fwt_arr = _FWT_ARR
77
+ Dm2t_arr = _DM2T_ARR
78
+ Dm32t_arr = _DM32T_ARR
79
+ Dm1t_arr = _DM1T_ARR
80
+ Dm12t_arr = _DM12T_ARR
81
+ Emt_arr = _EMT_ARR
82
+ Fm32t_arr = _FM32T_ARR
83
+ Fm1t_arr = _FM1T_ARR
84
+ Fm12t_arr = _FM12T_ARR
76
85
 
77
86
  rhow_t70 = Eq41(degc, rhow_t70_arr)
78
87
  Ewt = Eq41(degc, Ewt_arr)
@@ -171,7 +180,7 @@ def brine_props(p: float, degf: float, wt: float=0, ch4_sat: float=0) -> Tuple:
171
180
 
172
181
  try:
173
182
  mch4w = np.exp(A_t * np.power(np.log(Mpa - vap_pressure), 2) + B_t * np.log(Mpa - vap_pressure) + C_t) # Eq 4.15
174
- except:
183
+ except (ValueError, FloatingPointError):
175
184
  mch4w = 0
176
185
 
177
186
  u_arr = [
@@ -352,9 +361,9 @@ BBL2CUFT = 5.614583333 # cuft in a bbl
352
361
 
353
362
  #============================================================================
354
363
  # *** Mutual solubilities between CO2 and Brine - Calculated with ***
355
- # A Phase-Partitioning Model for CO2�Brine Mixtures at Elevated Temperatures
364
+ # A Phase-Partitioning Model for CO2�Brine Mixtures at Elevated Temperatures
356
365
  # and Pressures: Application to CO2-Enhanced Geothermal Systems
357
- # Nicolas Spycher & Karsten Pruess, Transp Porous Med (2010) 82:173�196
366
+ # Nicolas Spycher & Karsten Pruess, Transp Porous Med (2010) 82:173�196
358
367
  # DOI 10.1007/s11242-009-9425-y
359
368
  #============================================================================
360
369
 
@@ -712,31 +721,40 @@ class CO2_Brine_Mixture():
712
721
  # Cubic Polynomial Solver: f(Z) = Z**3 + E2*Z**2 + E1*Z + E1 = 0
713
722
  #=======================================================================
714
723
  self.repeat = False
715
- Z = np.roots(np.array([1.0, e2, e1, e0]))
716
- Z = np.array([x for x in Z if np.isreal(x)]) # Keep only real results
717
- if len(Z) > 1: # Evaluate which root to use per Eqs 25 and 26 in Spycher & Pruess (2003)
718
- vgas, vliq = max(Z), min(Z)
719
-
724
+
725
+ # Try Halley for all roots
726
+ roots = halley_solve_cubic(e2, e1, e0, flag=0)
727
+
728
+ # Fallback to np.roots if Halley returned None
729
+ if roots is None:
730
+ Z = np.roots(np.array([1.0, e2, e1, e0]))
731
+ Z = np.array([x for x in Z if np.isreal(x)]) # Keep only real results
732
+ roots = np.real(Z)
733
+
734
+ if len(roots) > 1: # Evaluate which root to use per Eqs 25 and 26 in Spycher & Pruess (2003)
735
+ vgas, vliq = max(roots), min(roots)
736
+
720
737
  w1 = self.pBar*(vgas - vliq)
721
738
  w2 = RGASCON * self.tKel * np.log((vgas - self.bMix)/(vliq - self.bMix)) + self.aMix/(self.tKel**0.5 * self.bMix) * np.log((vgas + self.bMix) * vliq / ((vliq + self.bMix) * vgas))
722
-
739
+
723
740
  if w2 - w1 > 0:
724
- Z[0] = max(Z)
741
+ result = max(roots)
725
742
  if self.CO2_sat: # CO2 was saturated in previous iteration, but now its not
726
743
  self.CO2_sat = False
727
744
  self.repeat = True
728
745
  else:
729
- Z[0] = min(Z)
746
+ result = min(roots)
730
747
  if not self.CO2_sat:
731
748
  self.CO2_sat = True
732
749
  self.repeat = True
733
750
  else:
751
+ result = roots[0]
734
752
  if self.CO2_sat: # CO2 was saturated in previous iteration, but now its not
735
753
  self.CO2_sat = False
736
754
  self.repeat = True
737
-
738
-
739
- return np.real(Z[0])
755
+
756
+
757
+ return np.real(result)
740
758
 
741
759
 
742
760
  def MolarVolume(self):
@@ -852,9 +870,9 @@ class CO2_Brine_Mixture():
852
870
  self.Bprime = B
853
871
 
854
872
  #============================================================================
855
- # A Phase-Partitioning Model for CO2�Brine Mixtures at Elevated Temperatures
873
+ # A Phase-Partitioning Model for CO2�Brine Mixtures at Elevated Temperatures
856
874
  # and Pressures: Application to CO2-Enhanced Geothermal Systems
857
- # Nicolas Spycher & Karsten Pruess, Transp Porous Med (2010) 82:173�196
875
+ # Nicolas Spycher & Karsten Pruess, Transp Porous Med (2010) 82:173�196
858
876
  # DOI 10.1007/s11242-009-9425-y
859
877
  #============================================================================
860
878
 
@@ -1069,26 +1087,18 @@ class CO2_Brine_Mixture():
1069
1087
  Mpa = pBar * 0.1 # Pressure in mPa
1070
1088
  tKel = degc + CEL2KEL # Temperature in deg K
1071
1089
 
1072
- def Eq41(t, input_array): # From McCain Petroleum Reservoir Fluid Properties
1073
- t2 = t / 100
1074
- return (input_array[1] * t2 ** 2 + input_array[2] * t2 + input_array[3]) / (input_array[4] * t2 ** 2 + input_array[5] * t2 + 1)
1075
-
1076
- # Table 4-6 Coefficients
1077
- rhow_t70_arr = [0, -0.127213, 0.645486, 1.03265, -0.070291, 0.639589]
1078
- Ewt_arr = [0, 4.221, -3.478, 6.221, 0.5182, -0.4405]
1079
- Fwt_arr = [0, -11.403, 29.932, 27.952, 0.20684, 0.3768]
1080
-
1081
- # Table 4-7 Coefficients
1082
- Dm2t_arr = [0, -0.00011149, 0.000175105, -0.00043766, 0, 0]
1083
- Dm32t_arr = [0, -0.0008878, -0.0001388, -0.00296318, 0, 0.51103]
1084
- Dm1t_arr = [0, 0.0021466, 0.012427, 0.042648, -0.081009, 0.525417]
1085
- Dm12t_arr = [0, 0.0002356, -0.0003636, -0.0002278, 0, 0]
1086
-
1087
- # Table 4-8 Coefficients
1088
- Emt_arr = [0, 0, 0, 0.1249, 0, 0]
1089
- Fm32t_arr = [0, -0.617, -0.747, -0.4339, 0, 10.26]
1090
- Fm1t_arr = [0, 0, 9.917, 5.1128, 0, 3.892]
1091
- Fm12t_arr = [0, 0.0365, -0.0369, 0, 0, 0]
1090
+ Eq41 = _Eq41
1091
+ rhow_t70_arr = _RHOW_T70_ARR
1092
+ Ewt_arr = _EWT_ARR
1093
+ Fwt_arr = _FWT_ARR
1094
+ Dm2t_arr = _DM2T_ARR
1095
+ Dm32t_arr = _DM32T_ARR
1096
+ Dm1t_arr = _DM1T_ARR
1097
+ Dm12t_arr = _DM12T_ARR
1098
+ Emt_arr = _EMT_ARR
1099
+ Fm32t_arr = _FM32T_ARR
1100
+ Fm1t_arr = _FM1T_ARR
1101
+ Fm12t_arr = _FM12T_ARR
1092
1102
 
1093
1103
  # Table 4-14 Mao-Duan Coefficients
1094
1104
  d = [0, 2885310, -11072.577, -9.0834095, 0.030925651, -0.0000274071, -1928385.1, 5621.6046, 13.82725, -0.047609523, 0.000035545041]
@@ -1115,7 +1125,7 @@ class CO2_Brine_Mixture():
1115
1125
 
1116
1126
  # -- CO2-Free Brine Density (gm/cm3)
1117
1127
  def brine_denw(Mpa):
1118
- # cw(T, p), in MPa�1, of pure water at temperature T and pressure p,
1128
+ # cw(T, p), in MPa�1, of pure water at temperature T and pressure p,
1119
1129
  cwtp = (1 / 70) * (1 / (Ewt * (Mpa / 70) + Fwt)) # Eq 4.2
1120
1130
 
1121
1131
  #Density of pure water at temperature T and pressure p.
@@ -1168,7 +1178,7 @@ class CO2_Brine_Mixture():
1168
1178
  return (1.0 + mRat * xRat) / (vPhi * xRat / MwB + 1.0 / rhoBRnoCO2) # --Equation 18 of Garcia paper
1169
1179
 
1170
1180
  # Correct CO2 free brine viscosity for dissolved CO2
1171
- # Using approach from "Viscosity Models and Effects of Dissolved CO2", Akand W. Islam and Eric S. Carlson (Jul 2012), Energy Fuels 2012, 26, 8, 5330�5336, https://doi.org/10.1021/ef3006228
1181
+ # Using approach from "Viscosity Models and Effects of Dissolved CO2", Akand W. Islam and Eric S. Carlson (Jul 2012), Energy Fuels 2012, 26, 8, 5330�5336, https://doi.org/10.1021/ef3006228
1172
1182
  def co2_vis_brine(cP_brine, xCO2):
1173
1183
  # Uses CO2 free brine viscosity (cP) and mole fraction CO2 in brine (xCO2), and returns cP
1174
1184
  return cP_brine * (1 + 4.65 * xCO2**1.0134)
@@ -1237,4 +1247,116 @@ class CO2_Brine_Mixture():
1237
1247
  # Undersaturated compressibility = 1/V dV/dP
1238
1248
  c_usat = 1 - sg_CO2_Brine / sg_CO2_Brine_ # 1/Bar
1239
1249
 
1240
- return ([sg_CO2_Brine, sg_brine, rhowtp], [cP_CO2_brine, cP_brine, cP_freshwater], viscosblty, [bw, brine_res_vol, bw_freshwater], rs, c_usat)
1250
+ return ([sg_CO2_Brine, sg_brine, rhowtp], [cP_CO2_brine, cP_brine, cP_freshwater], viscosblty, [bw, brine_res_vol, bw_freshwater], rs, c_usat)
1251
+
1252
+
1253
+ def make_pvtw_table(
1254
+ pi: float,
1255
+ degf: float,
1256
+ wt: float = 0,
1257
+ ch4_sat: float = 0,
1258
+ pmin: float = 500,
1259
+ pmax: float = 10000,
1260
+ nrows: int = 20,
1261
+ export: bool = False,
1262
+ ) -> dict:
1263
+ """ Generates a PVTW (water PVT) table over a pressure range using brine_props (Spivey correlation).
1264
+ Follows the pattern of make_bot_og from the oil module.
1265
+
1266
+ pi: Initial (reference) pressure (psia)
1267
+ degf: Temperature (deg F)
1268
+ wt: Salt wt% (0-100), default 0
1269
+ ch4_sat: Degree of methane saturation (0 - 1), default 0
1270
+ pmin: Minimum pressure for table (psia), default 500
1271
+ pmax: Maximum pressure for table (psia), default 10000
1272
+ nrows: Number of rows in table, default 20
1273
+ export: If True, writes PVTW.INC (ECLIPSE keyword) and pvtw_table.xlsx
1274
+
1275
+ Returns dict with keys:
1276
+ table: pandas DataFrame with columns Pressure, Bw, Density, Viscosity, Cw, Rsw
1277
+ pref: Reference pressure (psia)
1278
+ bw_ref: Bw at reference pressure (rb/stb)
1279
+ cw_ref: Compressibility at reference pressure (1/psi)
1280
+ visw_ref: Viscosity at reference pressure (cP)
1281
+ rsw_ref: Rsw at reference pressure (scf/stb)
1282
+ den_ref: Density (sg) at reference pressure
1283
+ """
1284
+ # Build pressure grid, ensuring pi is included
1285
+ pressures = list(np.linspace(pmin, pmax, nrows))
1286
+ if pi not in pressures:
1287
+ pressures.append(pi)
1288
+ pressures = sorted(set(pressures))
1289
+
1290
+ bws, dens, visws, cws, rsws = [], [], [], [], []
1291
+ for p in pressures:
1292
+ bw, lden, visw, cw, rsw = brine_props(p=p, degf=degf, wt=wt, ch4_sat=ch4_sat)
1293
+ bws.append(bw)
1294
+ dens.append(lden)
1295
+ visws.append(visw)
1296
+ cws.append(cw)
1297
+ rsws.append(rsw)
1298
+
1299
+ df = pd.DataFrame()
1300
+ df["Pressure (psia)"] = pressures
1301
+ df["Bw (rb/stb)"] = bws
1302
+ df["Density (sg)"] = dens
1303
+ df["Viscosity (cP)"] = visws
1304
+ df["Cw (1/psi)"] = cws
1305
+ df["Rsw (scf/stb)"] = rsws
1306
+
1307
+ # Reference properties at pi
1308
+ bw_ref, den_ref, visw_ref, cw_ref, rsw_ref = brine_props(
1309
+ p=pi, degf=degf, wt=wt, ch4_sat=ch4_sat
1310
+ )
1311
+
1312
+ if export:
1313
+ # Write ECLIPSE PVTW keyword
1314
+ # PVTW format: Pref Bw Cw Visw Viscosibility
1315
+ # Viscosibility set to 0 (constant viscosity assumption)
1316
+ pvtw_line = f" {pi:.1f} {bw_ref:.6f} {cw_ref:.6e} {visw_ref:.4f} 0.0"
1317
+ fileout = f"-- Generated by pyResToolbox make_pvtw_table\n"
1318
+ fileout += f"-- Temperature: {degf:.1f} deg F, Salt: {wt:.1f} wt%, CH4 sat: {ch4_sat:.2f}\n"
1319
+ fileout += f"PVTW\n{pvtw_line} /\n"
1320
+ with open("PVTW.INC", "w") as f:
1321
+ f.write(fileout)
1322
+
1323
+ # Write full table to Excel
1324
+ df.to_excel("pvtw_table.xlsx", index=False)
1325
+
1326
+ return {
1327
+ "table": df,
1328
+ "pref": pi,
1329
+ "bw_ref": bw_ref,
1330
+ "cw_ref": cw_ref,
1331
+ "visw_ref": visw_ref,
1332
+ "rsw_ref": rsw_ref,
1333
+ "den_ref": den_ref,
1334
+ }
1335
+
1336
+
1337
+ class SoreideWhitson:
1338
+ """ Soreide-Whitson (1992) model for gas solubility in water/brine.
1339
+
1340
+ Planned support for multicomponent gas mixtures containing:
1341
+ C1, C2, C3, nC4, CO2, H2S, N2, H2
1342
+
1343
+ Will calculate:
1344
+ - Mole fraction of dissolved gas components in aqueous phase
1345
+ - Mole fraction of vaporised water in gas phase
1346
+ - Water content of gas (stb/mmscf)
1347
+ - Gas solubility in water (scf/stb)
1348
+
1349
+ Supports fresh and saline water (NaCl equivalent).
1350
+
1351
+ Reference:
1352
+ Soreide, I. and Whitson, C.H., "Peng-Robinson Predictions for Hydrocarbons,
1353
+ CO2, N2, and H2S with Pure Water and NaCl Brine", Fluid Phase Equilibria,
1354
+ 77, 217-240, 1992.
1355
+ """
1356
+
1357
+ def __init__(self, **kwargs):
1358
+ raise NotImplementedError(
1359
+ "SoreideWhitson model is not yet implemented. "
1360
+ "Planned support includes multicomponent gas (C1, C2, C3, nC4, CO2, H2S, N2, H2) "
1361
+ "solubility in fresh/saline water using the Soreide-Whitson (1992) PR-EOS approach."
1362
+ )
@@ -28,11 +28,13 @@ class z_method(Enum): # Gas Z-Factor calculation model
28
28
  HY = 1
29
29
  WYW = 2
30
30
  BUR = 3
31
+ BNS = 3
31
32
 
32
33
  class c_method(Enum): # Gas critical properties calculation method
33
34
  PMC = 0
34
35
  SUT = 1
35
36
  BUR = 2
37
+ BNS = 3
36
38
 
37
39
  class pb_method(Enum): # Bubble point calculation method
38
40
  STAN = 0
@@ -161,5 +161,106 @@ Usage example for 175 Bara x 85 degC and 0% NaCl brine:
161
161
 
162
162
  >>> mix = brine.CO2_Brine_Mixture(pres = 175, temp = 85)
163
163
  >>> mix.Rs # Returns sm3 dissolved CO2 / sm3 Brine
164
- 24.742923469934272
164
+ 24.742923469934272
165
+
166
+ pyrestoolbox.brine.make_pvtw_table
167
+ ======================
168
+
169
+ .. code-block:: python
170
+
171
+ make_pvtw_table(pi, degf, wt=0, ch4_sat=0, pmin=500, pmax=10000, nrows=20, export=False) -> dict
172
+
173
+ Generates a PVTW (water PVT) table over a pressure range using the Spivey correlation (brine_props).
174
+ Optionally exports ECLIPSE PVTW keyword file and Excel spreadsheet.
175
+
176
+ .. list-table:: Inputs
177
+ :widths: 10 15 40
178
+ :header-rows: 1
179
+
180
+ * - Parameter
181
+ - Type
182
+ - Description
183
+ * - pi
184
+ - float
185
+ - Initial (reference) pressure (psia)
186
+ * - degf
187
+ - float
188
+ - Temperature (deg F)
189
+ * - wt
190
+ - float
191
+ - Salt weight% in the brine (0 - 100). Default 0
192
+ * - ch4_sat
193
+ - float
194
+ - Degree of methane saturation (0 - 1). Default 0
195
+ * - pmin
196
+ - float
197
+ - Minimum table pressure (psia). Default 500
198
+ * - pmax
199
+ - float
200
+ - Maximum table pressure (psia). Default 10000
201
+ * - nrows
202
+ - int
203
+ - Number of table rows. Default 20
204
+ * - export
205
+ - bool
206
+ - If True, writes PVTW.INC and pvtw_table.xlsx. Default False
207
+
208
+ .. list-table:: Return dict keys
209
+ :widths: 10 15 40
210
+ :header-rows: 1
211
+
212
+ * - Key
213
+ - Type
214
+ - Description
215
+ * - table
216
+ - DataFrame
217
+ - Pressure, Bw, Density, Viscosity, Cw, Rsw
218
+ * - pref
219
+ - float
220
+ - Reference pressure (psia)
221
+ * - bw_ref
222
+ - float
223
+ - Bw at reference pressure (rb/stb)
224
+ * - cw_ref
225
+ - float
226
+ - Compressibility at reference pressure (1/psi)
227
+ * - visw_ref
228
+ - float
229
+ - Viscosity at reference pressure (cP)
230
+ * - rsw_ref
231
+ - float
232
+ - Rsw at reference pressure (scf/stb)
233
+ * - den_ref
234
+ - float
235
+ - Density (sg) at reference pressure
236
+
237
+ Examples:
238
+
239
+ .. code-block:: python
240
+
241
+ >>> from pyrestoolbox import brine
242
+ >>> result = brine.make_pvtw_table(pi=3000, degf=200, wt=0, ch4_sat=0)
243
+ >>> print(result['bw_ref'])
244
+ >>> print(result['table'].head())
245
+
246
+ pyrestoolbox.brine.SoreideWhitson
247
+ ======================
248
+
249
+ .. code-block:: python
250
+
251
+ SoreideWhitson(**kwargs) -> raises NotImplementedError
252
+
253
+ Placeholder for the Soreide-Whitson (1992) PR-EOS model for gas solubility in water/brine.
254
+
255
+ Planned support includes multicomponent gas mixtures (C1, C2, C3, nC4, CO2, H2S, N2, H2)
256
+ solubility in fresh and saline water.
257
+
258
+ Planned return values:
259
+ - Mole fraction of dissolved gas components in aqueous phase
260
+ - Mole fraction of vaporised water in gas phase
261
+ - Water content of gas (stb/mmscf)
262
+ - Gas solubility in water (scf/stb)
263
+
264
+ Reference: Soreide, I. and Whitson, C.H., "Peng-Robinson Predictions for Hydrocarbons,
265
+ CO2, N2, and H2S with Pure Water and NaCl Brine", Fluid Phase Equilibria, 77, 217-240, 1992.
165
266
 
@@ -1,3 +1,8 @@
1
+ Changelist in 2.2:
2
+
3
+ - Bugfixes.
4
+
5
+
1
6
  Changelist in 2.1.3:
2
7
 
3
8
  - Updated viscosity parameters for BUR method.