pyrestoolbox 2.2.1__tar.gz → 2.2.2__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 (59) hide show
  1. {pyrestoolbox-2.2.1/pyrestoolbox.egg-info → pyrestoolbox-2.2.2}/PKG-INFO +1 -1
  2. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/docs/gas.rst +3 -19
  3. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/docs/library.rst +2 -2
  4. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/docs/oil.rst +14 -15
  5. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/docs/simtools.rst +17 -13
  6. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/library/library.py +10 -2
  7. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/tests/run_all_tests.py +1 -0
  8. pyrestoolbox-2.2.2/pyrestoolbox/tests/test_doc_examples.py +556 -0
  9. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2/pyrestoolbox.egg-info}/PKG-INFO +1 -1
  10. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox.egg-info/SOURCES.txt +1 -0
  11. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/setup.cfg +1 -1
  12. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/setup.py +1 -1
  13. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/LICENSE +0 -0
  14. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/MANIFEST.in +0 -0
  15. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/README.md +0 -0
  16. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/README.rst +0 -0
  17. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyproject.toml +0 -0
  18. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/__init__.py +0 -0
  19. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/brine/__init__.py +0 -0
  20. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/brine/brine.py +0 -0
  21. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/classes/__init__.py +0 -0
  22. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/classes/classes.py +0 -0
  23. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/constants/__init__.py +0 -0
  24. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/constants/constants.py +0 -0
  25. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/docs/brine.rst +0 -0
  26. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/docs/changelist.rst +0 -0
  27. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/docs/img/bot.png +0 -0
  28. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/docs/img/bot_PVTO.png +0 -0
  29. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/docs/img/bot_img.png +0 -0
  30. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/docs/img/dry_gas.png +0 -0
  31. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/docs/img/grid_sat_df.png +0 -0
  32. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/docs/img/influence.png +0 -0
  33. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/docs/img/properties_df.png +0 -0
  34. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/docs/img/sgof.png +0 -0
  35. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/docs/img/swof.png +0 -0
  36. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/docs/layer.rst +0 -0
  37. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/gas/__init__.py +0 -0
  38. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/gas/gas.py +0 -0
  39. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/layer/__init__.py +0 -0
  40. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/layer/layer.py +0 -0
  41. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/library/__init__.py +0 -0
  42. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/library/component_library.xlsx +0 -0
  43. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/oil/__init__.py +0 -0
  44. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/oil/oil.py +0 -0
  45. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/shared_fns/__init__.py +0 -0
  46. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/shared_fns/shared_fns.py +0 -0
  47. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/simtools/__init__.py +0 -0
  48. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/simtools/simtools.py +0 -0
  49. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/tests/__init__.py +0 -0
  50. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/tests/test_brine.py +0 -0
  51. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/tests/test_gas.py +0 -0
  52. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/tests/test_layer.py +0 -0
  53. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/tests/test_oil.py +0 -0
  54. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/tests/test_simtools.py +0 -0
  55. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/validate/__init__.py +0 -0
  56. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox/validate/validate.py +0 -0
  57. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox.egg-info/dependency_links.txt +0 -0
  58. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/pyrestoolbox.egg-info/requires.txt +0 -0
  59. {pyrestoolbox-2.2.1 → pyrestoolbox-2.2.2}/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.2.1
3
+ Version: 2.2.2
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
@@ -210,22 +210,7 @@ Examples:
210
210
 
211
211
  >>> gas.gas_z(p=[1000, 2000], sg=0.75, degf=160, cmethod='SUT', n2 = 0.02, co2 = 0.17)
212
212
  array([0.91920553, 0.87196032])
213
-
214
- def gas_ug(
215
- p: npt.ArrayLike,
216
- sg: float,
217
- degf: float,
218
- zmethod: z_method = z_method.DAK,
219
- cmethod: c_method = c_method.PMC,
220
- co2: float = 0,
221
- h2s: float = 0,
222
- n2: float = 0,
223
- h2: float = 0,
224
- tc: float = 0,
225
- pc: float = 0,
226
- zee: float = 0,
227
- ugz = False
228
-
213
+
229
214
  pyrestoolbox.gas.gas_ug
230
215
  ===================
231
216
 
@@ -741,7 +726,7 @@ pyrestoolbox.gas.gas_rate_radial
741
726
  gas_rate_radial(k, h, pr, pwf, r_w, r_ext, degf, zmethod='DAK, cmethod='PMC', S = 0, D = 0, sg = 0.75, n2 = 0, co2 = 0, h2s = 0, tc = 0, pc = 0) -> float or np.array
742
727
 
743
728
  Returns gas rate (mscf/day) for radial flow using Darcy pseudo steady state equation & gas pseudopressure.
744
- Arrays can be used for any one of k, h, pr or pwf, returning corresponding 1-D array of rates. Using more than one input array � while not prohibited - will not return expected results
729
+ Arrays can be used for any one of k, h, pr or pwf, returning corresponding 1-D array of rates. Using more than one input array � while not prohibited - will not return expected results
745
730
 
746
731
  .. list-table:: Inputs
747
732
  :widths: 10 15 40
@@ -806,7 +791,6 @@ Examples:
806
791
 
807
792
  .. code-block:: python
808
793
 
809
- >>> from pyrestoolbox import pyrestoolbox as rtb
810
794
  >>> gas.gas_rate_radial(k=5, h=50, pr=2000, pwf=750, r_w=0.3, r_ext=1500, degf=180, sg = 0.75, D = 0.01, S=5)
811
795
  10269.669190157822
812
796
 
@@ -822,7 +806,7 @@ pyrestoolbox.gas.gas_rate_linear
822
806
  gas_rate_linear(k, pr, pwf, area, length, degf, zmethod='DAK, cmethod='PMC', sg = 0.75, n2 = 0, co2 = 0, h2s = 0, tc = 0, pc = 0) -> float or np.array
823
807
 
824
808
  Returns gas rate (mscf/day) for linear flow using Darcy steady state equation & gas pseudopressure.
825
- Arrays can be used for any one of k, pr, pwf or area, returning corresponding 1-D array of rates. Using more than one input array � while not prohibited - will not return expected results
809
+ Arrays can be used for any one of k, pr, pwf or area, returning corresponding 1-D array of rates. Using more than one input array � while not prohibited - will not return expected results
826
810
 
827
811
 
828
812
  .. list-table:: Inputs
@@ -65,7 +65,7 @@ Examples:
65
65
 
66
66
  .. code-block:: python
67
67
 
68
- >>> library.prop(comp = 'C3', prop = 'VTran'), rtb.comp_library.prop(comp = 'C3', prop = 'VTran', model='SRK')
68
+ >>> library.prop(comp = 'C3', prop = 'VTran'), library.prop(comp = 'C3', prop = 'VTran', model='SRK')
69
69
  (-0.06381, 0.09075)
70
70
 
71
71
 
@@ -158,5 +158,5 @@ Example:
158
158
 
159
159
  >>> library.df
160
160
 
161
- .. image:: https://github.com/mwburgoyne/pyResToolbox/blob/main/docs/img/properties_df.png
161
+ .. image:: img/properties_df.png
162
162
  :alt: DataFrame of Component Library data
@@ -57,7 +57,7 @@ pyResToolBox uses class objects to track calculation options through the functio
57
57
  - Method for calculating oil formation volume factor. Defaults to 'MCAIN'
58
58
  Options are:
59
59
  + 'STAN': Standing Correlation
60
- + 'MCAIN': McCain approach, calculating from densities � Default
60
+ + 'MCAIN': McCain approach, calculating from densities � Default
61
61
 
62
62
  Users can specify which calculation method to use either by passing an option string, or a class object to any given function. The implementation of class objects should make it easier to program in an IDE that supports type hinting
63
63
 
@@ -148,7 +148,7 @@ Examples:
148
148
 
149
149
  .. code-block:: python
150
150
 
151
- >>> oil.ja_sg(mw=150, ja=0.5)
151
+ >>> oil.oil_ja_sg(mw=150, ja=0.5)
152
152
  0.8583666666666667
153
153
 
154
154
  pyrestoolbox.oil.oil_twu_props
@@ -239,7 +239,7 @@ pyrestoolbox.oil.oil_pbub
239
239
 
240
240
  .. code-block:: python
241
241
 
242
- pbub(api, degf, rsb, sg_g =0, sg_sp =0, pbmethod ='VALMC') -> float
242
+ oil_pbub(api, degf, rsb, sg_g =0, sg_sp =0, pbmethod ='VALMC') -> float
243
243
 
244
244
  Returns bubble point pressure (psia) calculated with different correlations.
245
245
  At least one of sg_g and sg_sp must be supplied. This function will make simple assumption to estimate missing gas sg if only one is provided.
@@ -344,15 +344,15 @@ Returns solution gas oil ratio (scf/stb) calculated with different correlations.
344
344
  * - degf
345
345
  - float
346
346
  - Oil Temperature (deg F)
347
- * - sg_g
347
+ * - sg_sp
348
348
  - float
349
- - Weighted average specific gravity of surface gas, inclusive of gas evolved after separation (relative to air).
349
+ - Separator gas gravity (relative to air).
350
350
  * - p
351
351
  - float
352
- - Pressure (psia).
352
+ - Pressure (psia).
353
353
  * - pb
354
354
  - float
355
- - Original bubble point pressure (psia)
355
+ - Original bubble point pressure (psia)
356
356
  * - rsb
357
357
  - float
358
358
  - Original solution GOR at original bubble point pressure (scf/stb)
@@ -367,7 +367,7 @@ Examples:
367
367
 
368
368
  .. code-block:: python
369
369
 
370
- >>> oil.oil_rs(api = 43, degf = 185, sg_sp =0 .72, p = 3000, pb = 5179.5, rsb = 2370)
370
+ >>> oil.oil_rs(api = 43, degf = 185, sg_sp=0.72, p = 3000, pb = 5179.5, rsb = 2370)
371
371
  1017.9424240354475
372
372
 
373
373
  >>> oil.oil_rs(api=43, degf=185, sg_sp=0.72, p=3000, rsb =2370)
@@ -610,7 +610,7 @@ pyrestoolbox.oil.make_bot_og
610
610
 
611
611
  .. code-block:: python
612
612
 
613
- make_bot_og(pi, api, degf, sg_g, pmax, pb =0, rsb =0, pmin =14.7, nrows = 20, wt =0, ch4_sat =0, comethod='EXPLT', zmethod='DAK', rsmethod='VELAR', cmethod'PMC', denomethod='SWMH', bomethod='MCAIN', pbmethod='VALMC', export=False) -> tuple
613
+ make_bot_og(pi, api, degf, sg_g, pmax, pb =0, rsb =0, pmin =14.7, nrows = 20, wt =0, ch4_sat =0, comethod='EXPLT', zmethod='DAK', rsmethod='VELAR', cmethod='PMC', denomethod='SWMH', bomethod='MCAIN', pbmethod='VALMC', export=False) -> tuple
614
614
 
615
615
  Creates data required for Oil-Gas-Water black oil tables. Returns dictionary of results, with index:
616
616
  - bot: Pandas table of blackoil data (for PVTO == False), or Saturated properties to pmax (if PVTO == True)
@@ -709,7 +709,7 @@ Examples:
709
709
  >>> results = oil.make_bot_og(pvto=False, pi=4000, api=38, degf=175, sg_g=0.68, pmax=5500, pb=4500, nrows=10, export=True)
710
710
  >>> df, st_deno, st_deng, res_denw, res_cw, visw, pb, rsb, rsb_frac, usat = results['bot'], results['deno'], results['deng'], results['denw'], results['cw'], results['uw'], results['pb'], results['rsb'], results['rsb_scale'], results['usat']
711
711
  >>> df
712
- .. image:: https://github.com/mwburgoyne/pyResToolbox/blob/main/docs/img/bot_img.png
712
+ .. image:: img/bot_img.png
713
713
  :alt: Black Oil Table DataFrame
714
714
 
715
715
  pyrestoolbox.oil.sg_evolved_gas
@@ -749,9 +749,8 @@ Examples:
749
749
 
750
750
  .. code-block:: python
751
751
 
752
- >>> oil.sg_st_gas(114.7, rsp=1500, api=42, sg_sp=0.72, degf_sp=80)
753
- 1.1923932340625523
754
-
752
+ >>> oil.sg_evolved_gas(p=2000, degf=185, rsb=2370, api=43, sg_sp=0.72)
753
+ 0.7872810977386344
755
754
 
756
755
  pyrestoolbox.oil.sg_st_gas
757
756
  =======================
@@ -891,7 +890,7 @@ pyrestoolbox.oil.oil_rate_radial
891
890
  oil_rate_radial(k, h, pr, pwf, r_w, r_ext, uo, bo, S = 0, vogel = False, pb = 0) -> float or np.array
892
891
 
893
892
  Returns liquid rate (stb/day) for radial flow using Darcy pseudo steady state equation with optional Vogel correction.
894
- Arrays can be used for any one of k, h, pr or pwf, returning corresponding 1-D array of rates. Using more than one input array � while not prohibited - will not return expected results
893
+ Arrays can be used for any one of k, h, pr or pwf, returning corresponding 1-D array of rates. Using more than one input array � while not prohibited - will not return expected results
895
894
 
896
895
  .. list-table:: Inputs
897
896
  :widths: 10 15 40
@@ -952,7 +951,7 @@ pyrestoolbox.oil.oil_rate_linear
952
951
  oil_rate_linear(k, pr, pwf, area, length, uo, bo, vogel = False, pb = 0) -> float or np.array
953
952
 
954
953
  Returns liquid rate (stb/day) for linear flow using Darcy steady state equation with optional Vogel correction.
955
- Arrays can be used for any one of k, pr, pwf or area, returning corresponding 1-D array of rates. Using more than one input array � while not prohibited - will not return expected results
954
+ Arrays can be used for any one of k, pr, pwf or area, returning corresponding 1-D array of rates. Using more than one input array � while not prohibited - will not return expected results
956
955
 
957
956
  .. list-table:: Inputs
958
957
  :widths: 10 15 40
@@ -52,7 +52,8 @@ Examples:
52
52
 
53
53
  .. code-block:: python
54
54
 
55
- >>> results = rtb.simtools.ix_extract_problem_cells()
55
+ >>> from pyrestoolbox import simtools
56
+ >>> results = simtools.ix_extract_problem_cells()
56
57
  >>> wells, grid_pres, grid_sat, grid_comp = results
57
58
  >>> grid_sat
58
59
  Processing TEST.PRT
@@ -64,7 +65,7 @@ Examples:
64
65
  Grid Saturation Change 310 32,121,24 13
65
66
  Grid Composition Change 1627 35,212,25 544
66
67
 
67
- .. image:: https://github.com/mwburgoyne/pyResToolbox/blob/main/docs/img/grid_sat_df.png
68
+ .. image:: img/grid_sat_df.png
68
69
  :alt: DSorted ataFrame of grid blocks with saturation related convergence issues
69
70
 
70
71
 
@@ -112,10 +113,10 @@ Examples:
112
113
 
113
114
  .. code-block:: python
114
115
 
115
- >>> from pyrestoolbox import pyrestoolbox as rtb
116
+ >>> from pyrestoolbox import simtools
116
117
  >>> import matplotlib.pyplot as plt
117
118
  >>> ReDs = [1.5, 2, 3, 5, 10, 25, 1000]
118
- >>> tds, pds = rtb.simtools.influence_tables(ReDs=ReDs, export=True)
119
+ >>> tds, pds = simtools.influence_tables(ReDs=ReDs, export=True)
119
120
  >>>
120
121
  >>> for p, pd in enumerate(pds):
121
122
  >>> plt.plot(tds, pd, label = str(ReDs[p]))
@@ -129,7 +130,7 @@ Examples:
129
130
  >>> plt.title('Constant Terminal Rate Solution')
130
131
  >>> plt.show()
131
132
 
132
- .. image:: https://github.com/mwburgoyne/pyResToolbox/blob/main/docs/img/influence.png
133
+ .. image:: img/influence.png
133
134
  :alt: Constant Terminal Rate influence tables
134
135
 
135
136
 
@@ -173,10 +174,10 @@ Examples:
173
174
 
174
175
  .. code-block:: python
175
176
 
176
- >>> rtb.simtools.zip_check_sim_deck(['FIELD_A.DATA', 'FIELD_B.afi'], console_summary=False)
177
+ >>> simtools.zip_check_sim_deck(['FIELD_A.DATA', 'FIELD_B.afi'], console_summary=False)
177
178
  ['INCLUDE/GridOpts.inc', 'INCLUDE/ZCORN_COORD.GRDECL', 'EPS.ixf']
178
179
 
179
- >>> rtb.simtools.zip_check_sim_deck()
180
+ >>> simtools.zip_check_sim_deck()
180
181
  Index File Name
181
182
  ------- ------------
182
183
  0 FIELD_A.DATA
@@ -247,7 +248,7 @@ Examples:
247
248
 
248
249
  .. code-block:: python
249
250
 
250
- >>> rtb.simtools.rr_solver(zi =np.array([0.7, 0.15, 0.1, 0.05]), ki = np.array([50, 5, 0.5, 0.01]))
251
+ >>> simtools.rr_solver(zi =np.array([0.7, 0.15, 0.1, 0.05]), ki = np.array([50, 5, 0.5, 0.01]))
251
252
  (6,
252
253
  array([0.7406252 , 0.1570315 , 0.09469948, 0.00764382]),
253
254
  array([0.0148125 , 0.0314063 , 0.18939896, 0.76438224]),
@@ -342,9 +343,12 @@ pyrestoolbox.simtools.rel_perm_table
342
343
 
343
344
 
344
345
  Examples:
345
- >>> from pyrestoolbox import pyrestoolbox as rtb
346
+
347
+ .. code-block:: python
348
+
349
+ >>> from pyrestoolbox import simtools
346
350
  >>> import matplotlib.pyplot as plt
347
- >>> df = rtb.simtools.rel_perm_table(rows=25, krtable='SGOF', krfamily='LET', kromax =1, krgmax =1, swc =0.2, sorg =0.15, Lo=2.5, Eo = 1.25, To = 1.75, Lg = 1.2, Eg = 1.5, Tg = 2.0)
351
+ >>> df = simtools.rel_perm_table(rows=25, krtable='SGOF', krfamily='LET', kromax =1, krgmax =1, swc =0.2, sorg =0.15, Lo=2.5, Eo = 1.25, To = 1.75, Lg = 1.2, Eg = 1.5, Tg = 2.0)
348
352
  >>> plt.plot(df['Sg'], df['Krgo'], c = 'r', label='Gas')
349
353
  >>> plt.plot(df['Sg'], df['Krog'], c = 'g', label='Oil')
350
354
  >>> plt.title('SGOF Gas Oil LET Relative Permeability Curves')
@@ -354,12 +358,12 @@ Examples:
354
358
  >>> plt.grid('both')
355
359
  >>> plt.plot()
356
360
 
357
- .. image:: https://github.com/mwburgoyne/pyResToolbox/blob/main/docs/img/sgof.png
361
+ .. image:: img/sgof.png
358
362
  :alt: SGOF Relative Permeability Curves
359
363
 
360
364
  .. code-block:: python
361
365
 
362
- >>> df = rtb.simtools.rel_perm_table(rows=25, krtable='SWOF', kromax =1, krwmax =0.25, swc =0.15, swcr = 0.2, sorw =0.15, no=2.5, nw=1.5)
366
+ >>> df = simtools.rel_perm_table(rows=25, krtable='SWOF', kromax =1, krwmax =0.25, swc =0.15, swcr = 0.2, sorw =0.15, no=2.5, nw=1.5)
363
367
  >>> plt.plot(df['Sw'], df['Krow'], c = 'g', label='Oil')
364
368
  >>> plt.plot(df['Sw'], df['Krwo'], c = 'b', label='Water')
365
369
  >>> plt.title('SWOF Water Oil Corey Relative Permeability Curves')
@@ -369,7 +373,7 @@ Examples:
369
373
  >>> plt.grid('both')
370
374
  >>> plt.plot()
371
375
 
372
- .. image:: https://github.com/mwburgoyne/pyResToolbox/blob/main/docs/img/swof.png
376
+ .. image:: img/swof.png
373
377
  :alt: SWOF Relative Permeability Curves
374
378
 
375
379
 
@@ -38,7 +38,7 @@ class component_library:
38
38
  def __init__(self, model='PR79'):
39
39
  path = 'component_library.xlsx'
40
40
  #filepath = pkg_resources.resource_filename(__name__, path)
41
- filepath = str(files(__name__).joinpath(path))
41
+ filepath = str(files('pyrestoolbox.library').joinpath(path))
42
42
  self.df = pd.read_excel(filepath, engine="openpyxl")
43
43
  self.model = model
44
44
  self.all_cols = ['Name', 'MW', 'Tc_R', 'Pc_psia',
@@ -82,4 +82,12 @@ class component_library:
82
82
  return dic[prop][comp]
83
83
  return 'Component or Property not in library'
84
84
 
85
- comp_library = component_library()
85
+ comp_library = component_library()
86
+
87
+ # Module-level convenience aliases matching documented API
88
+ prop = comp_library.prop
89
+ components = comp_library.components
90
+ names = comp_library.names
91
+ property_list = comp_library.property_list
92
+ models = comp_library.models
93
+ df = comp_library.df
@@ -60,6 +60,7 @@ def main():
60
60
  ('test_brine.py', 'Brine Module'),
61
61
  ('test_layer.py', 'Layer Module'),
62
62
  ('test_simtools.py', 'SimTools Module'),
63
+ ('test_doc_examples.py', 'Documentation Examples'),
63
64
  ]
64
65
 
65
66
  total_passed = 0
@@ -0,0 +1,556 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Tests for every documented code example in the RST documentation files.
4
+ Ensures that documented API calls actually work and return expected results.
5
+ Run with: PYTHONPATH=/home/mark/projects python3 -m pytest tests/test_doc_examples.py -v
6
+ Or standalone: PYTHONPATH=/home/mark/projects python3 tests/test_doc_examples.py
7
+ """
8
+
9
+ import sys
10
+ import os
11
+ import numpy as np
12
+
13
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
14
+ import pyrestoolbox.gas as gas
15
+ import pyrestoolbox.oil as oil
16
+ import pyrestoolbox.brine as brine
17
+ import pyrestoolbox.layer as layer
18
+ import pyrestoolbox.simtools as simtools
19
+ import pyrestoolbox.library as library
20
+
21
+ RTOL = 1e-4 # Relative tolerance for floating point comparisons
22
+
23
+ # =============================================================================
24
+ # Gas Module Documentation Examples (docs/gas.rst)
25
+ # =============================================================================
26
+
27
+ def test_doc_gas_z_dak_pmc():
28
+ """gas.rst: gas_z with DAK/PMC for sg=0.68"""
29
+ result = gas.gas_z(p=2350, sg=0.68, degf=180, zmethod='DAK', cmethod='PMC')
30
+ assert isinstance(result, float)
31
+ assert abs(result - 0.8785399927100872) / 0.8785399927100872 < RTOL
32
+
33
+ def test_doc_gas_z_bur_co2():
34
+ """gas.rst: gas_z for pure CO2 with BUR method"""
35
+ result = gas.gas_z(p=2350, sg=0.68, degf=180, co2=1.0, zmethod='BUR', cmethod='BUR')
36
+ assert isinstance(result, float)
37
+ assert abs(result - 0.5258309021348752) / 0.5258309021348752 < RTOL
38
+
39
+ def test_doc_gas_sg():
40
+ """gas.rst: gas_sg for mixture with H2"""
41
+ result = gas.gas_sg(hc_mw=19.0, co2=0.05, h2s=0.10, n2=0, h2=0.20)
42
+ assert isinstance(result, float)
43
+ assert abs(result - 0.6338246461857093) / 0.6338246461857093 < RTOL
44
+
45
+ def test_doc_gas_z_bur_mixture():
46
+ """gas.rst: gas_z BUR for complex mixture"""
47
+ gsg = gas.gas_sg(hc_mw=19.0, co2=0.05, h2s=0.10, n2=0, h2=0.20)
48
+ result = gas.gas_z(p=2350, sg=gsg, degf=180, co2=0.05, h2s=0.10, n2=0, h2=0.20, zmethod='BUR', cmethod='BUR')
49
+ assert isinstance(result, float)
50
+ assert abs(result - 0.9048153036714465) / 0.9048153036714465 < RTOL
51
+
52
+ def test_doc_gas_tc_pc_pmc():
53
+ """gas.rst: gas_tc_pc with PMC defaults"""
54
+ tc, pc = gas.gas_tc_pc(sg=0.7, co2=0.15)
55
+ assert abs(tc - 363.9387708314338) / 363.9387708314338 < RTOL
56
+ assert abs(pc - 738.3190067714969) / 738.3190067714969 < RTOL
57
+
58
+ def test_doc_gas_tc_pc_sut_fixed_tc():
59
+ """gas.rst: gas_tc_pc with SUT and fixed tc"""
60
+ tc, pc = gas.gas_tc_pc(sg=0.7, co2=0.15, tc=365, cmethod='SUT')
61
+ assert tc == 365 # Should be returned unchanged
62
+ assert abs(pc - 709.2356299485114) / 709.2356299485114 < RTOL
63
+
64
+ def test_doc_gas_z_n2_co2():
65
+ """gas.rst: gas_z with N2 and CO2"""
66
+ result = gas.gas_z(p=1000, sg=0.75, degf=160, n2=0.02, co2=0.17)
67
+ assert isinstance(result, float)
68
+ assert abs(result - 0.9138558878125714) / 0.9138558878125714 < RTOL
69
+
70
+ def test_doc_gas_z_hy():
71
+ """gas.rst: gas_z with HY method"""
72
+ result = gas.gas_z(p=1000, sg=0.75, degf=160, n2=0.02, co2=0.17, zmethod='HY')
73
+ assert isinstance(result, float)
74
+ assert abs(result - 0.9142136711443208) / 0.9142136711443208 < RTOL
75
+
76
+ def test_doc_gas_z_sut_array():
77
+ """gas.rst: gas_z array with SUT"""
78
+ result = gas.gas_z(p=[1000, 2000], sg=0.75, degf=160, cmethod='SUT', n2=0.02, co2=0.17)
79
+ assert isinstance(result, np.ndarray)
80
+ assert len(result) == 2
81
+ expected = np.array([0.91900003, 0.87160514])
82
+ np.testing.assert_allclose(result, expected, rtol=RTOL)
83
+
84
+ def test_doc_gas_ug_hy_sut():
85
+ """gas.rst: gas_ug with HY/SUT"""
86
+ result = gas.gas_ug(p=1000, sg=0.75, degf=180, zmethod='HY', cmethod='SUT')
87
+ assert isinstance(result, float)
88
+ assert abs(result - 0.014118890100250796) / 0.014118890100250796 < RTOL
89
+
90
+ def test_doc_gas_ug_default():
91
+ """gas.rst: gas_ug with defaults"""
92
+ result = gas.gas_ug(p=1000, sg=0.75, degf=180)
93
+ assert isinstance(result, float)
94
+ assert abs(result - 0.014110092961853301) / 0.014110092961853301 < RTOL
95
+
96
+ def test_doc_gas_cg_scalar():
97
+ """gas.rst: gas_cg scalar"""
98
+ result = gas.gas_cg(p=2000, sg=0.68, degf=120, co2=0.05)
99
+ assert isinstance(result, float)
100
+ assert abs(result - 0.0005374854430839333) / 0.0005374854430839333 < RTOL
101
+
102
+ def test_doc_gas_cg_array():
103
+ """gas.rst: gas_cg array"""
104
+ result = gas.gas_cg(p=np.array([1000, 2000]), sg=0.68, degf=120, co2=0.05)
105
+ assert isinstance(result, np.ndarray)
106
+ expected = np.array([0.00110369, 0.00053749])
107
+ np.testing.assert_allclose(result, expected, rtol=RTOL)
108
+
109
+ def test_doc_gas_bg_scalar():
110
+ """gas.rst: gas_bg scalar"""
111
+ result = gas.gas_bg(p=3000, sg=0.78, degf=240)
112
+ assert isinstance(result, float)
113
+ assert abs(result - 0.005927563975073749) / 0.005927563975073749 < RTOL
114
+
115
+ def test_doc_gas_bg_array_inverse():
116
+ """gas.rst: 1/gas_bg array"""
117
+ result = 1 / gas.gas_bg(p=[3000, 5000], sg=0.78, degf=240)
118
+ assert isinstance(result, np.ndarray)
119
+ expected = np.array([168.70336688, 249.54573283])
120
+ np.testing.assert_allclose(result, expected, rtol=RTOL)
121
+
122
+ def test_doc_gas_den():
123
+ """gas.rst: gas_den with impurities"""
124
+ result = gas.gas_den(p=2000, sg=0.75, degf=150, zmethod='HY', cmethod='SUT', n2=0.02, co2=0.15, h2s=0.02)
125
+ assert isinstance(result, float)
126
+ assert abs(result - 7.736656004563576) / 7.736656004563576 < RTOL
127
+
128
+ def test_doc_gas_water_content():
129
+ """gas.rst: gas_water_content"""
130
+ result = gas.gas_water_content(p=1500, degf=165)
131
+ assert isinstance(result, float)
132
+ assert abs(result - 0.6474226409378979) / 0.6474226409378979 < RTOL
133
+
134
+ def test_doc_gas_ponz2p_scalar():
135
+ """gas.rst: gas_ponz2p scalar"""
136
+ result = gas.gas_ponz2p(poverz=2500, sg=0.75, degf=165)
137
+ assert isinstance(result, float)
138
+ assert abs(result - 2081.5489292144775) / 2081.5489292144775 < RTOL
139
+
140
+ def test_doc_gas_ponz2p_array():
141
+ """gas.rst: gas_ponz2p array"""
142
+ result = gas.gas_ponz2p(poverz=[2500, 5000], sg=0.75, degf=165)
143
+ assert isinstance(result, np.ndarray)
144
+ expected = np.array([2081.54892921, 4856.97983205])
145
+ np.testing.assert_allclose(result, expected, rtol=RTOL)
146
+
147
+ def test_doc_gas_grad2sg():
148
+ """gas.rst: gas_grad2sg"""
149
+ result = gas.gas_grad2sg(grad=0.0657, p=2500, degf=175)
150
+ assert isinstance(result, float)
151
+ assert abs(result - 0.7495803994806547) / 0.7495803994806547 < RTOL
152
+
153
+ def test_doc_gas_dmp_positive():
154
+ """gas.rst: gas_dmp positive (p1 < p2)"""
155
+ result = gas.gas_dmp(p1=1000, p2=2000, degf=185, sg=0.78, zmethod='HY', cmethod='SUT', n2=0.05, co2=0.1, h2s=0.02)
156
+ assert isinstance(result, float)
157
+ assert result > 0
158
+ assert abs(result - 213690308.9907268) / 213690308.9907268 < RTOL
159
+
160
+ def test_doc_gas_dmp_negative():
161
+ """gas.rst: gas_dmp negative (p1 > p2) with fixed tc/pc"""
162
+ result = gas.gas_dmp(p1=2000, p2=1000, degf=185, sg=0.78, tc=371, pc=682)
163
+ assert isinstance(result, float)
164
+ assert result < 0
165
+ assert abs(result - (-213713909.36339885)) / 213713909.36339885 < RTOL
166
+
167
+ def test_doc_gas_fws_sg():
168
+ """gas.rst: gas_fws_sg"""
169
+ result = gas.gas_fws_sg(sg_g=0.855, cgr=30, api_st=53)
170
+ assert isinstance(result, float)
171
+ assert abs(result - 0.937116010334538) / 0.937116010334538 < RTOL
172
+
173
+ def test_doc_gas_rate_radial_scalar():
174
+ """gas.rst: gas_rate_radial scalar"""
175
+ result = gas.gas_rate_radial(k=5, h=50, pr=2000, pwf=750, r_w=0.3, r_ext=1500, degf=180, sg=0.75, D=0.01, S=5)
176
+ assert isinstance(result, float)
177
+ assert abs(result - 2078.9101970773477) / 2078.9101970773477 < RTOL
178
+
179
+ def test_doc_gas_rate_radial_array():
180
+ """gas.rst: gas_rate_radial array"""
181
+ result = gas.gas_rate_radial(k=1, h=50, pr=[2000, 1000], pwf=750, r_w=0.3, r_ext=1500, degf=180, sg=0.75, D=0.01, S=5)
182
+ assert isinstance(result, np.ndarray)
183
+ expected = np.array([704.29202227, 135.05317439])
184
+ np.testing.assert_allclose(result, expected, rtol=RTOL)
185
+
186
+ def test_doc_gas_rate_linear_scalar():
187
+ """gas.rst: gas_rate_linear scalar"""
188
+ result = gas.gas_rate_linear(k=0.1, area=50, length=200, pr=2000, pwf=250, degf=180, sg=0.8)
189
+ assert isinstance(result, float)
190
+ assert abs(result - 8.202200317597859) / 8.202200317597859 < RTOL
191
+
192
+ def test_doc_gas_rate_linear_array():
193
+ """gas.rst: gas_rate_linear array"""
194
+ result = gas.gas_rate_linear(k=0.1, area=50, length=200, pr=[2000, 1000, 500], pwf=250, degf=180, sg=0.8)
195
+ assert isinstance(result, np.ndarray)
196
+ expected = np.array([8.20220032, 2.10691337, 0.42685002])
197
+ np.testing.assert_allclose(result, expected, rtol=RTOL)
198
+
199
+ # =============================================================================
200
+ # Oil Module Documentation Examples (docs/oil.rst)
201
+ # =============================================================================
202
+
203
+ def test_doc_oil_ja_sg():
204
+ """oil.rst: oil_ja_sg"""
205
+ result = oil.oil_ja_sg(mw=150, ja=0.5)
206
+ assert isinstance(result, float)
207
+ assert abs(result - 0.8583666666666667) / 0.8583666666666667 < RTOL
208
+
209
+ def test_doc_oil_twu_props():
210
+ """oil.rst: oil_twu_props"""
211
+ result = oil.oil_twu_props(mw=225, ja=0.5)
212
+ assert isinstance(result, tuple)
213
+ assert len(result) == 5
214
+ expected = (0.8954444444444445, 1068.3961103813851, 1422.4620493584146, 264.23402773211745, 13.498328588856445)
215
+ for r, e in zip(result, expected):
216
+ assert abs(float(r) - e) / abs(e) < RTOL
217
+
218
+ def test_doc_oil_rs_st():
219
+ """oil.rst: oil_rs_st"""
220
+ result = oil.oil_rs_st(psp=114.7, degf_sp=80, api=38)
221
+ assert isinstance(result, float)
222
+ assert abs(result - 4.176458005559282) / 4.176458005559282 < RTOL
223
+
224
+ def test_doc_oil_pbub_valmc():
225
+ """oil.rst: oil_pbub with VALMC default"""
226
+ result = oil.oil_pbub(api=43, degf=185, rsb=2350, sg_g=0.72)
227
+ assert isinstance(result, float)
228
+ assert abs(result - 5199.2406069808885) / 5199.2406069808885 < RTOL
229
+
230
+ def test_doc_oil_pbub_stan():
231
+ """oil.rst: oil_pbub with Standing via sg_sp"""
232
+ result = oil.oil_pbub(api=43, degf=185, rsb=2350, sg_sp=0.72, pbmethod='STAN')
233
+ assert isinstance(result, float)
234
+ assert abs(result - 6390.281894698239) / 6390.281894698239 < RTOL
235
+
236
+ def test_doc_oil_pbub_class_object():
237
+ """oil.rst: oil_pbub using class object"""
238
+ result = oil.oil_pbub(api=43, degf=185, rsb=2350, sg_g=0.72, pbmethod=oil.pb_method.STAN)
239
+ assert isinstance(result, float)
240
+ assert result > 0
241
+
242
+ def test_doc_oil_rs_bub():
243
+ """oil.rst: oil_rs_bub"""
244
+ result = oil.oil_rs_bub(api=43, degf=185, pb=5179.5, sg_sp=0.72)
245
+ assert isinstance(result, float)
246
+ assert abs(result - 1872.666133282599) / 1872.666133282599 < RTOL
247
+
248
+ def test_doc_oil_rs_with_pb_rsb():
249
+ """oil.rst: oil_rs with both pb and rsb"""
250
+ result = oil.oil_rs(api=43, degf=185, sg_sp=0.72, p=3000, pb=5179.5, rsb=2370)
251
+ assert isinstance(result, float)
252
+ assert abs(result - 1017.9424383646037) / 1017.9424383646037 < RTOL
253
+
254
+ def test_doc_oil_rs_with_rsb_only():
255
+ """oil.rst: oil_rs with rsb only"""
256
+ result = oil.oil_rs(api=43, degf=185, sg_sp=0.72, p=3000, rsb=2370)
257
+ assert isinstance(result, float)
258
+ assert abs(result - 1010.0669567201218) / 1010.0669567201218 < RTOL
259
+
260
+ def test_doc_oil_rs_with_pb_only():
261
+ """oil.rst: oil_rs with pb only"""
262
+ result = oil.oil_rs(api=43, degf=185, sg_sp=0.72, p=3000, pb=5180)
263
+ assert isinstance(result, float)
264
+ assert abs(result - 804.2857187814161) / 804.2857187814161 < RTOL
265
+
266
+ def test_doc_oil_rs_stan():
267
+ """oil.rst: oil_rs with Standing method"""
268
+ result = oil.oil_rs(api=43, degf=185, sg_sp=0.72, p=3000, pb=5180, rsmethod='STAN')
269
+ assert isinstance(result, float)
270
+ assert abs(result - 947.1133546937306) / 947.1133546937306 < RTOL
271
+
272
+ def test_doc_oil_co_above_pb():
273
+ """oil.rst: oil_co above bubble point"""
274
+ result = oil.oil_co(p=4500, api=47, degf=180, sg_sp=0.72, rsb=2750)
275
+ assert isinstance(result, float)
276
+ assert abs(result - 0.0007587726853322233) / 0.0007587726853322233 < RTOL
277
+
278
+ def test_doc_oil_co_below_pb():
279
+ """oil.rst: oil_co below bubble point"""
280
+ result = oil.oil_co(p=2000, api=47, degf=180, sg_sp=0.72, rsb=2750, pb=4945)
281
+ assert isinstance(result, float)
282
+ assert abs(result - 0.0009245540028053584) / 0.0009245540028053584 < RTOL
283
+
284
+ def test_doc_oil_deno():
285
+ """oil.rst: oil_deno"""
286
+ result = oil.oil_deno(p=2000, degf=165, rs=1000, rsb=2000, sg_g=0.72, api=38)
287
+ assert isinstance(result, float)
288
+ assert abs(result - 40.98349866963842) / 40.98349866963842 < RTOL
289
+
290
+ def test_doc_oil_bo_mcain():
291
+ """oil.rst: oil_bo with McCain default"""
292
+ result = oil.oil_bo(p=2000, pb=3000, degf=165, rs=1000, rsb=2000, sg_o=0.8, sg_g=0.68)
293
+ assert isinstance(result, float)
294
+ assert abs(result - 1.5075107735318138) / 1.5075107735318138 < RTOL
295
+
296
+ def test_doc_oil_bo_stan():
297
+ """oil.rst: oil_bo with Standing"""
298
+ result = oil.oil_bo(p=2000, pb=3000, degf=165, rs=1000, rsb=2000, sg_o=0.8, sg_g=0.68, bomethod='STAN')
299
+ assert isinstance(result, float)
300
+ assert abs(result - 1.5393786735904431) / 1.5393786735904431 < RTOL
301
+
302
+ def test_doc_oil_viso():
303
+ """oil.rst: oil_viso"""
304
+ result = oil.oil_viso(p=2000, api=38, degf=165, pb=3500, rs=1000)
305
+ assert isinstance(result, float)
306
+ assert abs(result - 0.416858469042502) / 0.416858469042502 < RTOL
307
+
308
+ def test_doc_sg_evolved_gas():
309
+ """oil.rst: sg_evolved_gas"""
310
+ result = oil.sg_evolved_gas(p=2000, degf=185, rsb=2370, api=43, sg_sp=0.72)
311
+ assert isinstance(result, float)
312
+ assert abs(result - 0.7872810977386344) / 0.7872810977386344 < RTOL
313
+
314
+ def test_doc_sg_st_gas():
315
+ """oil.rst: sg_st_gas"""
316
+ result = oil.sg_st_gas(114.7, rsp=1500, api=42, sg_sp=0.72, degf_sp=80)
317
+ assert isinstance(result, float)
318
+ assert abs(result - 1.1923932340625523) / 1.1923932340625523 < RTOL
319
+
320
+ def test_doc_sgg_wt_avg():
321
+ """oil.rst: sgg_wt_avg"""
322
+ result = oil.sgg_wt_avg(sg_sp=0.72, rsp=1000, sg_st=1.1, rst=5)
323
+ assert isinstance(result, float)
324
+ assert abs(result - 0.7218905472636816) / 0.7218905472636816 < RTOL
325
+
326
+ def test_doc_oil_api():
327
+ """oil.rst: oil_api"""
328
+ result = oil.oil_api(sg_value=0.82)
329
+ assert isinstance(result, float)
330
+ assert abs(result - 41.0609756097561) / 41.0609756097561 < RTOL
331
+
332
+ def test_doc_oil_sg():
333
+ """oil.rst: oil_sg"""
334
+ result = oil.oil_sg(api_value=45)
335
+ assert isinstance(result, float)
336
+ assert abs(result - 0.8016997167138811) / 0.8016997167138811 < RTOL
337
+
338
+ def test_doc_oil_rate_radial_scalar():
339
+ """oil.rst: oil_rate_radial scalar with Vogel"""
340
+ result = oil.oil_rate_radial(k=20, h=20, pr=1500, pwf=250, r_w=0.3, r_ext=1500, uo=0.8, bo=1.4, vogel=True, pb=1800)
341
+ assert isinstance(result, float)
342
+ assert abs(result - 213.8147848023242) / 213.8147848023242 < RTOL
343
+
344
+ def test_doc_oil_rate_radial_array():
345
+ """oil.rst: oil_rate_radial array with Vogel"""
346
+ result = oil.oil_rate_radial(k=20, h=20, pr=[1500, 2000], pwf=250, r_w=0.3, r_ext=1500, uo=0.8, bo=1.4, vogel=True, pb=1800)
347
+ assert isinstance(result, np.ndarray)
348
+ expected = np.array([213.8147848, 376.58731835])
349
+ np.testing.assert_allclose(result, expected, rtol=RTOL)
350
+
351
+ def test_doc_oil_rate_linear_scalar():
352
+ """oil.rst: oil_rate_linear scalar"""
353
+ result = oil.oil_rate_linear(k=0.1, area=15000, pr=3000, pwf=500, length=500, uo=0.4, bo=1.5)
354
+ assert isinstance(result, float)
355
+ assert abs(result - 14.08521246363274) / 14.08521246363274 < RTOL
356
+
357
+ def test_doc_oil_rate_linear_array():
358
+ """oil.rst: oil_rate_linear array"""
359
+ result = oil.oil_rate_linear(k=[0.1, 1, 5, 10], area=15000, pr=3000, pwf=500, length=500, uo=0.4, bo=1.5)
360
+ assert isinstance(result, np.ndarray)
361
+ expected = np.array([14.08521246, 140.85212464, 704.26062318, 1408.52124636])
362
+ np.testing.assert_allclose(result, expected, rtol=RTOL)
363
+
364
+ def test_doc_make_bot_og():
365
+ """oil.rst: make_bot_og returns correct structure"""
366
+ results = oil.make_bot_og(pvto=False, pi=4000, api=38, degf=175, sg_g=0.68, pmax=5500, pb=4500, nrows=10, export=False)
367
+ assert isinstance(results, dict)
368
+ for key in ['bot', 'deno', 'deng', 'denw', 'cw', 'uw', 'pb', 'rsb', 'rsb_scale', 'usat']:
369
+ assert key in results, f"Missing key: {key}"
370
+ assert results['pb'] == 4500
371
+ assert results['bot'].shape[0] == 10
372
+
373
+ # =============================================================================
374
+ # Brine Module Documentation Examples (docs/brine.rst)
375
+ # =============================================================================
376
+
377
+ def test_doc_brine_props():
378
+ """brine.rst: brine_props"""
379
+ bw, lsg, visw, cw, rsw = brine.brine_props(p=160, degf=135, wt=1.5, ch4_sat=1.0)
380
+ assert abs(bw - 1.0151710978322923) / 1.0151710978322923 < RTOL
381
+ assert abs(lsg - 0.9950036658248123) / 0.9950036658248123 < RTOL
382
+ assert abs(visw - 0.4993957925685823) / 0.4993957925685823 < RTOL
383
+ assert abs(cw - 0.00015465242842085548) / 0.00015465242842085548 < RTOL
384
+ assert abs(rsw - 1.2548933067431638) / 1.2548933067431638 < RTOL
385
+
386
+ def test_doc_co2_brine_field():
387
+ """brine.rst: CO2_Brine_Mixture field units"""
388
+ mix = brine.CO2_Brine_Mixture(pres=5000, temp=275, ppm=30000, metric=False)
389
+ assert isinstance(mix.bw, list)
390
+ assert len(mix.bw) == 3
391
+ assert abs(float(mix.bw[0]) - 1.108579075130017) / 1.108579075130017 < RTOL
392
+ assert isinstance(mix.x, np.ndarray)
393
+ assert abs(mix.x[0] - 0.02431225) / 0.02431225 < RTOL
394
+
395
+ def test_doc_co2_brine_metric():
396
+ """brine.rst: CO2_Brine_Mixture metric units"""
397
+ mix = brine.CO2_Brine_Mixture(pres=175, temp=85)
398
+ assert abs(mix.Rs - 24.742717860296057) / 24.742717860296057 < RTOL
399
+
400
+ def test_doc_make_pvtw_table():
401
+ """brine.rst: make_pvtw_table"""
402
+ result = brine.make_pvtw_table(pi=3000, degf=200, wt=0, ch4_sat=0)
403
+ assert isinstance(result, dict)
404
+ for key in ['table', 'pref', 'bw_ref', 'cw_ref', 'visw_ref', 'rsw_ref', 'den_ref']:
405
+ assert key in result, f"Missing key: {key}"
406
+ assert abs(result['bw_ref'] - 1.0275744009507692) / 1.0275744009507692 < RTOL
407
+
408
+ # =============================================================================
409
+ # Layer Module Documentation Examples (docs/layer.rst)
410
+ # =============================================================================
411
+
412
+ def test_doc_lorenz2b_lang():
413
+ """layer.rst: lorenz2b with Langmuir"""
414
+ result = layer.lorenz2b(0.75, lrnz_method='LANG')
415
+ assert abs(result - 16.139518537603912) / 16.139518537603912 < RTOL
416
+
417
+ def test_doc_lorenz2b_exp():
418
+ """layer.rst: lorenz2b with Exponential default"""
419
+ result = layer.lorenz2b(0.75)
420
+ assert abs(result - 7.978108090962671) / 7.978108090962671 < RTOL
421
+
422
+ def test_doc_lorenzfromb_lang():
423
+ """layer.rst: lorenzfromb with Langmuir"""
424
+ result = layer.lorenzfromb(16.139518537603912, lrnz_method='LANG')
425
+ assert abs(result - 0.750000182307895) / 0.750000182307895 < RTOL
426
+
427
+ def test_doc_lorenzfromb_exp():
428
+ """layer.rst: lorenzfromb with Exponential default"""
429
+ result = layer.lorenzfromb(7.978108090962671)
430
+ assert abs(result - 0.7500000108799212) / 0.7500000108799212 < RTOL
431
+
432
+ def test_doc_lorenz_from_flow_fraction():
433
+ """layer.rst: lorenz_from_flow_fraction"""
434
+ result = layer.lorenz_from_flow_fraction(kh_frac=0.6, phih_frac=0.15)
435
+ assert abs(result - 0.6759312029093838) / 0.6759312029093838 < RTOL
436
+
437
+ def test_doc_lorenz_2_flow_frac():
438
+ """layer.rst: lorenz_2_flow_frac"""
439
+ result = layer.lorenz_2_flow_frac(lorenz=0.6759312029093838, phih_frac=0.15)
440
+ assert abs(result - 0.6000001346893536) / 0.6000001346893536 < RTOL
441
+
442
+ def test_doc_lorenz_2_layers_nlayers():
443
+ """layer.rst: lorenz_2_layers with nlayers"""
444
+ result = layer.lorenz_2_layers(lorenz=0.67, nlayers=5, k_avg=10, shuffle=False)
445
+ assert isinstance(result, np.ndarray)
446
+ assert len(result) == 5
447
+ # With shuffle=False, should be sorted descending
448
+ assert all(result[i] >= result[i+1] for i in range(len(result)-1))
449
+ # Average should be close to 10
450
+ assert abs(np.mean(result) - 10) / 10 < 0.01
451
+ expected = np.array([34.9323596, 10.58944038, 3.21009656, 0.9731128, 0.29499066])
452
+ np.testing.assert_allclose(result, expected, rtol=RTOL)
453
+
454
+ def test_doc_lorenz_2_layers_phi_h_fracs():
455
+ """layer.rst: lorenz_2_layers with phi_h_fracs"""
456
+ result = layer.lorenz_2_layers(lorenz=0.67, k_avg=10, phi_h_fracs=[0.05, 0.5])
457
+ assert isinstance(result, np.ndarray)
458
+ assert len(result) == 3
459
+ expected = np.array([51.72990694, 14.12556056, 0.77938749])
460
+ np.testing.assert_allclose(result, expected, rtol=RTOL)
461
+
462
+ # =============================================================================
463
+ # SimTools Module Documentation Examples (docs/simtools.rst)
464
+ # =============================================================================
465
+
466
+ def test_doc_rel_perm_table_sgof():
467
+ """simtools.rst: rel_perm_table SGOF with LET"""
468
+ df = simtools.rel_perm_table(rows=25, krtable='SGOF', krfamily='LET', kromax=1, krgmax=1, swc=0.2, sorg=0.15, Lo=2.5, Eo=1.25, To=1.75, Lg=1.2, Eg=1.5, Tg=2.0)
469
+ assert 'Sg' in df.columns
470
+ assert 'Krgo' in df.columns
471
+ assert 'Krog' in df.columns
472
+ assert df.shape[0] >= 25
473
+
474
+ def test_doc_rel_perm_table_swof():
475
+ """simtools.rst: rel_perm_table SWOF with Corey"""
476
+ df = simtools.rel_perm_table(rows=25, krtable='SWOF', kromax=1, krwmax=0.25, swc=0.15, swcr=0.2, sorw=0.15, no=2.5, nw=1.5)
477
+ assert 'Sw' in df.columns
478
+ assert 'Krwo' in df.columns
479
+ assert 'Krow' in df.columns
480
+ assert df.shape[0] == 25
481
+
482
+ def test_doc_rr_solver():
483
+ """simtools.rst: rr_solver"""
484
+ n_it, yi, xi, V, L = simtools.rr_solver(zi=np.array([0.7, 0.15, 0.1, 0.05]), ki=np.array([50, 5, 0.5, 0.01]))
485
+ assert n_it == 6
486
+ assert abs(V - 0.9440279802330239) / 0.9440279802330239 < RTOL
487
+ assert abs(L - 0.05597201976697608) / 0.05597201976697608 < RTOL
488
+ expected_yi = np.array([0.7406252, 0.1570315, 0.09469948, 0.00764382])
489
+ expected_xi = np.array([0.0148125, 0.0314063, 0.18939896, 0.76438224])
490
+ np.testing.assert_allclose(yi, expected_yi, rtol=RTOL)
491
+ np.testing.assert_allclose(xi, expected_xi, rtol=RTOL)
492
+
493
+ # =============================================================================
494
+ # Library Module Documentation Examples (docs/library.rst)
495
+ # =============================================================================
496
+
497
+ def test_doc_library_prop_pc():
498
+ """library.rst: library.prop for CH4 Pc_psia"""
499
+ result = library.prop(comp='CH4', prop='Pc_psia')
500
+ assert result == 667.029
501
+
502
+ def test_doc_library_prop_vtran_pr79():
503
+ """library.rst: library.prop for C3 VTran PR79"""
504
+ result = library.prop(comp='C3', prop='VTran')
505
+ assert result == -0.06381
506
+
507
+ def test_doc_library_prop_vtran_srk():
508
+ """library.rst: library.prop for C3 VTran SRK"""
509
+ result = library.prop(comp='C3', prop='VTran', model='SRK')
510
+ assert result == 0.09075
511
+
512
+ def test_doc_library_components():
513
+ """library.rst: library.components is a list with CH4"""
514
+ assert isinstance(library.components, list)
515
+ assert 'CH4' in library.components
516
+
517
+ def test_doc_library_property_list():
518
+ """library.rst: library.property_list"""
519
+ assert isinstance(library.property_list, list)
520
+ assert 'MW' in library.property_list
521
+ assert 'Tc_R' in library.property_list
522
+ assert 'Pc_psia' in library.property_list
523
+
524
+ def test_doc_library_models():
525
+ """library.rst: library.models"""
526
+ assert library.models == ['PR79', 'PR77', 'SRK', 'RK']
527
+
528
+
529
+ # =============================================================================
530
+ # Main runner
531
+ # =============================================================================
532
+
533
+ if __name__ == '__main__':
534
+ import traceback
535
+ tests = [(k, v) for k, v in sorted(globals().items()) if k.startswith('test_') and callable(v)]
536
+ passed = failed = 0
537
+ errors = []
538
+ for name, func in tests:
539
+ try:
540
+ func()
541
+ passed += 1
542
+ print(f" PASS: {name}")
543
+ except Exception as e:
544
+ failed += 1
545
+ errors.append((name, str(e)))
546
+ print(f" FAIL: {name}")
547
+ print(f" {e}")
548
+ traceback.print_exc()
549
+
550
+ print(f"\n{'='*60}")
551
+ print(f"TOTAL: {passed} passed, {failed} failed out of {passed + failed}")
552
+ if errors:
553
+ print(f"\nFailed tests:")
554
+ for name, msg in errors:
555
+ print(f" - {name}: {msg}")
556
+ print("="*60)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyrestoolbox
3
- Version: 2.2.1
3
+ Version: 2.2.2
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
@@ -49,6 +49,7 @@ pyrestoolbox/simtools/simtools.py
49
49
  pyrestoolbox/tests/__init__.py
50
50
  pyrestoolbox/tests/run_all_tests.py
51
51
  pyrestoolbox/tests/test_brine.py
52
+ pyrestoolbox/tests/test_doc_examples.py
52
53
  pyrestoolbox/tests/test_gas.py
53
54
  pyrestoolbox/tests/test_layer.py
54
55
  pyrestoolbox/tests/test_oil.py
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = pyrestoolbox
3
- version = 2.2.1
3
+ version = 2.2.2
4
4
  author = Mark W. Burgoyne
5
5
  author_email = mark.w.burgoyne@gmail.com
6
6
  description = pyResToolbox - A collection of Reservoir Engineering Utilities
@@ -12,7 +12,7 @@ with open(os.path.join(ROOT, 'README.md'), 'r', encoding='utf-8') as f:
12
12
  setup(
13
13
  name='pyrestoolbox',
14
14
  include_package_data=True,
15
- version='2.2.1', # Ideally should be same as your GitHub release tag version
15
+ version='2.2.2', # Ideally should be same as your GitHub release tag version
16
16
  packages=find_packages(),
17
17
  description='pyResToolbox - A collection of Reservoir Engineering Utilities',
18
18
  license="GNU General Public License v3 or later (GPLv3+)",
File without changes
File without changes
File without changes
File without changes