pyrestoolbox 3.1.2__tar.gz → 3.1.3__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 (113) hide show
  1. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/PKG-INFO +1 -1
  2. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyproject.toml +1 -1
  3. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/docs/changelist.rst +8 -0
  4. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/gas/gas.py +17 -15
  5. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/setup.cfg +1 -1
  6. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/setup.py +1 -1
  7. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/src/gas_viscosity/mod.rs +51 -0
  8. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/src/lib.rs +9 -0
  9. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/src/zfactor/mod.rs +74 -0
  10. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/.github/workflows/build-wheels.yml +0 -0
  11. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/.gitignore +0 -0
  12. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/Cargo.lock +0 -0
  13. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/Cargo.toml +0 -0
  14. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/LICENSE +0 -0
  15. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/MANIFEST.in +0 -0
  16. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/README.rst +0 -0
  17. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/ResToolbox/privacy_policy.md +0 -0
  18. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/benchmark_rust_vs_python.py +0 -0
  19. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/build_pure_python.py +0 -0
  20. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/__init__.py +0 -0
  21. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/_accelerator.py +0 -0
  22. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/brine/__init__.py +0 -0
  23. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/brine/_lib_salting_library.py +0 -0
  24. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/brine/_lib_vle_engine.py +0 -0
  25. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/brine/brine.py +0 -0
  26. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/classes/__init__.py +0 -0
  27. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/classes/classes.py +0 -0
  28. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/constants/__init__.py +0 -0
  29. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/constants/constants.py +0 -0
  30. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/dca/__init__.py +0 -0
  31. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/dca/dca.py +0 -0
  32. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/docs/brine.rst +0 -0
  33. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/docs/dca.rst +0 -0
  34. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/docs/examples.ipynb +0 -0
  35. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/docs/gas.rst +0 -0
  36. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/docs/img/bot.png +0 -0
  37. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/docs/img/bot_PVTO.png +0 -0
  38. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/docs/img/bot_img.png +0 -0
  39. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/docs/img/dry_gas.png +0 -0
  40. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/docs/img/grid_sat_df.png +0 -0
  41. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/docs/img/influence.png +0 -0
  42. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/docs/img/properties_df.png +0 -0
  43. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/docs/img/sgof.png +0 -0
  44. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/docs/img/swof.png +0 -0
  45. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/docs/layer.rst +0 -0
  46. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/docs/library.rst +0 -0
  47. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/docs/matbal.rst +0 -0
  48. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/docs/nodal.rst +0 -0
  49. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/docs/nodal_examples.ipynb +0 -0
  50. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/docs/nodal_hydrate_demo.ipynb +0 -0
  51. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/docs/oil.rst +0 -0
  52. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/docs/recommend.rst +0 -0
  53. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/docs/sensitivity.rst +0 -0
  54. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/docs/simtools.rst +0 -0
  55. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/gas/__init__.py +0 -0
  56. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/layer/__init__.py +0 -0
  57. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/layer/layer.py +0 -0
  58. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/library/__init__.py +0 -0
  59. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/library/component_library.xlsx +0 -0
  60. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/library/library.py +0 -0
  61. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/matbal/__init__.py +0 -0
  62. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/matbal/matbal.py +0 -0
  63. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/nodal/__init__.py +0 -0
  64. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/nodal/nodal.py +0 -0
  65. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/oil/__init__.py +0 -0
  66. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/oil/oil.py +0 -0
  67. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/plyasunov/__init__.py +0 -0
  68. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/plyasunov/iapws_if97.py +0 -0
  69. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/plyasunov/plyasunov_model.py +0 -0
  70. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/plyasunov/water_properties.py +0 -0
  71. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/recommend/__init__.py +0 -0
  72. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/recommend/recommend.py +0 -0
  73. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/sensitivity/__init__.py +0 -0
  74. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/sensitivity/sensitivity.py +0 -0
  75. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/shared_fns/__init__.py +0 -0
  76. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/shared_fns/shared_fns.py +0 -0
  77. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/simtools/__init__.py +0 -0
  78. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/simtools/simtools.py +0 -0
  79. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/validate/__init__.py +0 -0
  80. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/pyrestoolbox/validate/validate.py +0 -0
  81. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/src/bessel.rs +0 -0
  82. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/src/critical_properties/mod.rs +0 -0
  83. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/src/dca/hyperbolic.rs +0 -0
  84. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/src/dca/mod.rs +0 -0
  85. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/src/dca/ransac.rs +0 -0
  86. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/src/gwr.rs +0 -0
  87. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/src/matbal/mod.rs +0 -0
  88. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/src/matbal/objective.rs +0 -0
  89. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/src/oil/density.rs +0 -0
  90. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/src/oil/mod.rs +0 -0
  91. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/src/pseudopressure.rs +0 -0
  92. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/src/spycher_pruess/mod.rs +0 -0
  93. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/src/spycher_pruess/solubility.rs +0 -0
  94. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/src/vle/alpha.rs +0 -0
  95. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/src/vle/bip.rs +0 -0
  96. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/src/vle/components.rs +0 -0
  97. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/src/vle/eos.rs +0 -0
  98. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/src/vle/flash.rs +0 -0
  99. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/src/vle/fugacity.rs +0 -0
  100. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/src/vle/k_init.rs +0 -0
  101. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/src/vle/mod.rs +0 -0
  102. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/src/vle/rachford_rice.rs +0 -0
  103. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/src/vlp/friction.rs +0 -0
  104. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/src/vlp/holdup_bb.rs +0 -0
  105. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/src/vlp/holdup_gray.rs +0 -0
  106. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/src/vlp/holdup_hb.rs +0 -0
  107. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/src/vlp/holdup_wg.rs +0 -0
  108. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/src/vlp/ift.rs +0 -0
  109. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/src/vlp/mod.rs +0 -0
  110. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/src/vlp/pvt_helpers.rs +0 -0
  111. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/src/vlp/segment_gas.rs +0 -0
  112. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/src/vlp/segment_oil.rs +0 -0
  113. {pyrestoolbox-3.1.2 → pyrestoolbox-3.1.3}/src/vlp/static_column.rs +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyrestoolbox
3
- Version: 3.1.2
3
+ Version: 3.1.3
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Rust
6
6
  Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "pyrestoolbox"
7
- version = "3.1.2"
7
+ version = "3.1.3"
8
8
  description = "pyResToolbox - A collection of Reservoir Engineering Utilities"
9
9
  license = {text = "GPL-3.0-or-later"}
10
10
  authors = [{name = "Mark W. Burgoyne", email = "mark.w.burgoyne@gmail.com"}]
@@ -1,3 +1,11 @@
1
+ Changelist in 3.1.3:
2
+
3
+ - **Z-factor Z*=Z-B reformulation**: BNS Peng-Robinson cubic solver now uses Aaron Zick's Z* = Z - B variable substitution, which bounds all physical roots to (0, 1] and guarantees Halley solver convergence. Eliminates rare failures at extreme pressures. Falls back to Cardano analytically if needed.
4
+ - **Rust batch vectorization**: Rust-accelerated ``gas_z()`` and ``gas_ug()`` now use batch dispatch (single FFI call for all pressures) instead of per-pressure scalar loops. Precomputes critical properties, BIPs, and LBC mixture parameters once per call. New Rust functions: ``bns_zfactor_batch``, ``dak_zfactor_batch``, ``hy_zfactor_batch``, ``gas_ug_lge_batch``, ``gas_ug_lbc_batch``. LBC viscosity batch is 9.3x faster than scalar; full BNS pipeline (Z + viscosity) is 2x faster than pure Python.
5
+ - **Rust GWR inverse Laplace transform**: Rust/MPFR-accelerated Gaver-Wynn-Rho algorithm and Bessel functions for influence table generation. Switched dependency from ``gwr_inversion`` to ``ilt-inversion``.
6
+ - **oil_co() Pb discontinuity fix**: Fixed oil compressibility discontinuity at bubble point in black oil tables. Added ``undersaturated_only`` mode.
7
+ - 630 validation tests (up from 603 in 3.0.5).
8
+
1
9
  Changelist in 3.0.5:
2
10
 
3
11
  - **Rust acceleration (optional)**: Computationally intensive algorithms now have optional Rust-compiled acceleration. When the compiled extension is present it is used automatically with no API changes; when unavailable, all functions fall back silently to pure Python. Accelerated functions include all 8 VLP segment loops (4 methods x gas/oil), gas Z-factor (DAK, HY, BNS), gas viscosity (LGE, LBC), gas pseudopressure, oil density (SWMH), oil FVF (McCain), DCA hyperbolic grid search (``fit_decline``, ``fit_decline_cum``), and the material balance regression objective. Set ``PYRESTOOLBOX_NO_RUST=1`` to force pure Python; set ``PYRESTOOLBOX_RETRY_RUST=1`` to retry after a blocked probe. Use ``from pyrestoolbox._accelerator import get_status`` for programmatic status checks.
@@ -840,29 +840,29 @@ def gas_z(
840
840
 
841
841
  zfuncs = {"DAK": zdak, "HY": z_hy, "WYW": z_wyw, "BUR": z_bur}
842
842
 
843
- # Rust acceleration dispatch
843
+ # Rust acceleration dispatch (batch — single FFI call for all pressures)
844
844
  if RUST_AVAILABLE:
845
845
  if zmethod.name in ('BNS', 'BUR'):
846
- zout = np.array([_rust_module.bns_zfactor_full(
847
- float(pi), float(degf), float(sg),
846
+ zout = np.array(_rust_module.bns_zfactor_batch(
847
+ p.tolist(), float(degf), float(sg),
848
848
  float(co2), float(h2s), float(n2), float(h2)
849
- ) for pi in p])
849
+ ))
850
850
  return process_output(zout, is_list)
851
851
  elif zmethod.name == 'DAK':
852
852
  if cmethod.name == 'SUT':
853
- zout = np.array([_rust_module.dak_zfactor_full(
854
- float(pi), float(degf), float(sg),
853
+ zout = np.array(_rust_module.dak_zfactor_batch(
854
+ p.tolist(), float(degf), float(sg),
855
855
  float(co2), float(h2s), float(n2)
856
- ) for pi in p])
856
+ ))
857
857
  else:
858
858
  zout = np.array([_rust_module.dak_zfactor(float(pr_i), float(tr)) for pr_i in pprs])
859
859
  return process_output(zout, is_list)
860
860
  elif zmethod.name == 'HY':
861
861
  if cmethod.name == 'SUT':
862
- zout = np.array([_rust_module.hall_yarborough_zfactor_full(
863
- float(pi), float(degf), float(sg),
862
+ zout = np.array(_rust_module.hy_zfactor_batch(
863
+ p.tolist(), float(degf), float(sg),
864
864
  float(co2), float(h2s), float(n2)
865
- ) for pi in p])
865
+ ))
866
866
  else:
867
867
  zout = np.array([_rust_module.hall_yarborough_zfactor(float(pr_i), float(tr)) for pr_i in pprs])
868
868
  return process_output(zout, is_list)
@@ -945,15 +945,17 @@ def gas_ug(
945
945
 
946
946
  rho = m * p / (t * zee * R * 62.37)
947
947
 
948
- # Rust-accelerated viscosity (scalar dispatch)
948
+ # Rust-accelerated viscosity (batch — single FFI call for all pressures)
949
949
  if RUST_AVAILABLE:
950
950
  if zmethod.name not in ('BNS', 'BUR'):
951
- ug_list = np.array([_rust_module.gas_ug_lge(float(pi), sg, degf, float(zi))
952
- for pi, zi in zip(p, zee)])
951
+ ug_list = np.array(_rust_module.gas_ug_lge_batch(
952
+ p.tolist(), zee.tolist(), sg, degf
953
+ ))
953
954
  ug = process_output(ug_list, is_list)
954
955
  else:
955
- ug_list = np.array([_rust_module.gas_ug_lbc(float(pi), sg, degf, co2, h2s, n2, h2, float(zi))
956
- for pi, zi in zip(p, zee)])
956
+ ug_list = np.array(_rust_module.gas_ug_lbc_batch(
957
+ p.tolist(), zee.tolist(), sg, degf, co2, h2s, n2, h2
958
+ ))
957
959
  ug = process_output(ug_list, is_list)
958
960
  if ugz:
959
961
  return process_output(ug * zee, is_list)
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = pyrestoolbox
3
- version = 3.1.2
3
+ version = 3.1.3
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.rst'), 'r', encoding='utf-8') as f:
12
12
  setup(
13
13
  name='pyrestoolbox',
14
14
  include_package_data=True,
15
- version='3.1.2', # Ideally should be same as your GitHub release tag version
15
+ version='3.1.3', # Ideally should be same as your GitHub release tag version
16
16
  packages=find_packages(exclude=['pyrestoolbox.tests', 'pyrestoolbox.tests.*']),
17
17
  description='pyResToolbox - A collection of Reservoir Engineering Utilities',
18
18
  license="GNU General Public License v3 or later (GPLv3+)",
@@ -159,3 +159,54 @@ pub fn gas_ug_lbc(
159
159
  let params = lbc_params(degf, sg, co2, h2s, n2, h2);
160
160
  Ok(lbc_viscosity_with_params(p_psia, deg_r, zee, &params))
161
161
  }
162
+
163
+ // =========================================================================
164
+ // Batch (vectorized) viscosity functions
165
+ // =========================================================================
166
+
167
+ /// LGE viscosity for a batch of (pressure, z-factor) pairs.
168
+ #[pyfunction]
169
+ pub fn gas_ug_lge_batch(
170
+ pressures: Vec<f64>,
171
+ z_factors: Vec<f64>,
172
+ sg: f64,
173
+ degf: f64,
174
+ ) -> PyResult<Vec<f64>> {
175
+ let t_degr = degf + DEGF2R;
176
+ let m = MW_AIR * sg;
177
+
178
+ // Precompute temperature-dependent LGE coefficients
179
+ let b = 3.448 + (986.4 / t_degr) + (0.01009 * m);
180
+ let c = 2.447 - (0.2224 * b);
181
+ let a = (9.379 + (0.01607 * m)) * t_degr.powf(1.5) / (209.2 + (19.26 * m) + t_degr);
182
+
183
+ let result: Vec<f64> = pressures.iter().zip(z_factors.iter()).map(|(&p, &z)| {
184
+ let rho = m * p / (t_degr * z * R * 62.37);
185
+ a * 0.0001 * (b * rho.powf(c)).exp()
186
+ }).collect();
187
+
188
+ Ok(result)
189
+ }
190
+
191
+ /// LBC viscosity for a batch of (pressure, z-factor) pairs.
192
+ /// Precomputes LBC mixture parameters (u0, eta_mix, rhoc) once.
193
+ #[pyfunction]
194
+ pub fn gas_ug_lbc_batch(
195
+ pressures: Vec<f64>,
196
+ z_factors: Vec<f64>,
197
+ sg: f64,
198
+ degf: f64,
199
+ co2: f64,
200
+ h2s: f64,
201
+ n2: f64,
202
+ h2: f64,
203
+ ) -> PyResult<Vec<f64>> {
204
+ let deg_r = degf + DEGF2R;
205
+ let params = lbc_params(degf, sg, co2, h2s, n2, h2);
206
+
207
+ let result: Vec<f64> = pressures.iter().zip(z_factors.iter()).map(|(&p, &z)| {
208
+ lbc_viscosity_with_params(p, deg_r, z, &params)
209
+ }).collect();
210
+
211
+ Ok(result)
212
+ }
@@ -38,10 +38,19 @@ fn _native(m: &Bound<'_, PyModule>) -> PyResult<()> {
38
38
  m.add_function(wrap_pyfunction!(zfactor::hall_yarborough_zfactor_full, m)?)?;
39
39
  m.add_function(wrap_pyfunction!(zfactor::bns_zfactor_full, m)?)?;
40
40
 
41
+ // Z-factor batch functions
42
+ m.add_function(wrap_pyfunction!(zfactor::dak_zfactor_batch, m)?)?;
43
+ m.add_function(wrap_pyfunction!(zfactor::hy_zfactor_batch, m)?)?;
44
+ m.add_function(wrap_pyfunction!(zfactor::bns_zfactor_batch, m)?)?;
45
+
41
46
  // Gas viscosity
42
47
  m.add_function(wrap_pyfunction!(gas_viscosity::gas_ug_lge, m)?)?;
43
48
  m.add_function(wrap_pyfunction!(gas_viscosity::gas_ug_lbc, m)?)?;
44
49
 
50
+ // Gas viscosity batch functions
51
+ m.add_function(wrap_pyfunction!(gas_viscosity::gas_ug_lge_batch, m)?)?;
52
+ m.add_function(wrap_pyfunction!(gas_viscosity::gas_ug_lbc_batch, m)?)?;
53
+
45
54
  // Pseudopressure integration
46
55
  m.add_function(wrap_pyfunction!(pseudopressure::gas_dmp_rust, m)?)?;
47
56
 
@@ -463,6 +463,80 @@ pub fn bns_zfactor_full(
463
463
  Ok(bns_zfactor_core(p_psia, deg_r, co2_frac, h2s_frac, n2_frac, h2_frac, tpc, ppc))
464
464
  }
465
465
 
466
+ // =========================================================================
467
+ // Batch (vectorized) pipeline functions — precompute once, loop pressures
468
+ // =========================================================================
469
+
470
+ /// DAK Z-factor for a batch of pressures. Precomputes Sutton+WA critical properties once.
471
+ #[pyfunction]
472
+ pub fn dak_zfactor_batch(
473
+ pressures: Vec<f64>,
474
+ t_degf: f64,
475
+ sg: f64,
476
+ co2_frac: f64,
477
+ h2s_frac: f64,
478
+ n2_frac: f64,
479
+ ) -> PyResult<Vec<f64>> {
480
+ let (tpc, ppc) = critical_properties::sutton_wa_internal(sg, co2_frac, h2s_frac, n2_frac)
481
+ .map_err(|e| PyValueError::new_err(e))?;
482
+ let t_rankine = t_degf + DEGF2R;
483
+ let tr = t_rankine / tpc;
484
+
485
+ let result: Vec<f64> = pressures.iter().map(|&p| {
486
+ let pr = p / ppc;
487
+ dak_core(pr, tr)
488
+ }).collect();
489
+
490
+ Ok(result)
491
+ }
492
+
493
+ /// Hall-Yarborough Z-factor for a batch of pressures. Precomputes Sutton+WA critical properties once.
494
+ #[pyfunction]
495
+ pub fn hy_zfactor_batch(
496
+ pressures: Vec<f64>,
497
+ t_degf: f64,
498
+ sg: f64,
499
+ co2_frac: f64,
500
+ h2s_frac: f64,
501
+ n2_frac: f64,
502
+ ) -> PyResult<Vec<f64>> {
503
+ let (tpc, ppc) = critical_properties::sutton_wa_internal(sg, co2_frac, h2s_frac, n2_frac)
504
+ .map_err(|e| PyValueError::new_err(e))?;
505
+ let t_rankine = t_degf + DEGF2R;
506
+ let tr = t_rankine / tpc;
507
+
508
+ let result: Vec<f64> = pressures.iter().map(|&p| {
509
+ let pr = p / ppc;
510
+ hy_core(pr, tr)
511
+ }).collect();
512
+
513
+ Ok(result)
514
+ }
515
+
516
+ /// BNS Z-factor for a batch of pressures. Precomputes critical properties, BIPs,
517
+ /// alpha, and m once — loops only mixing rules, cubic solve, and fugacity selection.
518
+ #[pyfunction]
519
+ pub fn bns_zfactor_batch(
520
+ pressures: Vec<f64>,
521
+ t_degf: f64,
522
+ sg: f64,
523
+ co2_frac: f64,
524
+ h2s_frac: f64,
525
+ n2_frac: f64,
526
+ h2_frac: f64,
527
+ ) -> PyResult<Vec<f64>> {
528
+ let deg_r = t_degf + DEGF2R;
529
+ let (tpc_hc, ppc_hc, _) = critical_properties::bns_pseudocritical_internal(
530
+ sg, co2_frac, h2s_frac, n2_frac, h2_frac
531
+ );
532
+
533
+ let result: Vec<f64> = pressures.iter().map(|&p| {
534
+ bns_zfactor_core(p, deg_r, co2_frac, h2s_frac, n2_frac, h2_frac, tpc_hc, ppc_hc)
535
+ }).collect();
536
+
537
+ Ok(result)
538
+ }
539
+
466
540
  // =========================================================================
467
541
  // Public wrappers for cross-module access (used by pseudopressure)
468
542
  // =========================================================================
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes